diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php
new file mode 100644
index 0000000..7c2f4bb
--- /dev/null
+++ b/app/Http/Controllers/CategoryController.php
@@ -0,0 +1,29 @@
+products()
+ ->with(['variants.prices.currency', 'media'])
+ ->paginate(12)
+ ->withQueryString()
+ ->through(fn (Product $product) => [
+ 'name' => $product->translateAttribute('name'),
+ 'price' => $product->variants->first()?->prices->first()?->price->decimal,
+ 'image' => $product->media->first()?->getUrl(),
+ 'href' => route('product.show', ['product' => $product]),
+ ]);
+
+ return view('category.show', [
+ 'collection' => $collection,
+ 'products' => $products,
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/ContactController.php b/app/Http/Controllers/ContactController.php
new file mode 100644
index 0000000..dbad520
--- /dev/null
+++ b/app/Http/Controllers/ContactController.php
@@ -0,0 +1,11 @@
+inRandomOrder()
->limit(13)
@@ -20,6 +27,7 @@ public function index()
]);
return view('home', [
+ 'page' => $page,
'heroProducts' => $products->take(5)->values(),
'classicsProducts' => $products->slice(5)->values(),
]);
diff --git a/app/Http/Controllers/LegalPageController.php b/app/Http/Controllers/LegalPageController.php
new file mode 100644
index 0000000..cb2a270
--- /dev/null
+++ b/app/Http/Controllers/LegalPageController.php
@@ -0,0 +1,39 @@
+show('terms-and-conditions');
+ }
+
+ public function shippingReturns(string $locale)
+ {
+ return $this->show('shipping-returns');
+ }
+
+ public function privacy(string $locale)
+ {
+ return $this->show('privacy-policy');
+ }
+
+ public function cookies(string $locale)
+ {
+ return $this->show('cookies-policy');
+ }
+
+ private function show(string $slug)
+ {
+ $page = StoicPage::firstWhere('slug', $slug);
+
+ if (! $page) {
+ abort(404);
+ }
+
+ return view('legal', ['page' => $page]);
+ }
+}
diff --git a/app/Models/StoicPage.php b/app/Models/StoicPage.php
new file mode 100644
index 0000000..e44515e
--- /dev/null
+++ b/app/Models/StoicPage.php
@@ -0,0 +1,17 @@
+url($rel);
+ }
+
+ $webp = substr($filename, 0, -strlen($ext)) . "webp";
+ $rel = static::thumbsRelPath() . "presets/" . $preset . "/" . $webp;
+
+ return Storage::disk("public")->url($rel);
+ }
+
+ // Generates the full srcset string for all configured presets.
+ public static function srcset(string $filename): string
+ {
+ static::loadConfig();
+
+ $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
+ if ($ext === "svg") {
+ return "";
+ }
+
+ return collect(static::$presets)
+ ->map(
+ fn($p) => static::image($filename, $p["code"]) .
+ " " .
+ $p["width"] .
+ "w",
+ )
+ ->implode(", ");
+ }
+
+ // Returns [width, height] for a given preset from the sidecar JSON.
+ public static function dimensions(string $filename, string $preset): array
+ {
+ static::loadConfig();
+
+ $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
+ if ($ext === "svg") {
+ return [null, null];
+ }
+
+ $rel = static::thumbsRelPath() . "images/" . $filename . ".json";
+ $path = Storage::disk("public")->path($rel);
+ $meta = json_decode(@file_get_contents($path), true);
+
+ $p = $meta["presets"][$preset] ?? null;
+ if (!$p) {
+ return [null, null];
+ }
+
+ return [(int) $p["w"], (int) $p["h"]];
+ }
+
+ // Path of the thumbs directory relative to the public storage disk root.
+ // Uses a fixed marker so it works regardless of the app's base path (local vs Docker).
+ private static function thumbsRelPath(): string
+ {
+ $marker = "/storage/app/public/";
+ $pos = strpos(static::$thumbs_path, $marker);
+
+ if ($pos !== false) {
+ return substr(static::$thumbs_path, $pos + strlen($marker));
+ }
+
+ return ltrim(static::$thumbs_path, "/");
+ }
+
+ public static function token(string $email): string
+ {
+ $ttl = 60;
+ $payload = base64_encode(
+ json_encode([
+ "email" => $email,
+ "exp" => time() + $ttl,
+ ]),
+ );
+
+ $sig = hash_hmac(
+ "sha256",
+ $payload,
+ config("services.stoic.sso_secret"),
+ );
+
+ $token = "t=" . urlencode($payload) . "&s=" . $sig;
+
+ return $token;
+ }
+
+ public static function hasMany(
+ string $relation,
+ array $slugs,
+ string $prefix,
+ ) {
+ $slugs = array_map(
+ fn($i) => str_replace($prefix . "/", "", $i),
+ $slugs,
+ );
+
+ $entries = $relation::whereIn("slug", $slugs)->get();
+
+ return collect(array_map(
+ fn($i) => $entries->firstWhere("slug", $i),
+ $slugs
+ ));
+ }
+
+ public static function hasOne(
+ string $relation,
+ string $slug,
+ string $prefix,
+ ) {
+ $slug = str_replace($prefix . "/", "", $slug);
+ return $relation::firstWhere("slug", $slug);
+ }
+}
diff --git a/composer.json b/composer.json
index 0587d66..032ebb5 100644
--- a/composer.json
+++ b/composer.json
@@ -11,6 +11,7 @@
"require": {
"artesaos/seotools": "^1.4",
"boboko/core": "0.*",
+ "jacobjoergensen/laravel-paper": "^1.12",
"open-telemetry/exporter-otlp": "^1.4",
"open-telemetry/opentelemetry-auto-laravel": "^1.7",
"open-telemetry/sdk": "^1.14",
diff --git a/composer.lock b/composer.lock
index e7a4259..a494efc 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "497fd57dba085798efccc5819904020f",
+ "content-hash": "a89bc3e4e93372ee52664163d56f783b",
"packages": [
{
"name": "anourvalar/eloquent-serialize",
@@ -565,11 +565,11 @@
},
{
"name": "boboko/core",
- "version": "0.4.0",
+ "version": "0.5.0",
"source": {
"type": "git",
"url": "https://code.radical-elements.com/boboko/core.git",
- "reference": "f7da26b487f49d1b772ae89e9a66d943ad7b20c2"
+ "reference": "e31de1b4e3894c70a82a8d939b094bf305588eb5"
},
"require": {
"laravel/framework": "^12.0",
@@ -599,7 +599,8 @@
"Modules\\Core\\Providers\\CoreServiceProvider",
"Modules\\Core\\Providers\\AuthServiceProvider",
"Modules\\Core\\Providers\\CustomerServiceProvider",
- "Modules\\Core\\Providers\\LocalizationServiceProvider"
+ "Modules\\Core\\Providers\\LocalizationServiceProvider",
+ "Modules\\Core\\Providers\\ReviewServiceProvider"
]
}
},
@@ -609,7 +610,7 @@
}
},
"description": "Core module — authentication and shared panel behaviour",
- "time": "2026-08-06T09:24:07+00:00"
+ "time": "2026-08-24T11:47:59+00:00"
},
{
"name": "brick/math",
@@ -1896,7 +1897,7 @@
},
{
"name": "filament/actions",
- "version": "v3.3.54",
+ "version": "v3.3.55",
"source": {
"type": "git",
"url": "https://github.com/filamentphp/actions.git",
@@ -1949,7 +1950,7 @@
},
{
"name": "filament/filament",
- "version": "v3.3.54",
+ "version": "v3.3.55",
"source": {
"type": "git",
"url": "https://github.com/filamentphp/panels.git",
@@ -2014,7 +2015,7 @@
},
{
"name": "filament/forms",
- "version": "v3.3.54",
+ "version": "v3.3.55",
"source": {
"type": "git",
"url": "https://github.com/filamentphp/forms.git",
@@ -2070,7 +2071,7 @@
},
{
"name": "filament/infolists",
- "version": "v3.3.54",
+ "version": "v3.3.55",
"source": {
"type": "git",
"url": "https://github.com/filamentphp/infolists.git",
@@ -2121,7 +2122,7 @@
},
{
"name": "filament/notifications",
- "version": "v3.3.54",
+ "version": "v3.3.55",
"source": {
"type": "git",
"url": "https://github.com/filamentphp/notifications.git",
@@ -2173,7 +2174,7 @@
},
{
"name": "filament/spatie-laravel-media-library-plugin",
- "version": "v3.3.54",
+ "version": "v3.3.55",
"source": {
"type": "git",
"url": "https://github.com/filamentphp/spatie-laravel-media-library-plugin.git",
@@ -2211,16 +2212,16 @@
},
{
"name": "filament/support",
- "version": "v3.3.54",
+ "version": "v3.3.55",
"source": {
"type": "git",
"url": "https://github.com/filamentphp/support.git",
- "reference": "a9889ea4cdec47c365af47f344a8acd9917ed4c5"
+ "reference": "e2ce3ca8fd1007103e4a16ab564a76b6fee87968"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/filamentphp/support/zipball/a9889ea4cdec47c365af47f344a8acd9917ed4c5",
- "reference": "a9889ea4cdec47c365af47f344a8acd9917ed4c5",
+ "url": "https://api.github.com/repos/filamentphp/support/zipball/e2ce3ca8fd1007103e4a16ab564a76b6fee87968",
+ "reference": "e2ce3ca8fd1007103e4a16ab564a76b6fee87968",
"shasum": ""
},
"require": {
@@ -2266,11 +2267,11 @@
"issues": "https://github.com/filamentphp/filament/issues",
"source": "https://github.com/filamentphp/filament"
},
- "time": "2026-06-12T16:57:05+00:00"
+ "time": "2026-08-24T16:38:35+00:00"
},
{
"name": "filament/tables",
- "version": "v3.3.54",
+ "version": "v3.3.55",
"source": {
"type": "git",
"url": "https://github.com/filamentphp/tables.git",
@@ -2322,7 +2323,7 @@
},
{
"name": "filament/widgets",
- "version": "v3.3.54",
+ "version": "v3.3.55",
"source": {
"type": "git",
"url": "https://github.com/filamentphp/widgets.git",
@@ -2481,24 +2482,24 @@
},
{
"name": "graham-campbell/result-type",
- "version": "v1.1.4",
+ "version": "v1.2.0",
"source": {
"type": "git",
"url": "https://github.com/GrahamCampbell/Result-Type.git",
- "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b"
+ "reference": "adccca3324eece92ca35463648c12b9e6293c05b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b",
- "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b",
+ "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/adccca3324eece92ca35463648c12b9e6293c05b",
+ "reference": "adccca3324eece92ca35463648c12b9e6293c05b",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0",
- "phpoption/phpoption": "^1.9.5"
+ "phpoption/phpoption": "^1.10"
},
"require-dev": {
- "phpunit/phpunit": "^8.5.41 || ^9.6.22 || ^10.5.45 || ^11.5.7"
+ "phpunit/phpunit": "^8.5.52 || ^9.6.34 || ^10.5.63 || ^11.5.55 || ^12.5.14"
},
"type": "library",
"autoload": {
@@ -2527,7 +2528,7 @@
],
"support": {
"issues": "https://github.com/GrahamCampbell/Result-Type/issues",
- "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.4"
+ "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.2.0"
},
"funding": [
{
@@ -2539,26 +2540,26 @@
"type": "tidelift"
}
],
- "time": "2025-12-27T19:43:20+00:00"
+ "time": "2026-08-24T09:06:52+00:00"
},
{
"name": "guzzlehttp/guzzle",
- "version": "7.15.3",
+ "version": "7.15.5",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
- "reference": "ae311b8f045ea93ce7b1c9cdb7cec06c53f944bc"
+ "reference": "ee80339fd9177ba44c49cdb653ff02a4d1106b9a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ae311b8f045ea93ce7b1c9cdb7cec06c53f944bc",
- "reference": "ae311b8f045ea93ce7b1c9cdb7cec06c53f944bc",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ee80339fd9177ba44c49cdb653ff02a4d1106b9a",
+ "reference": "ee80339fd9177ba44c49cdb653ff02a4d1106b9a",
"shasum": ""
},
"require": {
"ext-json": "*",
- "guzzlehttp/promises": "^2.5.2",
- "guzzlehttp/psr7": "^2.13",
+ "guzzlehttp/promises": "^2.5.3",
+ "guzzlehttp/psr7": "^2.13.1",
"php": "^7.2.5 || ^8.0",
"psr/http-client": "^1.0",
"symfony/deprecation-contracts": "^2.5 || ^3.0",
@@ -2651,7 +2652,7 @@
],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
- "source": "https://github.com/guzzle/guzzle/tree/7.15.3"
+ "source": "https://github.com/guzzle/guzzle/tree/7.15.5"
},
"funding": [
{
@@ -2667,20 +2668,20 @@
"type": "tidelift"
}
],
- "time": "2026-08-05T19:48:21+00:00"
+ "time": "2026-08-24T09:21:06+00:00"
},
{
"name": "guzzlehttp/promises",
- "version": "2.5.2",
+ "version": "2.5.3",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
- "reference": "2823687acff28b2dbe67b2508a6b300e2c3fa4ce"
+ "reference": "cde49999552d185d64715fe9c1f77a2aadd2f9f1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/promises/zipball/2823687acff28b2dbe67b2508a6b300e2c3fa4ce",
- "reference": "2823687acff28b2dbe67b2508a6b300e2c3fa4ce",
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/cde49999552d185d64715fe9c1f77a2aadd2f9f1",
+ "reference": "cde49999552d185d64715fe9c1f77a2aadd2f9f1",
"shasum": ""
},
"require": {
@@ -2735,7 +2736,7 @@
],
"support": {
"issues": "https://github.com/guzzle/promises/issues",
- "source": "https://github.com/guzzle/promises/tree/2.5.2"
+ "source": "https://github.com/guzzle/promises/tree/2.5.3"
},
"funding": [
{
@@ -2751,20 +2752,20 @@
"type": "tidelift"
}
],
- "time": "2026-08-05T19:30:54+00:00"
+ "time": "2026-08-24T09:11:28+00:00"
},
{
"name": "guzzlehttp/psr7",
- "version": "2.13.0",
+ "version": "2.13.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
- "reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4"
+ "reference": "95e7828100de18b4e269fb1703be530082d5166d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/psr7/zipball/dad89620b7a6edb60c15858442eb2e408b45d8f4",
- "reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/95e7828100de18b4e269fb1703be530082d5166d",
+ "reference": "95e7828100de18b4e269fb1703be530082d5166d",
"shasum": ""
},
"require": {
@@ -2854,7 +2855,7 @@
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
- "source": "https://github.com/guzzle/psr7/tree/2.13.0"
+ "source": "https://github.com/guzzle/psr7/tree/2.13.1"
},
"funding": [
{
@@ -2870,20 +2871,20 @@
"type": "tidelift"
}
],
- "time": "2026-07-16T22:23:49+00:00"
+ "time": "2026-08-24T09:13:11+00:00"
},
{
"name": "guzzlehttp/uri-template",
- "version": "v1.0.10",
+ "version": "v1.0.11",
"source": {
"type": "git",
"url": "https://github.com/guzzle/uri-template.git",
- "reference": "f6c24c21f42b990e9a58912b332d0874df6ba839"
+ "reference": "d0058dccf4299d70c3d9da3378b8908b32780368"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/uri-template/zipball/f6c24c21f42b990e9a58912b332d0874df6ba839",
- "reference": "f6c24c21f42b990e9a58912b332d0874df6ba839",
+ "url": "https://api.github.com/repos/guzzle/uri-template/zipball/d0058dccf4299d70c3d9da3378b8908b32780368",
+ "reference": "d0058dccf4299d70c3d9da3378b8908b32780368",
"shasum": ""
},
"require": {
@@ -2940,7 +2941,7 @@
],
"support": {
"issues": "https://github.com/guzzle/uri-template/issues",
- "source": "https://github.com/guzzle/uri-template/tree/v1.0.10"
+ "source": "https://github.com/guzzle/uri-template/tree/v1.0.11"
},
"funding": [
{
@@ -2956,7 +2957,7 @@
"type": "tidelift"
}
],
- "time": "2026-07-17T13:53:03+00:00"
+ "time": "2026-08-24T09:15:32+00:00"
},
{
"name": "http-interop/http-factory-guzzle",
@@ -3016,6 +3017,74 @@
},
"time": "2025-12-15T11:28:16+00:00"
},
+ {
+ "name": "jacobjoergensen/laravel-paper",
+ "version": "1.15.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/JacobJoergensen/laravel-paper.git",
+ "reference": "024ced2ee5469ab511f957f9a505f5db5745d32b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/JacobJoergensen/laravel-paper/zipball/024ced2ee5469ab511f957f9a505f5db5745d32b",
+ "reference": "024ced2ee5469ab511f957f9a505f5db5745d32b",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/database": "^12.0|^13.0",
+ "illuminate/filesystem": "^12.0|^13.0",
+ "illuminate/support": "^12.0|^13.0",
+ "php": "^8.4",
+ "spatie/yaml-front-matter": "^2.0"
+ },
+ "require-dev": {
+ "laravel/pint": "^1.29",
+ "orchestra/testbench": "^10.0|^11.0",
+ "pestphp/pest": "^4.0",
+ "pestphp/pest-plugin-laravel": "^4.1",
+ "pestphp/pest-plugin-type-coverage": "^4.0",
+ "phpstan/phpstan": "^2.1"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "JacobJoergensen\\LaravelPaper\\PaperServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "JacobJoergensen\\LaravelPaper\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "JacobJoergensen",
+ "email": "jj@minehubsstudios.com"
+ }
+ ],
+ "description": "A flat-file Eloquent driver for modern Laravel.",
+ "keywords": [
+ "cms",
+ "content",
+ "eloquent",
+ "flat-file",
+ "frontmatter",
+ "laravel",
+ "markdown"
+ ],
+ "support": {
+ "issues": "https://github.com/JacobJoergensen/laravel-paper/issues",
+ "source": "https://github.com/JacobJoergensen/laravel-paper/tree/1.15.0"
+ },
+ "time": "2026-07-30T21:15:25+00:00"
+ },
{
"name": "kalnoy/nestedset",
"version": "v6.0.6",
@@ -3144,16 +3213,16 @@
},
{
"name": "laravel/framework",
- "version": "v12.67.0",
+ "version": "v12.68.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
- "reference": "fe2cdaba052cbb9f350761ccefc7ac221cbdf0b5"
+ "reference": "1343c22b92edd48e29ff273d8ce7c15cf75d974c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/framework/zipball/fe2cdaba052cbb9f350761ccefc7ac221cbdf0b5",
- "reference": "fe2cdaba052cbb9f350761ccefc7ac221cbdf0b5",
+ "url": "https://api.github.com/repos/laravel/framework/zipball/1343c22b92edd48e29ff273d8ce7c15cf75d974c",
+ "reference": "1343c22b92edd48e29ff273d8ce7c15cf75d974c",
"shasum": ""
},
"require": {
@@ -3362,20 +3431,20 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
- "time": "2026-08-18T13:37:47+00:00"
+ "time": "2026-08-25T14:18:36+00:00"
},
{
"name": "laravel/prompts",
- "version": "v0.3.23",
+ "version": "v0.3.24",
"source": {
"type": "git",
"url": "https://github.com/laravel/prompts.git",
- "reference": "b7b4c35e5bc47450f6b6238c6cc9c47ba19b2221"
+ "reference": "5d3cdef29e93ca3b62b1871359db3078cd99908b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/prompts/zipball/b7b4c35e5bc47450f6b6238c6cc9c47ba19b2221",
- "reference": "b7b4c35e5bc47450f6b6238c6cc9c47ba19b2221",
+ "url": "https://api.github.com/repos/laravel/prompts/zipball/5d3cdef29e93ca3b62b1871359db3078cd99908b",
+ "reference": "5d3cdef29e93ca3b62b1871359db3078cd99908b",
"shasum": ""
},
"require": {
@@ -3419,9 +3488,9 @@
"description": "Add beautiful and user-friendly forms to your command-line applications.",
"support": {
"issues": "https://github.com/laravel/prompts/issues",
- "source": "https://github.com/laravel/prompts/tree/v0.3.23"
+ "source": "https://github.com/laravel/prompts/tree/v0.3.24"
},
- "time": "2026-08-11T18:58:24+00:00"
+ "time": "2026-08-20T12:55:36+00:00"
},
{
"name": "laravel/scout",
@@ -3505,16 +3574,16 @@
},
{
"name": "laravel/serializable-closure",
- "version": "v2.0.15",
+ "version": "v2.0.16",
"source": {
"type": "git",
"url": "https://github.com/laravel/serializable-closure.git",
- "reference": "dccd8bcb851bb03fcc005df650b708b57cc52661"
+ "reference": "7cfc24e4fa2cca045fb8dd2a797a2b2b13b655ed"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/dccd8bcb851bb03fcc005df650b708b57cc52661",
- "reference": "dccd8bcb851bb03fcc005df650b708b57cc52661",
+ "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/7cfc24e4fa2cca045fb8dd2a797a2b2b13b655ed",
+ "reference": "7cfc24e4fa2cca045fb8dd2a797a2b2b13b655ed",
"shasum": ""
},
"require": {
@@ -3562,7 +3631,7 @@
"issues": "https://github.com/laravel/serializable-closure/issues",
"source": "https://github.com/laravel/serializable-closure"
},
- "time": "2026-07-21T16:49:22+00:00"
+ "time": "2026-08-18T20:28:54+00:00"
},
{
"name": "laravel/tinker",
@@ -4360,16 +4429,16 @@
},
{
"name": "livewire/livewire",
- "version": "v3.8.5",
+ "version": "v3.8.6",
"source": {
"type": "git",
"url": "https://github.com/livewire/livewire.git",
- "reference": "038443795e69af79ba03064cbc19d76b60e2c638"
+ "reference": "be5729e9accfa9255a28d82f7759a3cc4be0f079"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/livewire/livewire/zipball/038443795e69af79ba03064cbc19d76b60e2c638",
- "reference": "038443795e69af79ba03064cbc19d76b60e2c638",
+ "url": "https://api.github.com/repos/livewire/livewire/zipball/be5729e9accfa9255a28d82f7759a3cc4be0f079",
+ "reference": "be5729e9accfa9255a28d82f7759a3cc4be0f079",
"shasum": ""
},
"require": {
@@ -4424,7 +4493,7 @@
"description": "A front-end framework for Laravel.",
"support": {
"issues": "https://github.com/livewire/livewire/issues",
- "source": "https://github.com/livewire/livewire/tree/v3.8.5"
+ "source": "https://github.com/livewire/livewire/tree/v3.8.6"
},
"funding": [
{
@@ -4432,7 +4501,7 @@
"type": "github"
}
],
- "time": "2026-08-18T11:38:50+00:00"
+ "time": "2026-08-24T15:02:02+00:00"
},
{
"name": "lukascivil/treewalker",
@@ -6986,16 +7055,16 @@
},
{
"name": "phpoption/phpoption",
- "version": "1.9.5",
+ "version": "1.10.0",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/php-option.git",
- "reference": "75365b91986c2405cf5e1e012c5595cd487a98be"
+ "reference": "67b192b6a42ec03944b972d6e633ddec78ad2c6d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be",
- "reference": "75365b91986c2405cf5e1e012c5595cd487a98be",
+ "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/67b192b6a42ec03944b972d6e633ddec78ad2c6d",
+ "reference": "67b192b6a42ec03944b972d6e633ddec78ad2c6d",
"shasum": ""
},
"require": {
@@ -7003,7 +7072,7 @@
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
- "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34"
+ "phpunit/phpunit": "^8.5.54 || ^9.6.36 || ^10.5.64 || ^11.5.56 || ^12.5.33"
},
"type": "library",
"extra": {
@@ -7045,7 +7114,7 @@
],
"support": {
"issues": "https://github.com/schmittjoh/php-option/issues",
- "source": "https://github.com/schmittjoh/php-option/tree/1.9.5"
+ "source": "https://github.com/schmittjoh/php-option/tree/1.10.0"
},
"funding": [
{
@@ -7057,7 +7126,7 @@
"type": "tidelift"
}
],
- "time": "2025-12-27T19:41:33+00:00"
+ "time": "2026-08-24T00:54:40+00:00"
},
{
"name": "phpstan/phpdoc-parser",
@@ -8398,16 +8467,16 @@
},
{
"name": "spatie/invade",
- "version": "2.1.0",
+ "version": "2.1.1",
"source": {
"type": "git",
"url": "https://github.com/spatie/invade.git",
- "reference": "b920f6411d21df4e8610a138e2e87ae4957d7f63"
+ "reference": "f929c7b6e75fceeefa7b41a491e91274bafb17af"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/spatie/invade/zipball/b920f6411d21df4e8610a138e2e87ae4957d7f63",
- "reference": "b920f6411d21df4e8610a138e2e87ae4957d7f63",
+ "url": "https://api.github.com/repos/spatie/invade/zipball/f929c7b6e75fceeefa7b41a491e91274bafb17af",
+ "reference": "f929c7b6e75fceeefa7b41a491e91274bafb17af",
"shasum": ""
},
"require": {
@@ -8445,7 +8514,7 @@
"spatie"
],
"support": {
- "source": "https://github.com/spatie/invade/tree/2.1.0"
+ "source": "https://github.com/spatie/invade/tree/2.1.1"
},
"funding": [
{
@@ -8453,7 +8522,7 @@
"type": "github"
}
],
- "time": "2024-05-17T09:06:10+00:00"
+ "time": "2026-08-24T12:39:05+00:00"
},
{
"name": "spatie/laravel-activitylog",
@@ -9459,6 +9528,68 @@
],
"time": "2026-06-22T07:55:44+00:00"
},
+ {
+ "name": "spatie/yaml-front-matter",
+ "version": "2.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/spatie/yaml-front-matter.git",
+ "reference": "3066996d0e4ed74bcc22c261084a9df727bf7e36"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/spatie/yaml-front-matter/zipball/3066996d0e4ed74bcc22c261084a9df727bf7e36",
+ "reference": "3066996d0e4ed74bcc22c261084a9df727bf7e36",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.0",
+ "symfony/yaml": "^6.0|^7.0|^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Spatie\\YamlFrontMatter\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian De Deyne",
+ "email": "sebastian@spatie.be",
+ "homepage": "https://spatie.be",
+ "role": "Developer"
+ }
+ ],
+ "description": "A to the point yaml front matter parser",
+ "homepage": "https://github.com/sebastiandedeyne/yaml-front-matter",
+ "keywords": [
+ "front matter",
+ "jekyll",
+ "spatie",
+ "yaml"
+ ],
+ "support": {
+ "source": "https://github.com/spatie/yaml-front-matter/tree/2.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://spatie.be/open-source/support-us",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/spatie",
+ "type": "github"
+ }
+ ],
+ "time": "2025-11-24T16:17:28+00:00"
+ },
{
"name": "spomky-labs/cbor-php",
"version": "3.3.0",
@@ -11026,16 +11157,16 @@
},
{
"name": "symfony/polyfill-intl-idn",
- "version": "v1.38.1",
+ "version": "v1.42.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
- "reference": "dc21118016c039a66235cf93d96b435ffb282412"
+ "reference": "51b5ff5ba85452b31ec6f55490b08148612339d9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/dc21118016c039a66235cf93d96b435ffb282412",
- "reference": "dc21118016c039a66235cf93d96b435ffb282412",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/51b5ff5ba85452b31ec6f55490b08148612339d9",
+ "reference": "51b5ff5ba85452b31ec6f55490b08148612339d9",
"shasum": ""
},
"require": {
@@ -11089,7 +11220,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.38.1"
+ "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.42.0"
},
"funding": [
{
@@ -11109,20 +11240,20 @@
"type": "tidelift"
}
],
- "time": "2026-05-25T15:22:23+00:00"
+ "time": "2026-08-24T10:51:20+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
- "version": "v1.38.0",
+ "version": "v1.42.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
- "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b"
+ "reference": "aa20edea75bd9c48cfecc8360922e5a6e5c44502"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b",
- "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/aa20edea75bd9c48cfecc8360922e5a6e5c44502",
+ "reference": "aa20edea75bd9c48cfecc8360922e5a6e5c44502",
"shasum": ""
},
"require": {
@@ -11174,7 +11305,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0"
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.42.0"
},
"funding": [
{
@@ -11194,7 +11325,7 @@
"type": "tidelift"
}
],
- "time": "2026-05-25T13:48:31+00:00"
+ "time": "2026-08-07T06:33:24+00:00"
},
{
"name": "symfony/polyfill-mbstring",
@@ -12993,16 +13124,16 @@
},
{
"name": "technikermathe/blade-lucide-icons",
- "version": "v3.168.0",
+ "version": "v3.169.0",
"source": {
"type": "git",
"url": "https://github.com/PascaleBeier/blade-lucide-icons.git",
- "reference": "42cceff9aca03fe8e99e65c67473e65bd8b35388"
+ "reference": "f73b9b4f065da5d5afc865aadf28260aef0f4477"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PascaleBeier/blade-lucide-icons/zipball/42cceff9aca03fe8e99e65c67473e65bd8b35388",
- "reference": "42cceff9aca03fe8e99e65c67473e65bd8b35388",
+ "url": "https://api.github.com/repos/PascaleBeier/blade-lucide-icons/zipball/f73b9b4f065da5d5afc865aadf28260aef0f4477",
+ "reference": "f73b9b4f065da5d5afc865aadf28260aef0f4477",
"shasum": ""
},
"require": {
@@ -13052,9 +13183,9 @@
],
"support": {
"issues": "https://github.com/PascaleBeier/blade-lucide-icons/issues",
- "source": "https://github.com/PascaleBeier/blade-lucide-icons/tree/v3.168.0"
+ "source": "https://github.com/PascaleBeier/blade-lucide-icons/tree/v3.169.0"
},
- "time": "2026-08-20T00:38:36+00:00"
+ "time": "2026-08-25T00:39:46+00:00"
},
{
"name": "thecodingmachine/safe",
@@ -13325,23 +13456,23 @@
},
{
"name": "vlucas/phpdotenv",
- "version": "v5.6.4",
+ "version": "v5.7.0",
"source": {
"type": "git",
"url": "https://github.com/vlucas/phpdotenv.git",
- "reference": "416df702837983f8d5ff48c9c3fee4f5f57b980b"
+ "reference": "301c07936b16d88628b126b01d082ba153cf4c40"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/416df702837983f8d5ff48c9c3fee4f5f57b980b",
- "reference": "416df702837983f8d5ff48c9c3fee4f5f57b980b",
+ "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/301c07936b16d88628b126b01d082ba153cf4c40",
+ "reference": "301c07936b16d88628b126b01d082ba153cf4c40",
"shasum": ""
},
"require": {
"ext-pcre": "*",
- "graham-campbell/result-type": "^1.1.4",
+ "graham-campbell/result-type": "^1.2",
"php": "^7.2.5 || ^8.0",
- "phpoption/phpoption": "^1.9.5",
+ "phpoption/phpoption": "^1.10",
"symfony/polyfill-ctype": "^1.26",
"symfony/polyfill-mbstring": "^1.26",
"symfony/polyfill-php80": "^1.26"
@@ -13385,7 +13516,7 @@
"homepage": "https://github.com/vlucas"
}
],
- "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
+ "description": "Loads environment variables from `.env` to `$_ENV` and `$_SERVER` automagically, and optionally to `getenv()`.",
"keywords": [
"dotenv",
"env",
@@ -13393,7 +13524,7 @@
],
"support": {
"issues": "https://github.com/vlucas/phpdotenv/issues",
- "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.4"
+ "source": "https://github.com/vlucas/phpdotenv/tree/v5.7.0"
},
"funding": [
{
@@ -13405,7 +13536,7 @@
"type": "tidelift"
}
],
- "time": "2026-07-06T19:11:50+00:00"
+ "time": "2026-08-24T18:07:49+00:00"
},
{
"name": "voku/portable-ascii",
diff --git a/config/services.php b/config/services.php
index 6a90eb8..5307b21 100644
--- a/config/services.php
+++ b/config/services.php
@@ -35,4 +35,9 @@
],
],
+ 'stoic' => [
+ 'sso_secret' => env('STOIC_SSO_SECRET'),
+ 'host' => env('STOIC_HOST'),
+ ],
+
];
diff --git a/lang/el/general.php b/lang/el/general.php
index cf2401c..f1e0ede 100644
--- a/lang/el/general.php
+++ b/lang/el/general.php
@@ -3,6 +3,7 @@
return [
'nav' => [
+ 'home' => 'Αρχική',
'products' => 'Προϊόντα',
'contact' => 'Επικοινωνία',
],
@@ -15,4 +16,29 @@
// Laravel pluralization: {0} zero|{1} one|[2,*] many. :count is replaced automatically.
'customer_reviews' => '{0} Καμία αξιολόγηση πελάτη|{1} :count αξιολόγηση πελάτη|[2,*] :count αξιολογήσεις πελατών',
+ 'shop' => [
+ // Laravel pluralization keyed on the total result count.
+ 'showing_results' => '{0} Δεν βρέθηκαν προϊόντα|{1} Εμφάνιση :first–:last από :total αποτέλεσμα|[2,*] Εμφάνιση :first–:last από :total αποτελέσματα',
+ 'no_products' => 'Δεν βρέθηκαν προϊόντα σε αυτή την κατηγορία.',
+ 'search_label' => 'Αναζήτηση προϊόντων',
+ 'search_placeholder' => 'Αναζήτησε προϊόντα…',
+ 'filter_price' => 'Φίλτρο τιμής',
+ 'apply' => 'Εφαρμογή',
+ 'availability' => 'Διαθεσιμότητα',
+ 'in_stock_only' => 'Μόνο διαθέσιμα προϊόντα',
+ 'sort_label' => 'Ταξινόμηση προϊόντων',
+ 'sort_default' => 'Προεπιλεγμένη ταξινόμηση',
+ 'sort_popularity' => 'Δημοφιλή',
+ 'sort_price_asc' => 'Τιμή: Αύξουσα',
+ 'sort_price_desc' => 'Τιμή: Φθίνουσα',
+ 'sort_newest' => 'Νεότερα',
+ ],
+
+ 'pagination' => [
+ 'nav_label' => 'Σελιδοποίηση',
+ 'page' => 'Σελίδα :page',
+ 'next' => 'Επόμενη σελίδα',
+ 'previous' => 'Προηγούμενη σελίδα',
+ ],
+
];
diff --git a/lang/en/general.php b/lang/en/general.php
index e1538e7..e94338e 100644
--- a/lang/en/general.php
+++ b/lang/en/general.php
@@ -3,6 +3,7 @@
return [
'nav' => [
+ 'home' => 'Home',
'products' => 'Products',
'contact' => 'Contact',
],
@@ -15,4 +16,29 @@
// Laravel pluralization: {0} zero|{1} one|[2,*] many. :count is replaced automatically.
'customer_reviews' => '{0} No customer reviews|{1} :count customer review|[2,*] :count customer reviews',
+ 'shop' => [
+ // Laravel pluralization keyed on the total result count.
+ 'showing_results' => '{0} No products found|{1} Showing :first–:last of :total result|[2,*] Showing :first–:last of :total results',
+ 'no_products' => 'No products found in this category.',
+ 'search_label' => 'Search products',
+ 'search_placeholder' => 'Search products…',
+ 'filter_price' => 'Filter by price',
+ 'apply' => 'Apply',
+ 'availability' => 'Availability',
+ 'in_stock_only' => 'In-stock products only',
+ 'sort_label' => 'Sort products',
+ 'sort_default' => 'Default sorting',
+ 'sort_popularity' => 'Popularity',
+ 'sort_price_asc' => 'Price: Low to High',
+ 'sort_price_desc' => 'Price: High to Low',
+ 'sort_newest' => 'Newest',
+ ],
+
+ 'pagination' => [
+ 'nav_label' => 'Pagination',
+ 'page' => 'Page :page',
+ 'next' => 'Next page',
+ 'previous' => 'Previous page',
+ ],
+
];
diff --git a/resources/css/app.css b/resources/css/app.css
index 6573b3f..4f10355 100644
--- a/resources/css/app.css
+++ b/resources/css/app.css
@@ -45,7 +45,7 @@ @layer base {
}
}
-@keyframes back-to-top-spin {
+@keyframes spin {
from {
transform: rotate(0deg);
}
@@ -115,9 +115,9 @@ @layer components {
}
/* ── Hero star — same burst shape/spin as back-to-top, always on ─ */
- .hero-star {
+ .rotating-star {
transform-origin: center;
- animation: back-to-top-spin 7s infinite linear;
+ animation: spin 7s infinite linear;
}
/* ── Back to top ──────────────────────────────────────────────── */
@@ -138,7 +138,7 @@ @layer components {
.back-to-top-shape path {
fill: theme(colors.brand-light);
transform-origin: center;
- animation: back-to-top-spin 7s infinite linear;
+ animation: spin 7s infinite linear;
animation-play-state: paused;
}
@@ -300,27 +300,12 @@ @layer components {
transform: translate(0, 0);
}
- /* ── Text dance — the word itself toggles font-weight/font-style
- (normal → bold+italic) three times, then stays bold+italic, once
- its .is-visible ancestor appears (pair with the `appear` Stimulus
- controller — data-controller="appear" data-appear-visible-class
- ="is-visible"); add data-text="
+ {{ trans_choice('general.shop.showing_results', $products->total(), [
+ 'first' => $products->firstItem() ?? 0,
+ 'last' => $products->lastItem() ?? 0,
+ 'total' => $products->total(),
+ ]) }}
+ {{ __('general.shop.no_products') }}{{ $collection->translateAttribute('name') }}
+
+