From 84eaf3c5112e087efd4bd7a9dab2d28972b405ac Mon Sep 17 00:00:00 2001 From: Lionel Guichard Date: Fri, 5 Jul 2024 18:20:07 +0200 Subject: [PATCH] Support of Lunar 1.x (#86) --- .env.docker.example | 71 + .env.lando.example | 27 +- .gitignore | 11 + .lando.yml | 24 +- .styleci.yml | 14 - Dockerfile | 61 + README.md | 22 + app/Console/Kernel.php | 8 +- app/Exceptions/Handler.php | 22 +- app/Http/Controllers/Controller.php | 3 +- app/Http/Kernel.php | 11 +- app/Http/Middleware/Authenticate.php | 10 +- app/Http/Middleware/TrustHosts.php | 2 +- app/{Http => }/Livewire/CheckoutPage.php | 67 +- .../Livewire/CheckoutSuccessPage.php | 15 +- app/{Http => }/Livewire/CollectionPage.php | 31 +- .../Livewire/Components/AddToCart.php | 21 +- app/{Http => }/Livewire/Components/Cart.php | 36 +- .../Livewire/Components/CheckoutAddress.php | 27 +- .../Livewire/Components/Navigation.php | 7 +- .../Livewire/Components/ShippingOptions.php | 30 +- app/{Http => }/Livewire/Home.php | 17 +- app/{Http => }/Livewire/ProductPage.php | 50 +- app/{Http => }/Livewire/SearchPage.php | 12 +- app/Models/User.php | 2 +- app/Modifiers/ShippingModifier.php | 44 +- app/Providers/AppServiceProvider.php | 17 +- app/Providers/AuthServiceProvider.php | 6 +- app/Providers/EventServiceProvider.php | 8 +- app/Providers/RouteServiceProvider.php | 18 +- app/Traits/FetchesUrls.php | 5 +- app/View/Components/ProductPrice.php | 5 +- composer.json | 21 +- config/app.php | 33 +- config/auth.php | 2 +- config/broadcasting.php | 1 + config/cache.php | 1 + config/database.php | 6 +- config/hashing.php | 3 +- config/livewire.php | 179 +- config/logging.php | 10 + config/lunar-hub/customers.php | 16 - config/lunar-hub/products.php | 31 - config/lunar/cart.php | 145 - config/lunar/database.php | 18 - config/lunar/media.php | 13 - config/lunar/orders.php | 52 - config/lunar/payments.php | 12 - config/lunar/pricing.php | 5 - config/lunar/search.php | 38 - config/lunar/shipping.php | 75 - config/lunar/stripe.php | 17 - config/lunar/taxes.php | 14 - config/lunar/urls.php | 25 - config/mail.php | 13 +- config/sanctum.php | 22 +- config/session.php | 13 + database/factories/UserFactory.php | 14 +- .../2014_10_12_000000_create_users_table.php | 8 +- ...000_create_password_reset_tokens_table.php | 28 + ..._08_19_000000_create_failed_jobs_table.php | 8 +- ...01_create_personal_access_tokens_table.php | 9 +- ...res_at_to_personal_access_tokens_table.php | 32 - database/seeders/AbstractSeeder.php | 3 +- database/seeders/AttributeSeeder.php | 6 +- database/seeders/CollectionSeeder.php | 3 +- database/seeders/CustomerSeeder.php | 3 +- database/seeders/DatabaseSeeder.php | 4 +- database/seeders/OrderSeeder.php | 11 +- database/seeders/ProductSeeder.php | 102 +- database/seeders/ShippingSeeder.php | 160 + database/seeders/TaxSeeder.php | 3 +- database/seeders/data/products.json | 8 + docker-compose.yml | 86 + docker/caddy/Caddyfile | 14 + docker/php-fpm/dev/php.ini | 5 + docker/php-fpm/docker-entrypoint.sh | 41 + docker/php-fpm/php.ini | 5 + env.docker.example | 60 + package.json | 23 +- phpunit.xml | 1 + .../components/collection-sale.blade.php | 4 +- .../views/components/product-card.blade.php | 4 +- resources/views/layouts/checkout.blade.php | 4 - resources/views/layouts/storefront.blade.php | 5 +- .../livewire/components/add-to-cart.blade.php | 2 +- .../views/livewire/components/cart.blade.php | 8 +- .../components/checkout-address.blade.php | 28 +- .../livewire/components/navigation.blade.php | 12 +- .../components/shipping-options.blade.php | 6 +- resources/views/livewire/home.blade.php | 36 +- .../views/livewire/product-page.blade.php | 2 +- .../views/partials/checkout/address.blade.php | 28 +- .../views/partials/checkout/payment.blade.php | 2 +- .../checkout/shipping_option.blade.php | 6 +- routes/web.php | 12 +- tests/CreatesApplication.php | 5 +- tests/Unit/Http/Livewire/CheckoutPageTest.php | 2 +- .../Unit/Http/Livewire/CollectionPageTest.php | 2 +- .../Livewire/Components/NavigationTest.php | 2 +- tests/Unit/Http/Livewire/HomeTest.php | 2 +- tests/Unit/Http/Livewire/ProductPageTest.php | 2 +- vite.config.js | 11 + webpack.mix.js | 17 - yarn.lock | 5285 ----------------- 105 files changed, 1167 insertions(+), 6426 deletions(-) create mode 100644 .env.docker.example delete mode 100644 .styleci.yml create mode 100644 Dockerfile rename app/{Http => }/Livewire/CheckoutPage.php (86%) rename app/{Http => }/Livewire/CheckoutSuccessPage.php (70%) rename app/{Http => }/Livewire/CollectionPage.php (52%) rename app/{Http => }/Livewire/Components/AddToCart.php (73%) rename app/{Http => }/Livewire/Components/Cart.php (77%) rename app/{Http => }/Livewire/Components/CheckoutAddress.php (88%) rename app/{Http => }/Livewire/Components/Navigation.php (80%) rename app/{Http => }/Livewire/Components/ShippingOptions.php (74%) rename app/{Http => }/Livewire/Home.php (82%) rename app/{Http => }/Livewire/ProductPage.php (71%) rename app/{Http => }/Livewire/SearchPage.php (71%) delete mode 100644 config/lunar-hub/customers.php delete mode 100644 config/lunar-hub/products.php delete mode 100644 config/lunar/cart.php delete mode 100644 config/lunar/database.php delete mode 100644 config/lunar/media.php delete mode 100644 config/lunar/orders.php delete mode 100644 config/lunar/payments.php delete mode 100644 config/lunar/pricing.php delete mode 100644 config/lunar/search.php delete mode 100644 config/lunar/shipping.php delete mode 100644 config/lunar/stripe.php delete mode 100644 config/lunar/taxes.php delete mode 100644 config/lunar/urls.php create mode 100644 database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php delete mode 100644 database/migrations/2023_04_04_000000_add_expires_at_to_personal_access_tokens_table.php create mode 100644 database/seeders/ShippingSeeder.php create mode 100644 docker-compose.yml create mode 100644 docker/caddy/Caddyfile create mode 100644 docker/php-fpm/dev/php.ini create mode 100644 docker/php-fpm/docker-entrypoint.sh create mode 100644 docker/php-fpm/php.ini create mode 100644 env.docker.example create mode 100644 vite.config.js delete mode 100644 webpack.mix.js delete mode 100644 yarn.lock diff --git a/.env.docker.example b/.env.docker.example new file mode 100644 index 0000000..7891062 --- /dev/null +++ b/.env.docker.example @@ -0,0 +1,71 @@ +APP_NAME=DemoStore +APP_ENV=local +APP_KEY=base64:xXSJ4LcR6e/0M6o8eK6RGhkbPT1W9UNmY6PP5UK+q0w= +APP_DEBUG=true +APP_URL=http://localhost + +LOG_CHANNEL=stack +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=mysql +DB_HOST=mysql +DB_PORT=3306 +DB_DATABASE=demostore +DB_USERNAME=demostore +DB_PASSWORD=password + +ADMIN_FIRSTNAME=admin +ADMIN_LASTNAME=admin +ADMIN_EMAIL=admin@lunarphp.io +ADMIN_PASSWORD=password + +## Laravel media disk +FILESYSTEM_DISK=public +## Spatie media disk +MEDIA_DISK=public +## Filament system disk +FILAMENT_FILESYSTEM_DISK=public + +BROADCAST_DRIVER=log +CACHE_DRIVER=redis +QUEUE_CONNECTION=sync +SESSION_DRIVER=redis +SESSION_LIFETIME=120 + +REDIS_HOST=redis +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=smtp +MAIL_HOST=mailhog +MAIL_PORT=1025 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null +MAIL_FROM_ADDRESS=null +MAIL_FROM_NAME="${APP_NAME}" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +PUSHER_APP_ID= +PUSHER_APP_KEY= +PUSHER_APP_SECRET= +PUSHER_APP_CLUSTER=mt1 + +MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" +MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" + +SCOUT_DRIVER=meilisearch +SCOUT_PREFIX='demostore_' +MEILISEARCH_PORT=7700 +MEILISEARCH_HOST=http://meilisearch:${MEILISEARCH_PORT} +MEILISEARCH_KEY=jyjW99LxM28kG4 +MEILISEARCH_UI_PORT=7701 + +STRIPE_KEY= +STRIPE_SECRET= diff --git a/.env.lando.example b/.env.lando.example index e16c948..977bbe2 100644 --- a/.env.lando.example +++ b/.env.lando.example @@ -15,15 +15,24 @@ DB_DATABASE=laravel DB_USERNAME=laravel DB_PASSWORD=laravel +ADMIN_FIRSTNAME=admin +ADMIN_LASTNAME=admin +ADMIN_EMAIL=admin@lunarphp.io +ADMIN_PASSWORD=password + +## Laravel media disk +FILESYSTEM_DISK=public +## Spatie media disk +MEDIA_DISK=public +## Filament system disk +FILAMENT_FILESYSTEM_DISK=public + BROADCAST_DRIVER=log -CACHE_DRIVER=file -FILESYSTEM_DISK=local +CACHE_DRIVER=redis QUEUE_CONNECTION=sync SESSION_DRIVER=file SESSION_LIFETIME=120 -MEMCACHED_HOST=127.0.0.1 - REDIS_HOST=cache REDIS_PASSWORD=null REDIS_PORT=6379 @@ -51,4 +60,12 @@ PUSHER_APP_CLUSTER=mt1 MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" -SCOUT_DRIVER=database_index +SCOUT_DRIVER=meilisearch +SCOUT_PREFIX='demostore_' +MEILISEARCH_PORT=7700 +MEILISEARCH_HOST=http://meilisearch:${MEILISEARCH_PORT} +MEILISEARCH_KEY=jyjW99LxM28kG4 +MEILISEARCH_UI_PORT=7701 + +STRIPE_KEY= +STRIPE_SECRET= diff --git a/.gitignore b/.gitignore index a554175..7c3fe60 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,10 @@ /node_modules /public/hot +/public/css/filament +/public/css/awcodes +/public/css/lunarphp +/public/js/filament +/public/js/app/components /public/storage /storage/*.key /vendor @@ -15,3 +20,9 @@ yarn-error.log /.vscode .DS_Store .lando.local.yml +composer.lock +package-lock.json +yarn.lock +config/lunar +public/build +meilisearch/ diff --git a/.lando.yml b/.lando.yml index 6f992ff..c349e22 100644 --- a/.lando.yml +++ b/.lando.yml @@ -1,19 +1,39 @@ name: demostore recipe: laravel + config: webroot: public - php: '8.0' + php: '8.2' via: nginx cache: redis + xdebug: false + services: database: portforward: 3306 + + meilisearch: + type: compose + app_mount: false + services: + image: getmeili/meilisearch:latest + command: tini -- /bin/sh -c /bin/meilisearch + ports: + - '7700' + volumes: + - meilisearch:/meili_data + volumes: + meilsearch: + mailhog: type: mailhog + portforward: true hogfrom: - appserver + node: - type: node:16 + type: node:20 + tooling: npm: service: node diff --git a/.styleci.yml b/.styleci.yml deleted file mode 100644 index 877ea70..0000000 --- a/.styleci.yml +++ /dev/null @@ -1,14 +0,0 @@ -php: - preset: laravel - version: 8 - disabled: - - no_unused_imports - finder: - not-name: - - index.php - - server.php -js: - finder: - not-name: - - webpack.mix.js -css: true diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..968eaf0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,61 @@ +ARG PHP_VERSION=8.2 +ARG COMPOSER_VERSION=2.4 + +### COMPOSER ### +FROM composer:${COMPOSER_VERSION} AS composer + +### PHP-FPM ### +FROM php:${PHP_VERSION}-fpm-alpine AS php-fpm + +WORKDIR /var/www + +COPY --from=composer /usr/bin/composer /usr/bin/composer +COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/bin/ +COPY --from=ghcr.io/shyim/gnu-libiconv:v3.14 /gnu-libiconv-1.15-r3.apk /gnu-libiconv-1.15-r3.apk + +RUN apk add --no-cache \ + unzip \ + wget \ + sudo \ + bash \ + sudo \ + supervisor \ + npm \ + aws-cli + +RUN apk add --no-cache --allow-untrusted /gnu-libiconv-1.15-r3.apk +RUN rm /gnu-libiconv-1.15-r3.apk +RUN install-php-extensions \ + bcmath \ + gd \ + intl \ + mysqli \ + pdo_mysql \ + curl \ + dom \ + fileinfo \ + filter \ + hash \ + mbstring \ + openssl \ + pcre \ + session \ + xml \ + redis \ + opcache \ + zip \ + exif + +RUN export COMPOSER_PROCESS_TIMEOUT=9000 + +COPY docker/php-fpm/dev/php.ini $PHP_INI_DIR/php.ini + +# SETUP PHP-FPM CONFIG SETTINGS (max_children / max_requests) +RUN echo 'pm.max_children = 15' >> /usr/local/etc/php-fpm.d/zz-docker.conf && \ + echo 'pm.max_requests = 500' >> /usr/local/etc/php-fpm.d/zz-docker.conf + +COPY ./docker/php-fpm/docker-entrypoint.sh /usr/local/bin/docker-entrypoint +RUN chmod +x /usr/local/bin/docker-entrypoint + +ENTRYPOINT ["docker-entrypoint"] +CMD ["php-fpm"] diff --git a/README.md b/README.md index 88da0da..aa2d1c2 100644 --- a/README.md +++ b/README.md @@ -10,3 +10,25 @@ This repository is provided as a reference to learn how to use Lunar Laravel E-C # Installation For full installation instructions please visit https://docs.lunarphp.io/core/starter-kits.html + +## Installation with Docker + +> Make sure you have Docker installed on your local machine. + +### Environment Demo store + +You can execute it via the `docker compose up` command in your favorite terminal. +Please note that the speed of building images and initializing containers depends on your local machine and internet connection - it may take some time. + +```bash +cp .env.docker.example .env +docker-compose up +``` + +The demo store will be available to `http://localhost` in your browser. + +#### Log into Lunar panel + +Once the project is prepared, the Lunar panel will start and available to `http://localhost/lunar`. + +Default admin user is username `admin@lunarphp.io` and password `password` diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 854bcc8..e6b9960 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -9,20 +9,16 @@ class Kernel extends ConsoleKernel { /** * Define the application's command schedule. - * - * @return void */ - protected function schedule(Schedule $schedule) + protected function schedule(Schedule $schedule): void { // $schedule->command('inspire')->hourly(); } /** * Register the commands for the application. - * - * @return void */ - protected function commands() + protected function commands(): void { $this->load(__DIR__.'/Commands'); diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 82a37e4..a5d87a2 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -7,24 +7,6 @@ class Handler extends ExceptionHandler { - /** - * A list of exception types with their corresponding custom log levels. - * - * @var array, \Psr\Log\LogLevel::*> - */ - protected $levels = [ - // - ]; - - /** - * A list of the exception types that are not reported. - * - * @var array> - */ - protected $dontReport = [ - // - ]; - /** * A list of the inputs that are never flashed to the session on validation exceptions. * @@ -38,10 +20,8 @@ class Handler extends ExceptionHandler /** * Register the exception handling callbacks for the application. - * - * @return void */ - public function register() + public function register(): void { $this->reportable(function (Throwable $e) { // diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index a0a2a8a..77ec359 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -3,11 +3,10 @@ namespace App\Http\Controllers; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; -use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Routing\Controller as BaseController; class Controller extends BaseController { - use AuthorizesRequests, DispatchesJobs, ValidatesRequests; + use AuthorizesRequests, ValidatesRequests; } diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 0079688..9a6e294 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -40,19 +40,19 @@ class Kernel extends HttpKernel 'api' => [ // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, - 'throttle:api', + \Illuminate\Routing\Middleware\ThrottleRequests::class.':api', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], ]; /** - * The application's route middleware. - * - * These middleware may be assigned to groups or used individually. + * The application's middleware aliases. + * * + * * Aliases may be used instead of class names to conveniently assign middleware to routes and groups. * * @var array */ - protected $routeMiddleware = [ + protected $middlewareAliases = [ 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class, @@ -60,6 +60,7 @@ class Kernel extends HttpKernel 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, + 'precognitive' => \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class, 'signed' => \App\Http\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index 704089a..d4ef644 100644 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -3,19 +3,15 @@ namespace App\Http\Middleware; use Illuminate\Auth\Middleware\Authenticate as Middleware; +use Illuminate\Http\Request; class Authenticate extends Middleware { /** * Get the path the user should be redirected to when they are not authenticated. - * - * @param \Illuminate\Http\Request $request - * @return string|null */ - protected function redirectTo($request) + protected function redirectTo(Request $request): ?string { - if (! $request->expectsJson()) { - return route('login'); - } + return $request->expectsJson() ? null : route('login'); } } diff --git a/app/Http/Middleware/TrustHosts.php b/app/Http/Middleware/TrustHosts.php index 7186414..c9c58bd 100644 --- a/app/Http/Middleware/TrustHosts.php +++ b/app/Http/Middleware/TrustHosts.php @@ -11,7 +11,7 @@ class TrustHosts extends Middleware * * @return array */ - public function hosts() + public function hosts(): array { return [ $this->allSubdomainsOfApplicationUrl(), diff --git a/app/Http/Livewire/CheckoutPage.php b/app/Livewire/CheckoutPage.php similarity index 86% rename from app/Http/Livewire/CheckoutPage.php rename to app/Livewire/CheckoutPage.php index 1412127..7fded1b 100644 --- a/app/Http/Livewire/CheckoutPage.php +++ b/app/Livewire/CheckoutPage.php @@ -1,9 +1,10 @@ getAddressValidation('shipping'), @@ -96,12 +91,7 @@ public function rules() ); } - /** - * {@inheritDoc} - * - * @return void - */ - public function mount() + public function mount(): void { if (! $this->cart = CartSession::current()) { $this->redirect('/'); @@ -130,30 +120,23 @@ public function mount() $this->determineCheckoutStep(); } - /** - * {@inheritDoc} - */ - public function hydrate() + public function hydrate(): void { $this->cart = CartSession::current(); } /** * Trigger an event to refresh addresses. - * - * @return void */ - public function triggerAddressRefresh() + public function triggerAddressRefresh(): void { - $this->emit('refreshAddress'); + $this->dispatch('refreshAddress'); } /** * Determines what checkout step we should be at. - * - * @return void */ - public function determineCheckoutStep() + public function determineCheckoutStep(): void { $shippingAddress = $this->cart->shippingAddress; $billingAddress = $this->cart->billingAddress; @@ -182,18 +165,14 @@ public function determineCheckoutStep() /** * Refresh the cart instance. - * - * @return void */ - public function refreshCart() + public function refreshCart(): void { $this->cart = CartSession::current(); } /** * Return the shipping option. - * - * @return void */ public function getShippingOptionProperty() { @@ -214,11 +193,8 @@ public function getShippingOptionProperty() /** * Save the address for a given type. - * - * @param string $type - * @return void */ - public function saveAddress($type) + public function saveAddress(string $type): void { $validatedData = $this->validate( $this->getAddressValidation($type) @@ -256,10 +232,8 @@ public function saveAddress($type) /** * Save the selected shipping option. - * - * @return void */ - public function saveShippingOption() + public function saveShippingOption(): void { $option = $this->shippingOptions->first(fn ($option) => $option->getIdentifier() == $this->chosenShipping); @@ -288,20 +262,16 @@ public function checkout() /** * Return the available countries. - * - * @return \Illuminate\Support\Collection */ - public function getCountriesProperty() + public function getCountriesProperty(): Collection { return Country::whereIn('iso3', ['GBR', 'USA'])->get(); } /** * Return available shipping options. - * - * @return \Illuminate\Support\Collection */ - public function getShippingOptionsProperty() + public function getShippingOptionsProperty(): Collection { return ShippingManifest::getOptions( $this->cart @@ -310,11 +280,8 @@ public function getShippingOptionsProperty() /** * Return the address validation rules for a given type. - * - * @param string $type - * @return array */ - protected function getAddressValidation($type) + protected function getAddressValidation(string $type): array { return [ "{$type}.first_name" => 'required', @@ -333,7 +300,7 @@ protected function getAddressValidation($type) ]; } - public function render() + public function render(): View { return view('livewire.checkout-page') ->layout('layouts.checkout'); diff --git a/app/Http/Livewire/CheckoutSuccessPage.php b/app/Livewire/CheckoutSuccessPage.php similarity index 70% rename from app/Http/Livewire/CheckoutSuccessPage.php rename to app/Livewire/CheckoutSuccessPage.php index 20ee1e7..5436a41 100644 --- a/app/Http/Livewire/CheckoutSuccessPage.php +++ b/app/Livewire/CheckoutSuccessPage.php @@ -1,27 +1,20 @@ cart = CartSession::current(); if (! $this->cart || ! $this->cart->completedOrder) { @@ -34,7 +27,7 @@ public function mount() CartSession::forget(); } - public function render() + public function render(): View { return view('livewire.checkout-success-page'); } diff --git a/app/Http/Livewire/CollectionPage.php b/app/Livewire/CollectionPage.php similarity index 52% rename from app/Http/Livewire/CollectionPage.php rename to app/Livewire/CollectionPage.php index 49e8032..1e557c4 100644 --- a/app/Http/Livewire/CollectionPage.php +++ b/app/Livewire/CollectionPage.php @@ -1,30 +1,22 @@ url = $this->fetchUrl( $slug, - Collection::class, + CollectionModel::class, [ 'element.thumbnail', 'element.products.variants.basePrices', @@ -39,18 +31,13 @@ public function mount($slug) /** * Computed property to return the collection. - * - * @return \Lunar\Models\Collection */ - public function getCollectionProperty() + public function getCollectionProperty(): mixed { return $this->url->element; } - /** - * {@inheritDoc} - */ - public function render() + public function render(): View { return view('livewire.collection-page'); } diff --git a/app/Http/Livewire/Components/AddToCart.php b/app/Livewire/Components/AddToCart.php similarity index 73% rename from app/Http/Livewire/Components/AddToCart.php rename to app/Livewire/Components/AddToCart.php index 746202c..8b2a537 100644 --- a/app/Http/Livewire/Components/AddToCart.php +++ b/app/Livewire/Components/AddToCart.php @@ -1,7 +1,8 @@ 'required|numeric|min:1|max:10000', ]; } - public function addToCart() + public function addToCart(): void { $this->validate(); if ($this->purchasable->stock < $this->quantity) { $this->addError('quantity', 'The quantity exceeds the available stock.'); + return; } CartSession::manager()->add($this->purchasable, $this->quantity); - $this->emit('add-to-cart'); + $this->dispatch('add-to-cart'); } - public function render() + public function render(): View { return view('livewire.components.add-to-cart'); } diff --git a/app/Http/Livewire/Components/Cart.php b/app/Livewire/Components/Cart.php similarity index 77% rename from app/Http/Livewire/Components/Cart.php rename to app/Livewire/Components/Cart.php index ac73fd5..c08ec6e 100644 --- a/app/Http/Livewire/Components/Cart.php +++ b/app/Livewire/Components/Cart.php @@ -1,7 +1,9 @@ 'handleAddToCart', ]; - /** - * {@inheritDoc} - */ - public function rules() + public function rules(): array { return [ 'lines.*.quantity' => 'required|numeric|min:1|max:10000', ]; } - /** - * {@inheritDoc} - */ - public function mount() + public function mount(): void { $this->mapLines(); } /** * Get the current cart instance. - * - * @return \Lunar\Managers\CartManager */ public function getCartProperty() { @@ -48,20 +42,16 @@ public function getCartProperty() /** * Return the cart lines from the cart. - * - * @return \Illuminate\Support\Collection */ - public function getCartLinesProperty() + public function getCartLinesProperty(): Collection { return $this->cart->lines ?? collect(); } /** * Update the cart lines. - * - * @return void */ - public function updateLines() + public function updateLines(): void { $this->validate(); @@ -69,10 +59,10 @@ public function updateLines() collect($this->lines) ); $this->mapLines(); - $this->emit('cartUpdated'); + $this->dispatch('cartUpdated'); } - public function removeLine($id) + public function removeLine($id): void { CartSession::remove($id); $this->mapLines(); @@ -83,10 +73,8 @@ public function removeLine($id) * * We want to map out our cart lines like this so we can * add some validation rules and make them editable. - * - * @return void */ - public function mapLines() + public function mapLines(): void { $this->lines = $this->cartLines->map(function ($line) { return [ @@ -103,13 +91,13 @@ public function mapLines() })->toArray(); } - public function handleAddToCart() + public function handleAddToCart(): void { $this->mapLines(); $this->linesVisible = true; } - public function render() + public function render(): View { return view('livewire.components.cart'); } diff --git a/app/Http/Livewire/Components/CheckoutAddress.php b/app/Livewire/Components/CheckoutAddress.php similarity index 88% rename from app/Http/Livewire/Components/CheckoutAddress.php rename to app/Livewire/Components/CheckoutAddress.php index 264f094..4b65724 100644 --- a/app/Http/Livewire/Components/CheckoutAddress.php +++ b/app/Livewire/Components/CheckoutAddress.php @@ -1,7 +1,8 @@ cart = CartSession::current(); @@ -56,10 +50,7 @@ public function mount() $this->editing = (bool) ! $this->address->id; } - /** - * {@inheritDoc} - */ - public function rules() + public function rules(): array { return [ 'address.first_name' => 'required', @@ -80,10 +71,8 @@ public function rules() /** * Save the cart address. - * - * @return void */ - public function save() + public function save(): void { $validatedData = $this->validate(); @@ -112,7 +101,7 @@ public function save() $this->emitUp('addressUpdated'); } - public function refreshAddress() + public function refreshAddress(): void { if ($address = $this->cart->addresses()->whereType($this->type)->first()) { $this->address = $address; @@ -125,7 +114,7 @@ public function getCountriesProperty() return Country::whereIn('iso3', ['GBR', 'USA'])->get(); } - public function render() + public function render(): View { return view('livewire.components.checkout-address'); } diff --git a/app/Http/Livewire/Components/Navigation.php b/app/Livewire/Components/Navigation.php similarity index 80% rename from app/Http/Livewire/Components/Navigation.php rename to app/Livewire/Components/Navigation.php index 4aa02b4..81a1dba 100644 --- a/app/Http/Livewire/Components/Navigation.php +++ b/app/Livewire/Components/Navigation.php @@ -1,7 +1,8 @@ get()->toTree(); } - public function render() + public function render(): View { return view('livewire.components.navigation'); } diff --git a/app/Http/Livewire/Components/ShippingOptions.php b/app/Livewire/Components/ShippingOptions.php similarity index 74% rename from app/Http/Livewire/Components/ShippingOptions.php rename to app/Livewire/Components/ShippingOptions.php index 1ab7331..8815771 100644 --- a/app/Http/Livewire/Components/ShippingOptions.php +++ b/app/Livewire/Components/ShippingOptions.php @@ -1,7 +1,9 @@ shippingAddress?->shipping_option) { $option = $this->shippingOptions->first(function ($opt) use ($shippingOption) { @@ -30,20 +27,15 @@ public function mount() /** * Return available shipping options. - * - * @return \Illuminate\Support\Collection */ - public function getShippingOptionsProperty() + public function getShippingOptionsProperty(): Collection { return ShippingManifest::getOptions( CartSession::current() ); } - /** - * {@inheritDoc} - */ - public function rules() + public function rules(): array { return [ 'chosenOption' => 'required', @@ -52,10 +44,8 @@ public function rules() /** * Save the shipping option. - * - * @return void */ - public function save() + public function save(): void { $this->validate(); @@ -63,20 +53,18 @@ public function save() CartSession::setShippingOption($option); - $this->emit('selectedShippingOption'); + $this->dispatch('selectedShippingOption'); } /** * Return whether we have a shipping address. - * - * @return void */ public function getShippingAddressProperty() { return CartSession::getCart()->shippingAddress; } - public function render() + public function render(): View { return view('livewire.components.shipping-options'); } diff --git a/app/Http/Livewire/Home.php b/app/Livewire/Home.php similarity index 82% rename from app/Http/Livewire/Home.php rename to app/Livewire/Home.php index ad1f2c3..247d83a 100644 --- a/app/Http/Livewire/Home.php +++ b/app/Livewire/Home.php @@ -1,7 +1,8 @@ whereSlug('sale')->first()?->element ?? null; } /** * Return all images in sale collection. - * - * @return void */ public function getSaleCollectionImagesProperty() { if (! $this->getSaleCollectionProperty()) { - return; + return null; } $collectionProducts = $this->getSaleCollectionProperty() @@ -41,10 +38,8 @@ public function getSaleCollectionImagesProperty() /** * Return a random collection. - * - * @return void */ - public function getRandomCollectionProperty() + public function getRandomCollectionProperty(): ?Collection { $collections = Url::whereElementType(Collection::class); @@ -55,7 +50,7 @@ public function getRandomCollectionProperty() return $collections->inRandomOrder()->first()?->element; } - public function render() + public function render(): View { return view('livewire.home'); } diff --git a/app/Http/Livewire/ProductPage.php b/app/Livewire/ProductPage.php similarity index 71% rename from app/Http/Livewire/ProductPage.php rename to app/Livewire/ProductPage.php index 38aafbf..767e2c0 100644 --- a/app/Http/Livewire/ProductPage.php +++ b/app/Livewire/ProductPage.php @@ -1,30 +1,25 @@ url = $this->fetchUrl( $slug, @@ -48,10 +43,8 @@ public function mount($slug) /** * Computed property to get variant. - * - * @return \Lunar\Models\ProductVariant */ - public function getVariantProperty() + public function getVariantProperty(): ProductVariant { return $this->product->variants->first(function ($variant) { return ! $variant->values->pluck('id') @@ -63,20 +56,16 @@ public function getVariantProperty() /** * Computed property to return all available option values. - * - * @return \Illuminate\Support\Collection */ - public function getProductOptionValuesProperty() + public function getProductOptionValuesProperty(): Collection { return $this->product->variants->pluck('values')->flatten(); } /** * Computed propert to get available product options with values. - * - * @return \Illuminate\Support\Collection */ - public function getProductOptionsProperty() + public function getProductOptionsProperty(): Collection { return $this->productOptionValues->unique('id')->groupBy('product_option_id') ->map(function ($values) { @@ -89,30 +78,24 @@ public function getProductOptionsProperty() /** * Computed property to return product. - * - * @return \Lunar\Models\Product */ - public function getProductProperty() + public function getProductProperty(): Product { return $this->url->element; } /** * Return all images for the product. - * - * @return \Illuminate\Support\Collection */ - public function getImagesProperty() + public function getImagesProperty(): Collection { return $this->product->media; } /** * Computed property to return current image. - * - * @return string */ - public function getImageProperty() + public function getImageProperty(): Media { if (count($this->variant->images)) { return $this->variant->images->first(); @@ -125,10 +108,7 @@ public function getImageProperty() return $this->images->first(); } - /** - * {@inheritDoc} - */ - public function render() + public function render(): View { return view('livewire.product-page'); } diff --git a/app/Http/Livewire/SearchPage.php b/app/Livewire/SearchPage.php similarity index 71% rename from app/Http/Livewire/SearchPage.php rename to app/Livewire/SearchPage.php index 3bb716e..4ffc903 100644 --- a/app/Http/Livewire/SearchPage.php +++ b/app/Livewire/SearchPage.php @@ -1,7 +1,9 @@ term)->paginate(50); } - public function render() + public function render(): View { return view('livewire.search-page'); } diff --git a/app/Models/User.php b/app/Models/User.php index 7f75b8e..12d2745 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -10,7 +10,7 @@ class User extends Authenticatable { - use HasApiTokens, HasFactory, Notifiable, LunarUser; + use HasApiTokens, HasFactory, LunarUser, Notifiable; /** * The attributes that are mass assignable. diff --git a/app/Modifiers/ShippingModifier.php b/app/Modifiers/ShippingModifier.php index df05181..26addfd 100644 --- a/app/Modifiers/ShippingModifier.php +++ b/app/Modifiers/ShippingModifier.php @@ -2,37 +2,31 @@ namespace App\Modifiers; -use Lunar\DataTypes\Price; -use Lunar\DataTypes\ShippingOption; -use Lunar\Facades\ShippingManifest; use Lunar\Models\Cart; -use Lunar\Models\TaxClass; class ShippingModifier { - public function handle(Cart $cart) + public function handle(Cart $cart, \Closure $next) { - // Get the tax class - $taxClass = TaxClass::getDefault(); + /** + * Custom shipping option. + * -------------------------------------------- + * If you do not wish to use the shipping add-on you can add + * your own shipping options that will appear at checkout + */ - ShippingManifest::addOption( - new ShippingOption( - name: 'Basic Delivery', - description: 'Basic Delivery', - identifier: 'BASDEL', - price: new Price(500, $cart->currency, 1), - taxClass: $taxClass - ) - ); + /** + \Lunar\Facades\ShippingManifest::addOption( + new \Lunar\DataTypes\ShippingOption( + name: 'Basic Delivery', + description: 'Basic Delivery', + identifier: 'BASDEL', + price: new \Lunar\DataTypes\Price(500, $cart->currency, 1), + taxClass: \Lunar\Models\TaxClass::getDefault() + ) + ); + */ - ShippingManifest::addOption( - new ShippingOption( - name: 'Express Delivery', - description: 'Express Delivery', - identifier: 'EXPDEL', - price: new Price(1000, $cart->currency, 1), - taxClass: $taxClass - ) - ); + return $next($cart); } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index fa2f6e8..2e56bc7 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -4,26 +4,29 @@ use App\Modifiers\ShippingModifier; use Illuminate\Support\ServiceProvider; +use Lunar\Admin\Support\Facades\LunarPanel; use Lunar\Base\ShippingModifiers; +use Lunar\Shipping\ShippingPlugin; class AppServiceProvider extends ServiceProvider { /** * Register any application services. - * - * @return void */ - public function register() + public function register(): void { - // + LunarPanel::panel( + fn ($panel) => $panel->plugins([ + new ShippingPlugin, + ]) + ) + ->register(); } /** * Bootstrap any application services. - * - * @return void */ - public function boot(ShippingModifiers $shippingModifiers) + public function boot(ShippingModifiers $shippingModifiers): void { $shippingModifiers->add( ShippingModifier::class diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 59e239f..f4607be 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -17,13 +17,9 @@ class AuthServiceProvider extends ServiceProvider /** * Register any authentication / authorization services. - * - * @return void */ - public function boot() + public function boot(): void { - $this->registerPolicies(); - // } } diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index ab8b2cf..2d65aac 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -22,20 +22,16 @@ class EventServiceProvider extends ServiceProvider /** * Register any events for your application. - * - * @return void */ - public function boot() + public function boot(): void { // } /** * Determine if events and listeners should be automatically discovered. - * - * @return bool */ - public function shouldDiscoverEvents() + public function shouldDiscoverEvents(): bool { return false; } diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 0ba5291..0c18923 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -11,26 +11,24 @@ class RouteServiceProvider extends ServiceProvider { /** - * The path to the "home" route for your application. + * The path to your application's "home" route. * - * This is used by Laravel authentication to redirect users after login. + * Typically, users are redirected here after authentication. * * @var string */ public const HOME = '/home'; /** - * Define your route model bindings, pattern filters, etc. - * - * @return void + * Define your route model bindings, pattern filters, and other route configuration. */ - public function boot() + public function boot(): void { $this->configureRateLimiting(); $this->routes(function () { - Route::prefix('api') - ->middleware('api') + Route::middleware('api') + ->prefix('api') ->group(base_path('routes/api.php')); Route::middleware('web') @@ -40,10 +38,8 @@ public function boot() /** * Configure the rate limiters for the application. - * - * @return void */ - protected function configureRateLimiting() + protected function configureRateLimiting(): void { RateLimiter::for('api', function (Request $request) { return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); diff --git a/app/Traits/FetchesUrls.php b/app/Traits/FetchesUrls.php index 2c7da96..b480ae7 100644 --- a/app/Traits/FetchesUrls.php +++ b/app/Traits/FetchesUrls.php @@ -8,8 +8,6 @@ trait FetchesUrls { /** * The URL model from the slug. - * - * @var \Lunar\Models\Url */ public ?Url $url = null; @@ -19,9 +17,8 @@ trait FetchesUrls * @param string $slug * @param string $type * @param array $eagerLoad - * @return \Lunar\Models\Url|null */ - public function fetchUrl($slug, $type, $eagerLoad = []) + public function fetchUrl($slug, $type, $eagerLoad = []): ?Url { return Url::whereElementType($type) ->whereDefault(true) diff --git a/app/View/Components/ProductPrice.php b/app/View/Components/ProductPrice.php index 2833b4a..e66e756 100644 --- a/app/View/Components/ProductPrice.php +++ b/app/View/Components/ProductPrice.php @@ -3,6 +3,7 @@ namespace App\View\Components; use Illuminate\View\Component; +use Illuminate\View\View; use Lunar\Facades\Pricing; use Lunar\Models\Price; use Lunar\Models\ProductVariant; @@ -27,10 +28,8 @@ public function __construct($product = null, $variant = null) /** * Get the view / contents that represent the component. - * - * @return \Illuminate\Contracts\View\View|\Closure|string */ - public function render() + public function render(): View { return view('components.product-price'); } diff --git a/composer.json b/composer.json index c4fa343..d78d696 100644 --- a/composer.json +++ b/composer.json @@ -8,23 +8,24 @@ ], "license": "MIT", "require": { - "php": "^8.1", - "lunarphp/lunar": "^0.8", - "lunarphp/stripe": "^0.8", + "php": "^8.2", "guzzlehttp/guzzle": "^7.2", "http-interop/http-factory-guzzle": "^1.2", - "laravel/framework": "^9.52", - "laravel/sanctum": "^3.0", - "laravel/tinker": "^2.7", - "league/flysystem-aws-s3-v3": "^3.0" + "laravel/framework": "^v10.43", + "laravel/sanctum": "^3.3", + "laravel/tinker": "^2.8", + "league/flysystem-aws-s3-v3": "^3.0", + "lunarphp/lunar": "^1.0", + "lunarphp/table-rate-shipping": "^1.0", + "meilisearch/meilisearch-php": "^1.6", + "predis/predis": "^2.2" }, "require-dev": { "fakerphp/faker": "^1.9.1", - "laravel/sail": "^1.15", "mockery/mockery": "^1.4.4", "nunomaduro/collision": "^6.3", - "phpunit/phpunit": "^9.5.10", - "spatie/laravel-ignition": "^1.4" + "phpunit/phpunit": "^10.1", + "spatie/laravel-ignition": "^2.0" }, "autoload": { "psr-4": { diff --git a/config/app.php b/config/app.php index 23a4297..194acca 100644 --- a/config/app.php +++ b/config/app.php @@ -1,6 +1,7 @@ [ - - /* - * Laravel Framework Service Providers... - */ - Illuminate\Auth\AuthServiceProvider::class, - Illuminate\Broadcasting\BroadcastServiceProvider::class, - Illuminate\Bus\BusServiceProvider::class, - Illuminate\Cache\CacheServiceProvider::class, - Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, - Illuminate\Cookie\CookieServiceProvider::class, - Illuminate\Database\DatabaseServiceProvider::class, - Illuminate\Encryption\EncryptionServiceProvider::class, - Illuminate\Filesystem\FilesystemServiceProvider::class, - Illuminate\Foundation\Providers\FoundationServiceProvider::class, - Illuminate\Hashing\HashServiceProvider::class, - Illuminate\Mail\MailServiceProvider::class, - Illuminate\Notifications\NotificationServiceProvider::class, - Illuminate\Pagination\PaginationServiceProvider::class, - Illuminate\Pipeline\PipelineServiceProvider::class, - Illuminate\Queue\QueueServiceProvider::class, - Illuminate\Redis\RedisServiceProvider::class, - Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, - Illuminate\Session\SessionServiceProvider::class, - Illuminate\Translation\TranslationServiceProvider::class, - Illuminate\Validation\ValidationServiceProvider::class, - Illuminate\View\ViewServiceProvider::class, - + 'providers' => ServiceProvider::defaultProviders()->merge([ /* * Package Service Providers... */ @@ -194,8 +168,7 @@ // App\Providers\BroadcastServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, - - ], + ])->toArray(), /* |-------------------------------------------------------------------------- diff --git a/config/auth.php b/config/auth.php index d8c6cee..aa6b780 100644 --- a/config/auth.php +++ b/config/auth.php @@ -89,7 +89,7 @@ 'passwords' => [ 'users' => [ 'provider' => 'users', - 'table' => 'password_resets', + 'table' => 'password_reset_tokens', 'expire' => 60, 'throttle' => 60, ], diff --git a/config/broadcasting.php b/config/broadcasting.php index 9e4d4aa..2410485 100644 --- a/config/broadcasting.php +++ b/config/broadcasting.php @@ -36,6 +36,7 @@ 'secret' => env('PUSHER_APP_SECRET'), 'app_id' => env('PUSHER_APP_ID'), 'options' => [ + 'cluster' => env('PUSHER_APP_CLUSTER'), 'host' => env('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com', 'port' => env('PUSHER_PORT', 443), 'scheme' => env('PUSHER_SCHEME', 'https'), diff --git a/config/cache.php b/config/cache.php index 33bb295..d4171e2 100644 --- a/config/cache.php +++ b/config/cache.php @@ -52,6 +52,7 @@ 'file' => [ 'driver' => 'file', 'path' => storage_path('framework/cache/data'), + 'lock_path' => storage_path('framework/cache/data'), ], 'memcached' => [ diff --git a/config/database.php b/config/database.php index dc722b5..2a42e19 100644 --- a/config/database.php +++ b/config/database.php @@ -129,7 +129,8 @@ 'default' => [ 'url' => env('REDIS_URL'), 'host' => env('REDIS_HOST', '127.0.0.1'), - 'password' => env('REDIS_PASSWORD', null), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), 'port' => env('REDIS_PORT', '6379'), 'database' => env('REDIS_DB', '0'), ], @@ -137,7 +138,8 @@ 'cache' => [ 'url' => env('REDIS_URL'), 'host' => env('REDIS_HOST', '127.0.0.1'), - 'password' => env('REDIS_PASSWORD', null), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), 'port' => env('REDIS_PORT', '6379'), 'database' => env('REDIS_CACHE_DB', '1'), ], diff --git a/config/hashing.php b/config/hashing.php index bcd3be4..f13aa19 100644 --- a/config/hashing.php +++ b/config/hashing.php @@ -29,7 +29,8 @@ */ 'bcrypt' => [ - 'rounds' => env('BCRYPT_ROUNDS', 10), + 'rounds' => env('BCRYPT_ROUNDS', 12), + 'verify' => true, ], /* diff --git a/config/livewire.php b/config/livewire.php index 238e31e..f7f1eed 100644 --- a/config/livewire.php +++ b/config/livewire.php @@ -3,156 +3,157 @@ return [ /* - |-------------------------------------------------------------------------- + |--------------------------------------------------------------------------- | Class Namespace - |-------------------------------------------------------------------------- + |--------------------------------------------------------------------------- | - | This value sets the root namespace for Livewire component classes in - | your application. This value affects component auto-discovery and - | any Livewire file helper commands, like `artisan make:livewire`. - | - | After changing this item, run: `php artisan livewire:discover`. + | This value sets the root class namespace for Livewire component classes in + | your application. This value will change where component auto-discovery + | finds components. It's also referenced by the file creation commands. | */ - 'class_namespace' => 'App\\Http\\Livewire', + 'class_namespace' => 'App\\Livewire', /* - |-------------------------------------------------------------------------- + |--------------------------------------------------------------------------- | View Path - |-------------------------------------------------------------------------- + |--------------------------------------------------------------------------- | - | This value sets the path for Livewire component views. This affects - | file manipulation helper commands like `artisan make:livewire`. + | This value is used to specify where Livewire component Blade templates are + | stored when running file creation commands like `artisan make:livewire`. + | It is also used if you choose to omit a component's render() method. | */ 'view_path' => resource_path('views/livewire'), /* - |-------------------------------------------------------------------------- + |--------------------------------------------------------------------------- | Layout - |-------------------------------------------------------------------------- - | The default layout view that will be used when rendering a component via - | Route::get('/some-endpoint', SomeComponent::class);. In this case the - | the view returned by SomeComponent will be wrapped in "layouts.app" + |--------------------------------------------------------------------------- + | The view that will be used as the layout when rendering a single component + | as an entire page via `Route::get('/post/create', CreatePost::class);`. + | In this case, the view returned by CreatePost will render into $slot. | */ 'layout' => 'layouts.storefront', /* - |-------------------------------------------------------------------------- - | Livewire Assets URL - |-------------------------------------------------------------------------- + |--------------------------------------------------------------------------- + | Lazy Loading Placeholder + |--------------------------------------------------------------------------- + | Livewire allows you to lazy load components that would otherwise slow down + | the initial page load. Every component can have a custom placeholder or + | you can define the default placeholder view for all components below. | - | This value sets the path to Livewire JavaScript assets, for cases where - | your app's domain root is not the correct path. By default, Livewire - | will load its JavaScript assets from the app's "relative root". + */ + + 'lazy_placeholder' => null, + + /* + |--------------------------------------------------------------------------- + | Temporary File Uploads + |--------------------------------------------------------------------------- | - | Examples: "/assets", "myurl.com/app". + | Livewire handles file uploads by storing uploads in a temporary directory + | before the file is stored permanently. All file uploads are directed to + | a global endpoint for temporary storage. You may configure this below: | */ - 'asset_url' => null, + 'temporary_file_upload' => [ + 'disk' => null, // Example: 'local', 's3' | Default: 'default' + 'rules' => null, // Example: ['file', 'mimes:png,jpg'] | Default: ['required', 'file', 'max:12288'] (12MB) + 'directory' => null, // Example: 'tmp' | Default: 'livewire-tmp' + 'middleware' => null, // Example: 'throttle:5,1' | Default: 'throttle:60,1' + 'preview_mimes' => [ // Supported file types for temporary pre-signed file URLs... + 'png', 'gif', 'bmp', 'svg', 'wav', 'mp4', + 'mov', 'avi', 'wmv', 'mp3', 'm4a', + 'jpg', 'jpeg', 'mpga', 'webp', 'wma', + ], + 'max_upload_time' => 5, // Max duration (in minutes) before an upload is invalidated... + ], /* - |-------------------------------------------------------------------------- - | Livewire App URL - |-------------------------------------------------------------------------- - | - | This value should be used if livewire assets are served from CDN. - | Livewire will communicate with an app through this url. + |--------------------------------------------------------------------------- + | Render On Redirect + |--------------------------------------------------------------------------- | - | Examples: "https://my-app.com", "myurl.com/app". + | This value determines if Livewire will run a component's `render()` method + | after a redirect has been triggered using something like `redirect(...)` + | Setting this to true will render the view once more before redirecting | */ - 'app_url' => null, + 'render_on_redirect' => false, /* - |-------------------------------------------------------------------------- - | Livewire Endpoint Middleware Group - |-------------------------------------------------------------------------- + |--------------------------------------------------------------------------- + | Eloquent Model Binding + |--------------------------------------------------------------------------- | - | This value sets the middleware group that will be applied to the main - | Livewire "message" endpoint (the endpoint that gets hit everytime - | a Livewire component updates). It is set to "web" by default. + | Previous versions of Livewire supported binding directly to eloquent model + | properties using wire:model by default. However, this behavior has been + | deemed too "magical" and has therefore been put under a feature flag. | */ - 'middleware_group' => 'web', + 'legacy_model_binding' => true, /* - |-------------------------------------------------------------------------- - | Livewire Temporary File Uploads Endpoint Configuration - |-------------------------------------------------------------------------- + |--------------------------------------------------------------------------- + | Auto-inject Frontend Assets + |--------------------------------------------------------------------------- | - | Livewire handles file uploads by storing uploads in a temporary directory - | before the file is validated and stored permanently. All file uploads - | are directed to a global endpoint for temporary storage. The config - | items below are used for customizing the way the endpoint works. + | By default, Livewire automatically injects its JavaScript and CSS into the + | and of pages containing Livewire components. By disabling + | this behavior, you need to use @livewireStyles and @livewireScripts. | */ - 'temporary_file_upload' => [ - 'disk' => null, // Example: 'local', 's3' Default: 'default' - 'rules' => null, // Example: ['file', 'mimes:png,jpg'] Default: ['required', 'file', 'max:12288'] (12MB) - 'directory' => null, // Example: 'tmp' Default 'livewire-tmp' - 'middleware' => null, // Example: 'throttle:5,1' Default: 'throttle:60,1' - 'preview_mimes' => [ // Supported file types for temporary pre-signed file URLs. - 'png', 'gif', 'bmp', 'svg', 'wav', 'mp4', - 'mov', 'avi', 'wmv', 'mp3', 'm4a', - 'jpg', 'jpeg', 'mpga', 'webp', 'wma', - ], - 'max_upload_time' => 5, // Max duration (in minutes) before an upload gets invalidated. - ], + 'inject_assets' => true, /* - |-------------------------------------------------------------------------- - | Manifest File Path - |-------------------------------------------------------------------------- - | - | This value sets the path to the Livewire manifest file. - | The default should work for most cases (which is - | "/bootstrap/cache/livewire-components.php"), but for specific - | cases like when hosting on Laravel Vapor, it could be set to a different value. + |--------------------------------------------------------------------------- + | Navigate (SPA mode) + |--------------------------------------------------------------------------- | - | Example: for Laravel Vapor, it would be "/tmp/storage/bootstrap/cache/livewire-components.php". + | By adding `wire:navigate` to links in your Livewire application, Livewire + | will prevent the default link handling and instead request those pages + | via AJAX, creating an SPA-like effect. Configure this behavior here. | */ - 'manifest_path' => null, + 'navigate' => [ + 'show_progress_bar' => true, + 'progress_bar_color' => '#2299dd', + ], /* - |-------------------------------------------------------------------------- - | Back Button Cache - |-------------------------------------------------------------------------- - | - | This value determines whether the back button cache will be used on pages - | that contain Livewire. By disabling back button cache, it ensures that - | the back button shows the correct state of components, instead of - | potentially stale, cached data. + |--------------------------------------------------------------------------- + | HTML Morph Markers + |--------------------------------------------------------------------------- | - | Setting it to "false" (default) will disable back button cache. + | Livewire intelligently "morphs" existing HTML into the newly rendered HTML + | after each update. To make this process more reliable, Livewire injects + | "markers" into the rendered Blade surrounding @if, @class & @foreach. | */ - 'back_button_cache' => false, + 'inject_morph_markers' => true, /* - |-------------------------------------------------------------------------- - | Render On Redirect - |-------------------------------------------------------------------------- + |--------------------------------------------------------------------------- + | Pagination Theme + |--------------------------------------------------------------------------- | - | This value determines whether Livewire will render before it's redirected - | or not. Setting it to "false" (default) will mean the render method is - | skipped when redirecting. And "true" will mean the render method is - | run before redirecting. Browsers bfcache can store a potentially - | stale view if render is skipped on redirect. + | When enabling Livewire's pagination feature by using the `WithPagination` + | trait, Livewire will use Tailwind templates to render pagination views + | on the page. If you want Bootstrap CSS, you can specify: "bootstrap" | */ - 'render_on_redirect' => false, - + 'pagination_theme' => 'tailwind', ]; diff --git a/config/logging.php b/config/logging.php index 5aa1dbb..a114cb2 100644 --- a/config/logging.php +++ b/config/logging.php @@ -3,6 +3,7 @@ use Monolog\Handler\NullHandler; use Monolog\Handler\StreamHandler; use Monolog\Handler\SyslogUdpHandler; +use Monolog\Processor\PsrLogMessageProcessor; return [ @@ -55,12 +56,14 @@ 'driver' => 'stack', 'channels' => ['single'], 'ignore_exceptions' => false, + 'replace_placeholders' => true, ], 'single' => [ 'driver' => 'single', 'path' => storage_path('logs/laravel.log'), 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, ], 'daily' => [ @@ -68,6 +71,7 @@ 'path' => storage_path('logs/laravel.log'), 'level' => env('LOG_LEVEL', 'debug'), 'days' => 14, + 'replace_placeholders' => true, ], 'slack' => [ @@ -76,6 +80,7 @@ 'username' => 'Laravel Log', 'emoji' => ':boom:', 'level' => env('LOG_LEVEL', 'critical'), + 'replace_placeholders' => true, ], 'papertrail' => [ @@ -87,6 +92,7 @@ 'port' => env('PAPERTRAIL_PORT'), 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'), ], + 'processors' => [PsrLogMessageProcessor::class], ], 'stderr' => [ @@ -97,16 +103,20 @@ 'with' => [ 'stream' => 'php://stderr', ], + 'processors' => [PsrLogMessageProcessor::class], ], 'syslog' => [ 'driver' => 'syslog', 'level' => env('LOG_LEVEL', 'debug'), + 'facility' => LOG_USER, + 'replace_placeholders' => true, ], 'errorlog' => [ 'driver' => 'errorlog', 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, ], 'null' => [ diff --git a/config/lunar-hub/customers.php b/config/lunar-hub/customers.php deleted file mode 100644 index 0b74931..0000000 --- a/config/lunar-hub/customers.php +++ /dev/null @@ -1,16 +0,0 @@ - [], -]; diff --git a/config/lunar-hub/products.php b/config/lunar-hub/products.php deleted file mode 100644 index b153af8..0000000 --- a/config/lunar-hub/products.php +++ /dev/null @@ -1,31 +0,0 @@ - [ - 'required' => true, - 'unique' => true, - ], - 'gtin' => [ - 'required' => false, - 'unique' => false, - ], - 'mpn' => [ - 'required' => false, - 'unique' => false, - ], - 'ean' => [ - 'required' => false, - 'unique' => false, - ], -]; diff --git a/config/lunar/cart.php b/config/lunar/cart.php deleted file mode 100644 index 8396f43..0000000 --- a/config/lunar/cart.php +++ /dev/null @@ -1,145 +0,0 @@ - 'lunar_cart', - - /* - |-------------------------------------------------------------------------- - | Fingerprint Generator - |-------------------------------------------------------------------------- - | - | Specify which class should be used when generating a cart fingerprint. - | - */ - 'fingerprint_generator' => Lunar\Actions\Carts\GenerateFingerprint::class, - - /* - |-------------------------------------------------------------------------- - | Auto create a cart when none exists for user. - |-------------------------------------------------------------------------- - | - | Determines whether you want to automatically create a cart for a user if - | they do not currently have one in the session. By default this is false - | to minimise the amount of cart lines added to the database. - | - */ - 'auto_create' => false, - - /* - |-------------------------------------------------------------------------- - | Authentication policy - |-------------------------------------------------------------------------- - | - | When a user logs in, by default, Lunar will merge the current (guest) cart - | with the users current cart, if they have one. - | Available options: 'merge', 'override' - | - */ - 'auth_policy' => 'merge', - - /* - |-------------------------------------------------------------------------- - | Cart Pipelines - |-------------------------------------------------------------------------- - | - | Define which pipelines should be run when performing cart calculations. - | The default ones provided should suit most needs, however you are - | free to add your own as you see fit. - | - | Each pipeline class will be run from top to bottom. - | - */ - 'pipelines' => [ - /* - |-------------------------------------------------------------------------- - | Run these pipelines when the cart is calculating. - |-------------------------------------------------------------------------- - */ - 'cart' => [ - \Lunar\Pipelines\Cart\CalculateLines::class, - \Lunar\Pipelines\Cart\ApplyShipping::class, - \Lunar\Pipelines\Cart\ApplyDiscounts::class, - \Lunar\Pipelines\Cart\CalculateTax::class, - \Lunar\Pipelines\Cart\Calculate::class, - ], - /* - |-------------------------------------------------------------------------- - | Run these pipelines when the cart lines are being calculated. - |-------------------------------------------------------------------------- - */ - 'cart_lines' => [ - \Lunar\Pipelines\CartLine\GetUnitPrice::class, - ], - ], - - /* - |-------------------------------------------------------------------------- - | Cart Actions - |-------------------------------------------------------------------------- - | - | Here you can decide what action should be run during a Carts lifecycle. - | The default actions should be fine for most cases. - | - */ - 'actions' => [ - 'add_to_cart' => \Lunar\Actions\Carts\AddOrUpdatePurchasable::class, - 'update_cart_line' => \Lunar\Actions\Carts\UpdateCartLine::class, - 'remove_from_cart' => \Lunar\Actions\Carts\RemovePurchasable::class, - 'add_address' => \Lunar\Actions\Carts\AddAddress::class, - 'set_shipping_option' => \Lunar\Actions\Carts\SetShippingOption::class, - 'order_create' => \Lunar\Actions\Carts\CreateOrder::class, - ], - - /* - |-------------------------------------------------------------------------- - | Cart Action Validators - |-------------------------------------------------------------------------- - | - | You may wish to provide additional validation when actions executed on - | the cart model. The defaults provided should be enough for most cases. - | - */ - 'validators' => [ - 'add_to_cart' => [ - \Lunar\Validation\CartLine\CartLineQuantity::class, - ], - 'update_cart_line' => [ - \Lunar\Validation\CartLine\CartLineQuantity::class, - ], - 'remove_from_cart' => [], - 'set_shipping_option' => [ - \Lunar\Validation\Cart\ShippingOptionValidator::class, - ], - 'order_create' => [ - \Lunar\Validation\Cart\ValidateCartForOrderCreation::class, - ], - ], - - /* - |-------------------------------------------------------------------------- - | Default eager loading - |-------------------------------------------------------------------------- - | - | When loading up a cart and doing calculations, there's a few relationships - | that are used when it's running. Here you can define which relationships - | should be eager loaded when these calculations take place. - | - */ - 'eager_load' => [ - 'currency', - 'shippingAddress', - 'billingAddress', - 'lines.purchasable.prices.currency', - 'lines.purchasable.prices.priceable', - 'lines.purchasable.product', - 'lines.cart', - ], -]; diff --git a/config/lunar/database.php b/config/lunar/database.php deleted file mode 100644 index b106f8a..0000000 --- a/config/lunar/database.php +++ /dev/null @@ -1,18 +0,0 @@ - '', - - 'table_prefix' => 'lunar_', - - /* - |-------------------------------------------------------------------------- - | Users Table ID - |-------------------------------------------------------------------------- - | - | Lunar adds a relationship to your 'users' table and by default assumes - | a 'bigint'. You can change this to either an 'int' or 'uuid'. - | - */ - 'users_id_type' => 'bigint', -]; diff --git a/config/lunar/media.php b/config/lunar/media.php deleted file mode 100644 index fcbb737..0000000 --- a/config/lunar/media.php +++ /dev/null @@ -1,13 +0,0 @@ - [ - StandardMediaConversions::class, - ], - 'fallback' => [ - 'url' => env('FALLBACK_IMAGE_URL', null), - 'path' => env('FALLBACK_IMAGE_PATH', null), - ], -]; diff --git a/config/lunar/orders.php b/config/lunar/orders.php deleted file mode 100644 index fc41e44..0000000 --- a/config/lunar/orders.php +++ /dev/null @@ -1,52 +0,0 @@ - OrderReferenceGenerator::class, - /* - |-------------------------------------------------------------------------- - | Draft Status - |-------------------------------------------------------------------------- - | - | When a draft order is created from a cart, we need an initial status for - | the order that's created. Define that here, it can be anything that would - | make sense for the store you're building. - | - */ - 'draft_status' => 'awaiting-payment', - 'statuses' => [ - 'awaiting-payment' => [ - 'label' => 'Awaiting Payment', - 'color' => '#848a8c', - 'mailers' => [], - 'notifications' => [], - ], - 'payment-offline' => [ - 'label' => 'Payment Offline', - 'color' => '#0A81D7', - 'mailers' => [], - 'notifications' => [], - ], - 'payment-received' => [ - 'label' => 'Payment Received', - 'color' => '#6a67ce', - 'mailers' => [], - 'notifications' => [], - ], - 'dispatched' => [ - 'label' => 'Dispatched', - 'mailers' => [], - 'notifications' => [], - ], - ], -]; diff --git a/config/lunar/payments.php b/config/lunar/payments.php deleted file mode 100644 index 2f73d3a..0000000 --- a/config/lunar/payments.php +++ /dev/null @@ -1,12 +0,0 @@ - env('PAYMENTS_TYPE', 'cash-in-hand'), - - 'types' => [ - 'cash-in-hand' => [ - 'driver' => 'offline', - 'authorized' => 'payment-offline', - ], - ], -]; diff --git a/config/lunar/pricing.php b/config/lunar/pricing.php deleted file mode 100644 index 042edd8..0000000 --- a/config/lunar/pricing.php +++ /dev/null @@ -1,5 +0,0 @@ - true, -]; diff --git a/config/lunar/search.php b/config/lunar/search.php deleted file mode 100644 index 2e6a6ef..0000000 --- a/config/lunar/search.php +++ /dev/null @@ -1,38 +0,0 @@ - [ - // These models are required by the system, do not change them. - \Lunar\Models\Collection::class, - \Lunar\Models\Product::class, - \Lunar\Models\ProductOption::class, - \Lunar\Models\Order::class, - \Lunar\Models\Customer::class, - \Lunar\Models\Brand::class, - // Below you can add your own models for indexing - ], - /* - |-------------------------------------------------------------------------- - | Search engine mapping - |-------------------------------------------------------------------------- - | - | You can define what search driver each searchable model should use. - | If the model isn't defined here, it will use the SCOUT_DRIVER env variable. - | - */ - 'engine_map' => [ - // \Lunar\Models\Product::class => 'algolia', - // \Lunar\Models\Order::class => 'meilisearch', - // \Lunar\Models\Collection::class => 'meilisearch', - ], -]; diff --git a/config/lunar/shipping.php b/config/lunar/shipping.php deleted file mode 100644 index 2c47f47..0000000 --- a/config/lunar/shipping.php +++ /dev/null @@ -1,75 +0,0 @@ - [ - 'length' => [ - 'm' => [ - 'format' => '1,0.000 m', - 'unit' => 1.00, - ], - 'mm' => [ - 'format' => '1,0.000 mm', - 'unit' => 1000, - ], - 'cm' => [ - 'format' => '1!0 cm', - 'unit' => 100, - ], - 'ft' => [ - 'format' => '1,0.00 ft.', - 'unit' => 3.28084, - ], - 'in' => [ - 'format' => '1,0.00 in.', - 'unit' => 39.3701, - ], - ], - 'area' => [ - 'sqm' => [ - 'format' => '1,00.00 sq m', - 'unit' => 1, - ], - ], - 'weight' => [ - 'kg' => [ - 'format' => '1,0.00 kg', - 'unit' => 1.00, - ], - 'g' => [ - 'format' => '1,0.00 g', - 'unit' => 1000.00, - ], - 'lbs' => [ - 'format' => '1,0.00 lbs', - 'unit' => 0.453592, - ], - ], - 'volume' => [ - 'l' => [ - 'format' => '1,00.00l', - 'unit' => 1, - ], - 'ml' => [ - 'format' => '1,00.000ml', - 'unit' => 1000, - ], - 'gal' => [ - 'format' => '1,00.000gal', - 'unit' => 0.264172, - ], - 'floz' => [ - 'format' => '1,00.000Fl oz.', - 'unit' => 33.814, - ], - ], - ], -]; diff --git a/config/lunar/stripe.php b/config/lunar/stripe.php deleted file mode 100644 index 856a444..0000000 --- a/config/lunar/stripe.php +++ /dev/null @@ -1,17 +0,0 @@ - 'manual', -]; diff --git a/config/lunar/taxes.php b/config/lunar/taxes.php deleted file mode 100644 index 45ccbc1..0000000 --- a/config/lunar/taxes.php +++ /dev/null @@ -1,14 +0,0 @@ - 'system', -]; diff --git a/config/lunar/urls.php b/config/lunar/urls.php deleted file mode 100644 index 16a0fd2..0000000 --- a/config/lunar/urls.php +++ /dev/null @@ -1,25 +0,0 @@ - true, - - /* - |-------------------------------------------------------------------------- - | URL Generator - |-------------------------------------------------------------------------- - | - | Here you can specify a class to automatically generate URLs for models which - | implement the `HasUrls` trait. If left null no generation will happen. - | You are free to use your own generator, or you can use the one that - | ships with Lunar, which by default will use the name attribute. - | - */ - 'generator' => Lunar\Generators\UrlGenerator::class, -]; diff --git a/config/mail.php b/config/mail.php index 534395a..8ec0495 100644 --- a/config/mail.php +++ b/config/mail.php @@ -28,14 +28,15 @@ | sending an e-mail. You will specify which one you are using for your | mailers below. You are free to add additional mailers as required. | - | Supported: "smtp", "sendmail", "mailgun", "ses", - | "postmark", "log", "array", "failover" + | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", + | "postmark", "log", "array", "failover", "roundrobin" | */ 'mailers' => [ 'smtp' => [ 'transport' => 'smtp', + 'url' => env('MAIL_URL'), 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), 'port' => env('MAIL_PORT', 587), 'encryption' => env('MAIL_ENCRYPTION', 'tls'), @@ -78,6 +79,14 @@ 'log', ], ], + + 'roundrobin' => [ + 'transport' => 'roundrobin', + 'mailers' => [ + 'ses', + 'postmark', + ], + ], ], /* diff --git a/config/sanctum.php b/config/sanctum.php index 9281c92..bcab893 100644 --- a/config/sanctum.php +++ b/config/sanctum.php @@ -1,5 +1,7 @@ explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( '%s%s', 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', - env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : '' + Sanctum::currentApplicationUrlWithPort() ))), /* @@ -46,6 +48,21 @@ 'expiration' => null, + /* + |-------------------------------------------------------------------------- + | Token Prefix + |-------------------------------------------------------------------------- + | + | Sanctum can prefix new tokens in order to take advantage of numerous + | security scanning initiatives maintained by open source platforms + | that notify developers if they commit tokens into repositories. + | + | See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning + | + */ + + 'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''), + /* |-------------------------------------------------------------------------- | Sanctum Middleware @@ -58,8 +75,9 @@ */ 'middleware' => [ - 'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class, + 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class, 'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class, + 'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class, ], ]; diff --git a/config/session.php b/config/session.php index 8fed97c..e738cb3 100644 --- a/config/session.php +++ b/config/session.php @@ -198,4 +198,17 @@ 'same_site' => 'lax', + /* + |-------------------------------------------------------------------------- + | Partitioned Cookies + |-------------------------------------------------------------------------- + | + | Setting this value to true will tie the cookie to the top-level site for + | a cross-site context. Partitioned cookies are accepted by the browser + | when flagged "secure" and the Same-Site attribute is set to "none". + | + */ + + 'partitioned' => false, + ]; diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 41f8ae8..584104c 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -3,6 +3,7 @@ namespace Database\Factories; use Illuminate\Database\Eloquent\Factories\Factory; +use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; /** @@ -10,28 +11,31 @@ */ class UserFactory extends Factory { + /** + * The current password being used by the factory. + */ + protected static ?string $password; + /** * Define the model's default state. * * @return array */ - public function definition() + public function definition(): array { return [ 'name' => fake()->name(), 'email' => fake()->unique()->safeEmail(), 'email_verified_at' => now(), - 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password + 'password' => static::$password ??= Hash::make('password'), 'remember_token' => Str::random(10), ]; } /** * Indicate that the model's email address should be unverified. - * - * @return static */ - public function unverified() + public function unverified(): static { return $this->state(fn (array $attributes) => [ 'email_verified_at' => null, diff --git a/database/migrations/2014_10_12_000000_create_users_table.php b/database/migrations/2014_10_12_000000_create_users_table.php index cf6b776..444fafb 100644 --- a/database/migrations/2014_10_12_000000_create_users_table.php +++ b/database/migrations/2014_10_12_000000_create_users_table.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('users', function (Blueprint $table) { $table->id(); @@ -26,10 +24,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::dropIfExists('users'); } diff --git a/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php b/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php new file mode 100644 index 0000000..81a7229 --- /dev/null +++ b/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php @@ -0,0 +1,28 @@ +string('email')->primary(); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('password_reset_tokens'); + } +}; diff --git a/database/migrations/2019_08_19_000000_create_failed_jobs_table.php b/database/migrations/2019_08_19_000000_create_failed_jobs_table.php index 1719198..249da81 100644 --- a/database/migrations/2019_08_19_000000_create_failed_jobs_table.php +++ b/database/migrations/2019_08_19_000000_create_failed_jobs_table.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('failed_jobs', function (Blueprint $table) { $table->id(); @@ -26,10 +24,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::dropIfExists('failed_jobs'); } diff --git a/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php b/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php index fd235f8..e828ad8 100644 --- a/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php +++ b/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('personal_access_tokens', function (Blueprint $table) { $table->id(); @@ -20,16 +18,15 @@ public function up() $table->string('token', 64)->unique(); $table->text('abilities')->nullable(); $table->timestamp('last_used_at')->nullable(); + $table->timestamp('expires_at')->nullable(); $table->timestamps(); }); } /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::dropIfExists('personal_access_tokens'); } diff --git a/database/migrations/2023_04_04_000000_add_expires_at_to_personal_access_tokens_table.php b/database/migrations/2023_04_04_000000_add_expires_at_to_personal_access_tokens_table.php deleted file mode 100644 index 74bd86b..0000000 --- a/database/migrations/2023_04_04_000000_add_expires_at_to_personal_access_tokens_table.php +++ /dev/null @@ -1,32 +0,0 @@ -timestamp('expires_at')->nullable()->after('last_used_at'); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::table('personal_access_tokens', function (Blueprint $table) { - $table->dropColumn('expires_at'); - }); - } -}; diff --git a/database/seeders/AbstractSeeder.php b/database/seeders/AbstractSeeder.php index da74a59..1b69cd1 100644 --- a/database/seeders/AbstractSeeder.php +++ b/database/seeders/AbstractSeeder.php @@ -3,11 +3,12 @@ namespace Database\Seeders; use Illuminate\Database\Seeder; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\File; abstract class AbstractSeeder extends Seeder { - protected function getSeedData($file) + protected function getSeedData($file): Collection { return collect(json_decode( File::get( diff --git a/database/seeders/AttributeSeeder.php b/database/seeders/AttributeSeeder.php index 49a242b..17ebeef 100644 --- a/database/seeders/AttributeSeeder.php +++ b/database/seeders/AttributeSeeder.php @@ -11,9 +11,8 @@ class AttributeSeeder extends AbstractSeeder /** * Run the database seeds. * - * @return void */ - public function run() + public function run(): void { $attributes = $this->getSeedData('attributes'); @@ -35,6 +34,9 @@ public function run() 'name' => [ 'en' => $attribute->name, ], + 'description' => [ + 'en' => $attribute->name, + ], 'configuration' => (array) $attribute->configuration, ]); } diff --git a/database/seeders/CollectionSeeder.php b/database/seeders/CollectionSeeder.php index 96c9aba..df87a5d 100644 --- a/database/seeders/CollectionSeeder.php +++ b/database/seeders/CollectionSeeder.php @@ -13,9 +13,8 @@ class CollectionSeeder extends AbstractSeeder /** * Run the database seeds. * - * @return void */ - public function run() + public function run(): void { $collections = $this->getSeedData('collections'); diff --git a/database/seeders/CustomerSeeder.php b/database/seeders/CustomerSeeder.php index 0159f68..b8c4439 100644 --- a/database/seeders/CustomerSeeder.php +++ b/database/seeders/CustomerSeeder.php @@ -13,9 +13,8 @@ class CustomerSeeder extends AbstractSeeder /** * Run the database seeds. * - * @return void */ - public function run() + public function run(): void { DB::transaction(function () { $faker = Factory::create(); diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index ec98638..766607b 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -9,15 +9,15 @@ class DatabaseSeeder extends Seeder /** * Seed the application's database. * - * @return void */ - public function run() + public function run(): void { $this->call(CollectionSeeder::class); $this->call(AttributeSeeder::class); $this->call(TaxSeeder::class); $this->call(ProductSeeder::class); $this->call(CustomerSeeder::class); + $this->call(ShippingSeeder::class); $this->call(OrderSeeder::class); } } diff --git a/database/seeders/OrderSeeder.php b/database/seeders/OrderSeeder.php index aef41bf..15d83c3 100644 --- a/database/seeders/OrderSeeder.php +++ b/database/seeders/OrderSeeder.php @@ -22,9 +22,8 @@ class OrderSeeder extends Seeder /** * Run the database seeds. * - * @return void */ - public function run() + public function run(): void { DB::transaction(function () { $variants = ProductVariant::get(); @@ -71,8 +70,8 @@ public function run() identifier: 'VAT', description: 'VAT', percentage: 20, - ) - ])) + ), + ])), ]); } @@ -96,8 +95,8 @@ public function run() identifier: 'VAT', description: 'VAT', percentage: 20, - ) - ])) + ), + ])), ]; if ($hasUser) { diff --git a/database/seeders/ProductSeeder.php b/database/seeders/ProductSeeder.php index 4d5bc64..12a1f23 100644 --- a/database/seeders/ProductSeeder.php +++ b/database/seeders/ProductSeeder.php @@ -3,10 +3,11 @@ namespace Database\Seeders; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Str; +use Lunar\Admin\Actions\Products\MapVariantsToProductOptions; use Lunar\FieldTypes\ListField; use Lunar\FieldTypes\Text; use Lunar\FieldTypes\TranslatedText; -use Lunar\Hub\Jobs\Products\GenerateVariants; use Lunar\Models\Attribute; use Lunar\Models\Brand; use Lunar\Models\Collection; @@ -19,6 +20,7 @@ use Lunar\Models\ProductType; use Lunar\Models\ProductVariant; use Lunar\Models\TaxClass; +use App\Jobs\GenerateVariants; class ProductSeeder extends AbstractSeeder { @@ -27,7 +29,7 @@ class ProductSeeder extends AbstractSeeder * * @return void */ - public function run() + public function run(): void { $products = $this->getSeedData('products'); @@ -84,18 +86,20 @@ public function run() 'stock' => 500, ]); - Price::create([ - 'customer_group_id' => null, - 'currency_id' => $currency->id, - 'priceable_type' => ProductVariant::class, - 'priceable_id' => $variant->id, - 'price' => $product->price, - 'tier' => 1, - ]); + if (!count($product->options ?? [])) { + Price::create([ + 'customer_group_id' => null, + 'currency_id' => $currency->id, + 'priceable_type' => ProductVariant::class, + 'priceable_id' => $variant->id, + 'price' => $product->price, + 'min_quantity' => 1, + ]); + } $media = $productModel->addMedia( base_path("database/seeders/data/images/{$product->image}") - )->preservingOriginal()->toMediaCollection('products'); + )->preservingOriginal()->toMediaCollection('images'); $media->setCustomProperty('primary', true); $media->save(); @@ -111,11 +115,20 @@ public function run() } $options = ProductOption::get(); + $optionValues = ProductOptionValue::get(); - $optionValueIds = []; + $optionValueMapping = collect($product->options)->mapWithKeys( + function ($option) { + return [ + $option->name => $option->values + ]; + } + )->toArray(); + + $optionIds = []; - foreach ($product->options ?? [] as $option) { + foreach ($product->options ?? [] as $optionIndex => $option) { // Do we have this option already? $optionModel = $options->first(fn ($opt) => $option->name == $opt->translate('name')); @@ -124,25 +137,82 @@ public function run() 'name' => [ 'en' => $option->name, ], + 'label' => [ + 'en' => $option->name, + ], + 'shared' => $option->shared, + 'handle' => Str::slug($option->name), ]); } + $optionIds[$optionModel->id] = [ + 'position' => $optionIndex + 1 + ]; + foreach ($option->values as $value) { $valueModel = $optionValues->first(fn ($val) => $value == $val->translate('name')); if (! $valueModel) { - $valueModel = ProductOptionValue::create([ + ProductOptionValue::create([ 'product_option_id' => $optionModel->id, + 'position' => $optionIndex, + 'name' => [ 'en' => $value, ], ]); } + } + } + + if (! count($product->options ?? [])) { + return; + } - $optionValueIds[] = $valueModel->id; + $productModel->productOptions()->sync($optionIds); + + $variants = collect([$variant])->map(function ($variant) use ($product) { + return [ + 'id' => $variant->id, + 'sku' => $variant->sku, + 'price' => $product->price, + 'values' => [], + ]; + })->toArray(); + + $variants = MapVariantsToProductOptions::map($optionValueMapping, $variants, true); + + foreach ($variants as $variant) { + if (!$variant['variant_id']) { + $variantModel = ProductVariant::create([ + 'product_id' => $productModel->id, + 'purchasable' => 'always', + 'shippable' => true, + 'backorder' => 0, + 'sku' => $variant['sku'], + 'tax_class_id' => $taxClass->id, + 'stock' => 500, + ]); + $variant['variant_id'] = $variantModel->id; + } else { + $variantModel = ProductVariant::find($variant['variant_id']); } + + Price::create([ + 'customer_group_id' => null, + 'currency_id' => $currency->id, + 'priceable_type' => ProductVariant::class, + 'priceable_id' => $variant['variant_id'], + 'price' => $variant['price'], + 'min_quantity' => 1, + ]); + + $valueIds = ProductOptionValue::get()->filter(function ($option) use ($variant) { + return in_array($option->translate('name'), $variant['values']); + })->pluck('id'); + + $variantModel->values()->sync($valueIds); } - GenerateVariants::dispatch($productModel, $optionValueIds); }); }); } diff --git a/database/seeders/ShippingSeeder.php b/database/seeders/ShippingSeeder.php new file mode 100644 index 0000000..4ee3c0f --- /dev/null +++ b/database/seeders/ShippingSeeder.php @@ -0,0 +1,160 @@ + 'Standard Shipping', + 'code' => 'STNDRD', + 'enabled' => true, + 'driver' => 'ship-by', + 'data' => [ + 'charge_by' => 'cart_total', + ] + ]); + + $ukShippingZone = ShippingZone::create([ + 'name' => 'UK', + 'type' => 'countries', + ]); + + $ukShippingRate = ShippingRate::create([ + 'shipping_zone_id' => $ukShippingZone->id, + 'shipping_method_id' => $standardShipping->id, + 'enabled' => true, + ]); + + $ukShippingZone->countries()->sync( + Country::where('iso3', '=', 'GBR')->first()->id, + ); + + Price::create([ + 'priceable_type' => ShippingRate::class, + 'priceable_id' => $ukShippingRate->id, + 'price' => 1000, + 'min_quantity' => 1, + 'currency_id' => $currency->id, + ]); + + // Free shipping on £100 or over orders + Price::create([ + 'priceable_type' => ShippingRate::class, + 'priceable_id' => $ukShippingRate->id, + 'price' => 0, + 'min_quantity' => 10000, + 'currency_id' => $currency->id, + ]); + + // US Shipping + + $usShipping = ShippingMethod::create([ + 'name' => 'US Shipping', + 'code' => 'USA', + 'enabled' => true, + 'driver' => 'ship-by', + 'data' => [ + 'charge_by' => 'cart_total', + ] + ]); + + $usShippingZone = ShippingZone::create([ + 'name' => 'America', + 'type' => 'countries', + ]); + + $usShippingRate = ShippingRate::create([ + 'shipping_zone_id' => $usShippingZone->id, + 'shipping_method_id' => $usShipping->id, + 'enabled' => true, + ]); + + $usShippingZone->countries()->sync( + Country::where('iso3', '=', 'USA')->first()->id, + ); + + Price::create([ + 'priceable_type' => ShippingRate::class, + 'priceable_id' => $usShippingRate->id, + 'price' => 5000, + 'min_quantity' => 1, + 'currency_id' => $currency->id, + ]); + + // European shipping + + $euroShipping = ShippingMethod::create([ + 'name' => 'Europe Delivery', + 'code' => 'EURO', + 'enabled' => true, + 'driver' => 'ship-by', + ]); + + $euroShippingZone = ShippingZone::create([ + 'name' => 'Europe', + 'type' => 'countries', + ]); + + $euroShippingRate = ShippingRate::create([ + 'shipping_zone_id' => $euroShippingZone->id, + 'shipping_method_id' => $euroShipping->id, + 'enabled' => true, + ]); + + $euroShippingZone->countries()->sync( + Country::whereIn('iso3', [ + 'AUT', + 'BEL', + 'BGR', + 'HRV', + 'CYP', + 'CZE', + 'DNK', + 'EST', + 'FIN', + 'FRA', + 'DEU', + 'GRC', + 'HUN', + 'IRL', + 'ITA', + 'LVA', + 'LTU', + 'LUX', + 'MLT', + 'NLD', + 'POL', + 'ROU', + 'SVK', + 'ESP', + 'SWE', + ])->pluck('id'), + ); + + Price::create([ + 'priceable_type' => ShippingRate::class, + 'priceable_id' => $euroShippingRate->id, + 'price' => 2000, + 'min_quantity' => 1, + 'currency_id' => $currency->id, + ]); + + } +} diff --git a/database/seeders/TaxSeeder.php b/database/seeders/TaxSeeder.php index a86a7f1..c6f4853 100644 --- a/database/seeders/TaxSeeder.php +++ b/database/seeders/TaxSeeder.php @@ -14,9 +14,8 @@ class TaxSeeder extends Seeder /** * Run the database seeds. * - * @return void */ - public function run() + public function run(): void { $taxClass = TaxClass::first(); diff --git a/database/seeders/data/products.json b/database/seeders/data/products.json index 552c2fa..ff6fe84 100644 --- a/database/seeders/data/products.json +++ b/database/seeders/data/products.json @@ -12,6 +12,7 @@ "options": [ { "name": "Size", + "shared": false, "values": [ "30 S (L30)", "32 S (L30)", @@ -41,6 +42,7 @@ "options": [ { "name": "Shoe Size", + "shared": true, "values": ["UK 5", "UK 6", "UK 7", "UK 8", "UK 9", "UK 10"] } ] @@ -58,6 +60,7 @@ "options": [ { "name": "Shoe Size", + "shared": true, "values": ["UK 5", "UK 6", "UK 7", "UK 8", "UK 9", "UK 10"] } ] @@ -108,6 +111,7 @@ "options": [ { "name": "Shoe Size", + "shared": true, "values": ["UK 5", "UK 6", "UK 7", "UK 8", "UK 9", "UK 10"] } ] @@ -147,6 +151,7 @@ "options": [ { "name": "Shoe Size", + "shared": true, "values": ["UK 5", "UK 6", "UK 7", "UK 8", "UK 9", "UK 10"] } ] @@ -164,6 +169,7 @@ "options": [ { "name": "Shoe Size", + "shared": true, "values": ["UK 5", "UK 6", "UK 7", "UK 8", "UK 9", "UK 10"] } ] @@ -192,10 +198,12 @@ "options": [ { "name": "Colour", + "shared": true, "values": ["Black", "Blue", "Red", "Green"] }, { "name": "Size", + "shared": true, "values": ["Small", "Medium", "Large", "X-Large"] } ] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d74de3f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,86 @@ +version: "3.8" + +services: + mysql: + container_name: demostore-mysql + image: mysql:8.0 + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} + MYSQL_USER: ${DB_USERNAME} + MYSQL_PASSWORD: ${DB_PASSWORD} + MYSQL_DATABASE: ${DB_DATABASE} + volumes: + - storage:/var/lib/mysql + + caddy: + container_name: demostore-caddy + image: caddy:latest + restart: unless-stopped + ports: + - "80:80" + - "443:443" + - "443:443/udp" + volumes: + - $PWD/docker/caddy/Caddyfile:/etc/caddy/Caddyfile + - ./:/var/www/ + depends_on: + - mysql + - meilisearch + + php-fpm: + container_name: demostore-php-fpm + build: + target: php-fpm + restart: unless-stopped + environment: + - ADMIN_FIRSTNAME=${ADMIN_FIRSTNAME} + - ADMIN_LASTNAME=${ADMIN_LASTNAME} + - ADMIN_EMAIL=${ADMIN_EMAIL} + - ADMIN_PASSWORD=${ADMIN_PASSWORD} + volumes: + - ./:/var/www/:rw,cached + depends_on: + - mysql + - meilisearch + + meilisearch: + container_name: demostore-meilisearch + image: getmeili/meilisearch:latest + restart: unless-stopped + environment: + - MEILI_MASTER_KEY=${MEILISEARCH_KEY} + - MEILI_LOG_LEVEL=ERROR + volumes: + - meilisearch:/meili_data + ports: + - "${MEILISEARCH_PORT}:${MEILISEARCH_PORT}" + depends_on: + - mysql + + meilisearch-ui: + container_name: demostore-meilisearch-ui + image: riccoxie/meilisearch-ui:latest + restart: unless-stopped + ports: + - ${MEILISEARCH_UI_PORT}:24900 + depends_on: + - meilisearch + + redis: + container_name: demostore-redis + image: redis:latest + restart: unless-stopped + ports: + - "${REDIS_PORT}:${REDIS_PORT}" + + mailhog: + container_name: demostore-mailhog + image: 'mailhog/mailhog:latest' + ports: + - '1025:1025' + - '8025:8025' + +volumes: + storage: + meilisearch: diff --git a/docker/caddy/Caddyfile b/docker/caddy/Caddyfile new file mode 100644 index 0000000..c981673 --- /dev/null +++ b/docker/caddy/Caddyfile @@ -0,0 +1,14 @@ +{ + auto_https off +} + +localhost:80 { + root * /var/www/public + + php_fastcgi php-fpm:9000 { + root /var/www/public + } + + file_server + encode zstd gzip +} diff --git a/docker/php-fpm/dev/php.ini b/docker/php-fpm/dev/php.ini new file mode 100644 index 0000000..6210efc --- /dev/null +++ b/docker/php-fpm/dev/php.ini @@ -0,0 +1,5 @@ +memory_limit=1G +post_max_size=30M +upload_max_filesize=30M +realpath_cache_size=4096K +realpath_cache_ttl=600 diff --git a/docker/php-fpm/docker-entrypoint.sh b/docker/php-fpm/docker-entrypoint.sh new file mode 100644 index 0000000..57c67b5 --- /dev/null +++ b/docker/php-fpm/docker-entrypoint.sh @@ -0,0 +1,41 @@ +#!/bin/sh +set -e + +CYAN='\x1b[36m' +MAGENTA='\x1b[35m' + +# first arg is `-f` or `--some-option` +if [ "${1#-}" != "$1" ]; then + set -- php-fpm "$@" +fi + +if [ "$1" = 'php-fpm' ] || [ "$1" = 'artisan' ]; then + + echo "Launch project Lunar!" + + until nc -z -v -w30 mysql 3306 + do + echo "Waiting for database connection..." + # wait for 5 seconds before check again + sleep 5 + done + + if [ -d "config/lunar" ]; then + echo -e "$MAGENTA Lunar already install 🚀" + else + echo "$BLUE Starting installation..." + composer install + php artisan migrate + php artisan lunar:create-admin --firstname=${ADMIN_FIRSTNAME} --lastname=${ADMIN_LASTNAME} --email=${ADMIN_EMAIL} --password=${ADMIN_PASSWORD} + php artisan lunar:install -n + php artisan db:seed + php artisan storage:link + php artisan lunar:search:index + fi + + echo -e "$CYAN Would you like to show some love by giving us a star ⭐ on GitHub?" + echo -e "$CYAN Visit : https://github.com/lunarphp/lunar" + echo -e "Your project is live ! Storefront available here: http://localhost" +fi + +exec docker-php-entrypoint "$@" diff --git a/docker/php-fpm/php.ini b/docker/php-fpm/php.ini new file mode 100644 index 0000000..c91e3bf --- /dev/null +++ b/docker/php-fpm/php.ini @@ -0,0 +1,5 @@ +memory_limit=1G +post_max_size=6M +upload_max_filesize=5M +realpath_cache_size=4096K +realpath_cache_ttl=600 \ No newline at end of file diff --git a/env.docker.example b/env.docker.example new file mode 100644 index 0000000..98756f7 --- /dev/null +++ b/env.docker.example @@ -0,0 +1,60 @@ +APP_NAME=Laravel +APP_ENV=local +APP_KEY=base64:xXSJ4LcR6e/0M6o8eK6RGhkbPT1W9UNmY6PP5UK+q0w= +APP_DEBUG=true +APP_URL=http://demo-store.test + +LOG_CHANNEL=stack +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=mysql +DB_HOST=mysql +DB_PORT=3306 +DB_DATABASE=demostore +DB_USERNAME=root +DB_PASSWORD=password + +BROADCAST_DRIVER=log +CACHE_DRIVER=file +FILESYSTEM_DISK=local +QUEUE_CONNECTION=sync +SESSION_DRIVER=file +SESSION_LIFETIME=120 + +MEMCACHED_HOST=127.0.0.1 + +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=smtp +MAIL_HOST=mailhog +MAIL_PORT=1025 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null +MAIL_FROM_ADDRESS=null +MAIL_FROM_NAME="${APP_NAME}" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +PUSHER_APP_ID= +PUSHER_APP_KEY= +PUSHER_APP_SECRET= +PUSHER_APP_CLUSTER=mt1 + +MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" +MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" + +SCOUT_DRIVER=meilisearch +SCOUT_PREFIX='demostore_' + +MEILISEARCH_KEY=jyjW99LxM28kG4 + +STRIPE_KEY= +STRIPE_SECRET= diff --git a/package.json b/package.json index beb060f..d6bc2c4 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,18 @@ { "private": true, + "type": "module", "scripts": { - "dev": "npm run development", - "development": "mix", - "watch": "mix watch", - "watch-poll": "mix watch -- --watch-options-poll=1000", - "hot": "mix watch --hot", - "prod": "npm run production", - "production": "mix --production" + "dev": "vite", + "build": "vite build" }, "devDependencies": { - "@tailwindcss/forms": "^0.5.3", - "autoprefixer": "^10.4.12", - "laravel-mix": "^6.0.6", - "postcss": "^8.4.16", - "tailwindcss": "^3.1.8" + "@ryangjchandler/alpine-clipboard": "^2.3.0", + "@tailwindcss/forms": "^0.5.7", + "autoprefixer": "^10.4.17", + "axios": "^1.6.1", + "laravel-vite-plugin": "^0.8.0", + "postcss": "^8.4.35", + "tailwindcss": "^3.4.1", + "vite": "^4.0.0" } } diff --git a/phpunit.xml b/phpunit.xml index 2b74c2f..8cac64d 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -24,6 +24,7 @@ + diff --git a/resources/views/components/collection-sale.blade.php b/resources/views/components/collection-sale.blade.php index 6724d3c..575f0a4 100644 --- a/resources/views/components/collection-sale.blade.php +++ b/resources/views/components/collection-sale.blade.php @@ -13,7 +13,9 @@ @endif + class="inline-block px-5 py-3 mt-6 text-sm font-medium text-white bg-black rounded-lg hover:ring-1 hover:ring-black" + wire:navigate + > Shop the Sale diff --git a/resources/views/components/product-card.blade.php b/resources/views/components/product-card.blade.php index b3bb9a2..7afe73c 100644 --- a/resources/views/components/product-card.blade.php +++ b/resources/views/components/product-card.blade.php @@ -1,7 +1,9 @@ @props(['product']) + href="{{ route('product.view', $product->defaultUrl->slug) }}" + wire:navigate +>
@if ($product->thumbnail) - - + id="quantity" min="1" value="1" - wire:model="quantity" /> + wire:model.live="quantity" />
+ href="{{ route('checkout.view') }}" + wire:navigate + > Checkout diff --git a/resources/views/livewire/components/checkout-address.blade.php b/resources/views/livewire/components/checkout-address.blade.php index 7d64a40..ff36034 100644 --- a/resources/views/livewire/components/checkout-address.blade.php +++ b/resources/views/livewire/components/checkout-address.blade.php @@ -1,4 +1,4 @@ -
{{ ucfirst($type) }} Details @@ -6,7 +6,7 @@ class="border rounded shadow-lg"> @endif @@ -17,13 +17,13 @@ class="border rounded shadow-lg"> - - +
@@ -31,7 +31,7 @@ class="border rounded shadow-lg"> - @@ -39,12 +39,12 @@ class="border rounded shadow-lg">
- + -
@@ -55,18 +55,18 @@ class="border rounded shadow-lg"> - - + - + @@ -74,19 +74,19 @@ class="border rounded shadow-lg"> - - + - @@ -95,7 +95,7 @@ class="border rounded shadow-lg">
{{ $option->getPrice()->formatted() }} - {{ $option->getDescription() }} + {{ $option->name }}
@endforeach diff --git a/resources/views/livewire/home.blade.php b/resources/views/livewire/home.blade.php index 326daea..c99b2a7 100644 --- a/resources/views/livewire/home.blade.php +++ b/resources/views/livewire/home.blade.php @@ -1,21 +1,23 @@ - +
+ -
- @if ($this->saleCollection) - - @endif +
+ @if ($this->saleCollection) + + @endif - @if ($this->randomCollection) -
-

- {{ $this->randomCollection->translateAttribute('name') }} -

+ @if ($this->randomCollection) +
+

+ {{ $this->randomCollection->translateAttribute('name') }} +

-
- @foreach ($this->randomCollection->products as $product) - - @endforeach -
-
- @endif +
+ @foreach ($this->randomCollection->products as $product) + + @endforeach +
+
+ @endif +
diff --git a/resources/views/livewire/product-page.blade.php b/resources/views/livewire/product-page.blade.php index a2ce52a..d9e0443 100644 --- a/resources/views/livewire/product-page.blade.php +++ b/resources/views/livewire/product-page.blade.php @@ -51,7 +51,7 @@ class="object-cover rounded-xl"

@@ -10,7 +10,7 @@ class="bg-white border border-gray-100 rounded-xl"> + wire:model.live="shippingIsBilling" /> Same as billing @@ -35,7 +35,7 @@ class="bg-white border border-gray-100 rounded-xl"> label="First name" :errors="$errors->get($type . '.first_name')" required> - @@ -43,27 +43,27 @@ class="bg-white border border-gray-100 rounded-xl"> label="Last name" :errors="$errors->get($type . '.last_name')" required> - - + - + - @@ -76,41 +76,41 @@ class="bg-white border border-gray-100 rounded-xl"> label="Address line 1" :errors="$errors->get($type . '.line_one')" required> - - + - + - - + - @@ -118,7 +118,7 @@ class="bg-white border border-gray-100 rounded-xl"> label="Country" required> @@ -30,7 +30,7 @@ class="bg-white border border-gray-100 rounded-xl">