├── public
├── favicon.ico
├── js
│ └── filament
│ │ ├── forms
│ │ ├── forms.js
│ │ └── components
│ │ │ ├── textarea.js
│ │ │ ├── tags-input.js
│ │ │ └── key-value.js
│ │ └── tables
│ │ └── tables.js
├── robots.txt
├── mix-manifest.json
├── vendor
│ └── horizon
│ │ ├── img
│ │ └── favicon.png
│ │ └── mix-manifest.json
├── .htaccess
├── web.config
├── index.php
└── css
│ └── filament
│ └── support
│ └── support.css
├── app
├── Livewire
│ ├── .gitkeep
│ ├── Notifications.php
│ └── Form.php
├── Filament
│ ├── App
│ │ └── Pages
│ │ │ ├── Settings.php
│ │ │ └── RegisterTeam.php
│ ├── Resources
│ │ ├── Blog
│ │ │ ├── PostResource
│ │ │ │ └── Pages
│ │ │ │ │ ├── CreatePost.php
│ │ │ │ │ ├── ListPosts.php
│ │ │ │ │ ├── ViewPost.php
│ │ │ │ │ └── EditPost.php
│ │ │ ├── LinkResource
│ │ │ │ └── Pages
│ │ │ │ │ ├── CreateLink.php
│ │ │ │ │ ├── ViewLink.php
│ │ │ │ │ ├── ListLinks.php
│ │ │ │ │ └── EditLink.php
│ │ │ ├── AuthorResource
│ │ │ │ └── Pages
│ │ │ │ │ └── ManageAuthors.php
│ │ │ └── CategoryResource
│ │ │ │ └── Pages
│ │ │ │ └── ManageCategories.php
│ │ └── Shop
│ │ │ ├── CustomerResource
│ │ │ ├── Pages
│ │ │ │ ├── CreateCustomer.php
│ │ │ │ ├── ListCustomers.php
│ │ │ │ └── EditCustomer.php
│ │ │ └── RelationManagers
│ │ │ │ └── AddressesRelationManager.php
│ │ │ └── OrderResource
│ │ │ ├── Pages
│ │ │ ├── EditOrder.php
│ │ │ ├── ListOrders.php
│ │ │ └── CreateOrder.php
│ │ │ └── Widgets
│ │ │ └── OrderStats.php
│ ├── Clusters
│ │ ├── Products
│ │ │ └── Resources
│ │ │ │ ├── BrandResource
│ │ │ │ ├── Pages
│ │ │ │ │ ├── CreateBrand.php
│ │ │ │ │ ├── EditBrand.php
│ │ │ │ │ └── ListBrands.php
│ │ │ │ └── RelationManagers
│ │ │ │ │ ├── ProductsRelationManager.php
│ │ │ │ │ └── AddressesRelationManager.php
│ │ │ │ ├── ProductResource
│ │ │ │ ├── Pages
│ │ │ │ │ ├── CreateProduct.php
│ │ │ │ │ ├── EditProduct.php
│ │ │ │ │ └── ListProducts.php
│ │ │ │ └── Widgets
│ │ │ │ │ └── ProductStats.php
│ │ │ │ └── CategoryResource
│ │ │ │ ├── Pages
│ │ │ │ ├── CreateCategory.php
│ │ │ │ ├── EditCategory.php
│ │ │ │ └── ListCategories.php
│ │ │ │ └── RelationManagers
│ │ │ │ └── ProductsRelationManager.php
│ │ └── Products.php
│ ├── Pages
│ │ ├── Auth
│ │ │ └── Login.php
│ │ └── Dashboard.php
│ ├── Widgets
│ │ ├── OrdersChart.php
│ │ ├── CustomersChart.php
│ │ ├── LatestOrders.php
│ │ └── StatsOverviewWidget.php
│ ├── Exports
│ │ ├── Shop
│ │ │ └── BrandExporter.php
│ │ └── Blog
│ │ │ └── AuthorExporter.php
│ └── Imports
│ │ ├── Blog
│ │ └── CategoryImporter.php
│ │ └── Shop
│ │ └── CategoryImporter.php
├── Models
│ ├── Shop
│ │ ├── OrderItem.php
│ │ ├── OrderAddress.php
│ │ ├── Payment.php
│ │ ├── Brand.php
│ │ ├── Customer.php
│ │ ├── Category.php
│ │ ├── Product.php
│ │ └── Order.php
│ ├── Team.php
│ ├── Blog
│ │ ├── Link.php
│ │ ├── Author.php
│ │ ├── Category.php
│ │ └── Post.php
│ ├── Address.php
│ ├── Comment.php
│ └── User.php
├── Http
│ ├── Middleware
│ │ ├── EncryptCookies.php
│ │ ├── VerifyCsrfToken.php
│ │ ├── PreventRequestsDuringMaintenance.php
│ │ ├── TrustHosts.php
│ │ ├── TrimStrings.php
│ │ ├── Authenticate.php
│ │ ├── TrustProxies.php
│ │ └── RedirectIfAuthenticated.php
│ ├── Controllers
│ │ └── Controller.php
│ ├── Resources
│ │ └── Team.php
│ └── Kernel.php
├── Providers
│ ├── BroadcastServiceProvider.php
│ ├── AuthServiceProvider.php
│ ├── AppServiceProvider.php
│ ├── EventServiceProvider.php
│ ├── HorizonServiceProvider.php
│ ├── RouteServiceProvider.php
│ └── Filament
│ │ ├── AppPanelProvider.php
│ │ └── AdminPanelProvider.php
├── Console
│ └── Kernel.php
├── Exceptions
│ └── Handler.php
└── Enums
│ └── OrderStatus.php
├── resources
├── js
│ └── app.js
├── css
│ └── app.css
├── views
│ ├── filament
│ │ └── app
│ │ │ └── pages
│ │ │ └── settings.blade.php
│ ├── livewire
│ │ ├── notifications.blade.php
│ │ └── form.blade.php
│ ├── maintenance.blade.php
│ └── components
│ │ └── layouts
│ │ └── app.blade.php
├── svg
│ ├── twitter.svg
│ └── github.svg
└── lang
│ └── en
│ ├── pagination.php
│ ├── auth.php
│ └── passwords.php
├── database
├── .gitignore
├── seeders
│ ├── local_images
│ │ ├── 1280x720
│ │ │ ├── 05e68fd7-d7ac-46a3-837e-20937f8dc899.jpg
│ │ │ ├── 3188fb8e-15a9-4afb-9182-1aebdcf11b83.jpg
│ │ │ ├── 50f608f8-2549-4ecf-b6f6-a5997c13cb63.jpg
│ │ │ ├── 5b22e6fe-5958-4a9c-99f1-33bf59085469.jpg
│ │ │ ├── 609dd36a-1011-4185-b736-63971a6fb854.jpg
│ │ │ ├── 708c814b-0bbf-40f9-af54-f15673585d99.jpg
│ │ │ ├── 80043582-b25d-4403-8166-03baddfb96bc.jpg
│ │ │ ├── 80654c36-50f4-4144-91e9-58fc49b83f90.jpg
│ │ │ ├── 9144a5b3-efc6-42de-8b2d-fb77ea0656c6.jpg
│ │ │ ├── 99b95dcc-4a4f-4523-922a-b6f776d09faa.jpg
│ │ │ ├── 9b3227b0-584c-48f4-962f-b42318bade61.jpg
│ │ │ ├── 9cc59994-a348-4a55-8885-b6d478e2d2c7.jpg
│ │ │ ├── 9eda78f4-ac0c-4af9-b6a8-14f8727294dd.jpg
│ │ │ ├── b5d43aee-bf02-4331-8916-b10523ef861f.jpg
│ │ │ ├── cb6ca3f6-3643-4bc1-a0de-c113db3a92e0.jpg
│ │ │ ├── ccd5cbc5-afa7-4709-b572-9f72c6716846.jpg
│ │ │ ├── d970b8da-95e6-456e-9ddb-7cb1aad2de56.jpg
│ │ │ ├── dc5d4460-1637-4ae5-ab05-6732b0e9d3b8.jpg
│ │ │ ├── dd133d03-b444-4a19-a728-2eaab2a547ee.jpg
│ │ │ ├── e837e969-cbfe-4995-aaea-ee32cf4ba4d0.jpg
│ │ │ ├── f4e246e7-8bee-4011-8c8d-debff845d198.jpg
│ │ │ ├── f9697b3c-72df-4917-8317-6c3bdc530225.jpg
│ │ │ └── ffe9e024-8a9c-4bbb-ae13-e99a620ae2f7.jpg
│ │ └── 200x200
│ │ │ ├── 1aa69e68-a504-4495-bc38-5c520d035d8b.jpg
│ │ │ ├── 20d2e61e-3273-4521-9c47-e56a68c892fb.jpg
│ │ │ ├── 248eadf6-5227-42f3-ad31-1c320febdb7b.jpg
│ │ │ ├── 2f5c09b5-6268-47b2-8e0b-c4890850e727.jpg
│ │ │ ├── 3b95f9cc-e3e3-48d3-b743-443210fa19ec.jpg
│ │ │ ├── 3b9f504b-d276-4d4a-9997-68979f1bddcc.jpg
│ │ │ ├── 447a4c77-e7a2-49a3-8015-2dc5ba055c24.jpg
│ │ │ ├── 482659c5-764a-4071-848a-fa1fbd786fd5.jpg
│ │ │ ├── 503a4fac-68d7-4b64-ad44-5c2ec3986b94.jpg
│ │ │ ├── 57623f03-f7c2-4179-a366-f1530993bc09.jpg
│ │ │ ├── 680520bc-e880-4704-8dc7-a425d55b7a81.jpg
│ │ │ ├── 74d81728-0dc2-43e2-a7da-332e2e2e6c89.jpg
│ │ │ ├── 7e4b0b96-99df-4d1e-84fa-dad2b398f172.jpg
│ │ │ ├── 873353ff-3d3a-4bfc-a02d-22949e6e4558.jpg
│ │ │ ├── 948bfac1-6d9b-430f-86fc-8d2d6ef680ed.jpg
│ │ │ ├── b02337ca-473b-4ae1-957b-0c7854161f8e.jpg
│ │ │ ├── b14d2dca-4d45-4cc0-8d8b-864e10081dac.jpg
│ │ │ ├── b37987d1-a9ad-4671-9066-9812a033fdf4.jpg
│ │ │ ├── cf755312-0709-4f40-be4a-bba7edbcdabb.jpg
│ │ │ ├── dce10027-9b5d-44a5-91ed-7caeb23100bd.jpg
│ │ │ ├── e5d18094-036b-4c48-8232-8e0d9971cca0.jpg
│ │ │ ├── edc07898-49a5-4eb0-8af8-2a9ceeb3baf5.jpg
│ │ │ ├── f3f0ce20-b7a3-47db-b907-c77c5e6ca038.jpg
│ │ │ └── f8c8557d-bd70-4cf7-978c-89f35e918c8f.jpg
│ └── LocalImages.php
├── factories
│ ├── Shop
│ │ ├── OrderItemFactory.php
│ │ ├── OrderAddressFactory.php
│ │ ├── CategoryFactory.php
│ │ ├── BrandFactory.php
│ │ ├── CustomerFactory.php
│ │ ├── PaymentFactory.php
│ │ ├── OrderFactory.php
│ │ └── ProductFactory.php
│ ├── AddressFactory.php
│ ├── Concerns
│ │ └── CanCreateImages.php
│ ├── CommentFactory.php
│ ├── Blog
│ │ ├── CategoryFactory.php
│ │ ├── AuthorFactory.php
│ │ ├── PostFactory.php
│ │ └── LinkFactory.php
│ └── UserFactory.php
└── migrations
│ ├── 2022_06_01_232533_alter_order_items_add_sort_column.php
│ ├── 2023_11_29_144721_create_failed_import_rows_table.php
│ ├── 2021_12_13_072624_create_settings_table.php
│ ├── 2022_10_27_140431_create_teams_table.php
│ ├── 2014_10_12_100000_create_password_resets_table.php
│ ├── 2021_12_13_164524_create_shop_category_product_table.php
│ ├── 2022_06_09_092322_create_payments_table.php
│ ├── 2023_12_17_112735_create_blog_links_table.php
│ ├── 2022_09_10_131605_create_notifications_table.php
│ ├── 2014_10_12_000000_create_users_table.php
│ ├── 2023_11_29_144720_create_imports_table.php
│ ├── 2022_06_09_091930_create_comments_table.php
│ ├── 2019_08_19_000000_create_failed_jobs_table.php
│ ├── 2019_12_14_000001_create_personal_access_tokens_table.php
│ ├── 2021_12_13_182904_create_shop_order_items_table.php
│ ├── 2021_12_13_073001_create_blog_authors_table.php
│ ├── 2021_12_13_184540_create_addresses_table.php
│ ├── 2021_12_13_072541_create_tag_tables.php
│ ├── 2021_12_13_073006_create_blog_categories_table.php
│ ├── 2021_12_13_153307_create_shop_customers_table.php
│ ├── 2023_11_29_144716_create_job_batches_table.php
│ ├── 2024_01_01_105157_create_exports_table.php
│ ├── 2021_12_13_164316_create_shop_brands_table.php
│ ├── 2021_12_13_155621_create_shop_categories_table.php
│ ├── 2021_12_13_073022_create_blog_posts_table.php
│ ├── 2021_12_13_055514_create_media_table.php
│ ├── 2021_12_13_165855_create_shop_orders_table.php
│ ├── 2022_06_09_155042_create_addressable_table.php
│ └── 2021_12_13_164519_create_shop_products_table.php
├── bootstrap
├── cache
│ └── .gitignore
└── app.php
├── storage
├── logs
│ └── .gitignore
├── debugbar
│ └── .gitignore
├── app
│ └── .gitignore
└── framework
│ ├── testing
│ └── .gitignore
│ ├── views
│ └── .gitignore
│ ├── cache
│ ├── data
│ │ └── .gitignore
│ └── .gitignore
│ ├── sessions
│ └── .gitignore
│ └── .gitignore
├── screenshot.png
├── routes
├── web.php
├── channels.php
├── api.php
└── console.php
├── .gitattributes
├── postcss.config.js
├── phpstan.neon
├── tests
├── TestCase.php
├── Unit
│ └── ExampleTest.php
├── Feature
│ └── ExampleTest.php
└── CreatesApplication.php
├── .styleci.yml
├── .gitignore
├── tailwind.config.js
├── .editorconfig
├── pint.json
├── server.php
├── vite.config.js
├── package.json
├── .gitpod.yml
├── .github
└── workflows
│ └── pint.yml
├── config
├── cors.php
├── services.php
├── view.php
├── filament.php
├── hashing.php
├── broadcasting.php
├── sanctum.php
└── filesystems.php
├── .env.example
├── LICENSE.md
├── phpunit.xml
├── artisan
├── README.md
└── composer.json
/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/Livewire/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/resources/js/app.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/database/.gitignore:
--------------------------------------------------------------------------------
1 | *.sqlite*
2 |
--------------------------------------------------------------------------------
/bootstrap/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/logs/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/public/js/filament/forms/forms.js:
--------------------------------------------------------------------------------
1 | (()=>{})();
2 |
--------------------------------------------------------------------------------
/public/js/filament/tables/tables.js:
--------------------------------------------------------------------------------
1 | (()=>{})();
2 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow:
3 |
--------------------------------------------------------------------------------
/storage/debugbar/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/app/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !public/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/storage/framework/testing/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/views/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/cache/data/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/sessions/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/storage/framework/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !data/
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leandrocfe/filament-demo/HEAD/screenshot.png
--------------------------------------------------------------------------------
/resources/css/app.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/resources/views/filament/app/pages/settings.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/public/mix-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "/js/app.js": "/js/app.js",
3 | "/css/app.css": "/css/app.css"
4 | }
5 |
--------------------------------------------------------------------------------
/resources/views/livewire/notifications.blade.php:
--------------------------------------------------------------------------------
1 |
2 | {{-- Do your work, then step back. --}}
3 |
4 |
--------------------------------------------------------------------------------
/routes/web.php:
--------------------------------------------------------------------------------
1 |
2 | {{ $this->form }}
3 |
4 | {{ json_encode($this->data) }}
5 |
6 |
7 | Submit
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /public/hot
3 | /public/build
4 | /public/storage
5 | /storage/*.key
6 | /vendor
7 | .env
8 | .env.backup
9 | .phpunit.result.cache
10 | docker-compose.override.yml
11 | Homestead.json
12 | Homestead.yaml
13 | npm-debug.log
14 | yarn-error.log
15 | /.idea
16 | /.vscode
17 |
--------------------------------------------------------------------------------
/app/Livewire/Notifications.php:
--------------------------------------------------------------------------------
1 | assertTrue(true);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/Filament/Resources/Blog/PostResource/Pages/CreatePost.php:
--------------------------------------------------------------------------------
1 | get('/login');
17 |
18 | $response->assertStatus(200);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/Filament/Clusters/Products/Resources/ProductResource/Pages/CreateProduct.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | protected $except = [
15 | //
16 | ];
17 | }
18 |
--------------------------------------------------------------------------------
/app/Filament/Clusters/Products/Resources/CategoryResource/Pages/CreateCategory.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | protected $except = [
15 | //
16 | ];
17 | }
18 |
--------------------------------------------------------------------------------
/app/Filament/Clusters/Products.php:
--------------------------------------------------------------------------------
1 | form->fill([
14 | 'email' => 'admin@filamentphp.com',
15 | 'password' => 'password',
16 | 'remember' => true,
17 | ]);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/Http/Controllers/Controller.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | protected $except = [
15 | //
16 | ];
17 | }
18 |
--------------------------------------------------------------------------------
/app/Http/Middleware/TrustHosts.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | public function hosts()
15 | {
16 | return [
17 | $this->allSubdomainsOfApplicationUrl(),
18 | ];
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/Http/Middleware/TrimStrings.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | protected $except = [
15 | 'current_password',
16 | 'password',
17 | 'password_confirmation',
18 | ];
19 | }
20 |
--------------------------------------------------------------------------------
/public/vendor/horizon/mix-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "/app.js": "/app.js?id=4999da9248177ed487693daec2a7d3fe",
3 | "/app-dark.css": "/app-dark.css?id=dcaca44a9f0f1d019e3cd3d76c3cb8fd",
4 | "/app.css": "/app.css?id=14e3bcd1f1b1cf88e63e945529c4d0ce",
5 | "/img/favicon.png": "/img/favicon.png?id=1542bfe8a0010dcbee710da13cce367f",
6 | "/img/horizon.svg": "/img/horizon.svg?id=904d5b5185fefb09035384e15bfca765",
7 | "/img/sprite.svg": "/img/sprite.svg?id=afc4952b74895bdef3ab4ebe9adb746f"
8 | }
9 |
--------------------------------------------------------------------------------
/app/Models/Blog/Link.php:
--------------------------------------------------------------------------------
1 | make(Kernel::class)->bootstrap();
19 |
20 | return $app;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/Providers/BroadcastServiceProvider.php:
--------------------------------------------------------------------------------
1 | */
16 | public function addressable(): MorphTo
17 | {
18 | return $this->morphTo();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/Filament/Resources/Blog/PostResource/Pages/ListPosts.php:
--------------------------------------------------------------------------------
1 | |\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
14 | */
15 | public function toArray($request)
16 | {
17 | return parent::toArray($request);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/Filament/Resources/Shop/CustomerResource/Pages/ListCustomers.php:
--------------------------------------------------------------------------------
1 | */
18 | public function order(): BelongsTo
19 | {
20 | return $this->belongsTo(Order::class);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/database/seeders/LocalImages.php:
--------------------------------------------------------------------------------
1 | random();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/Http/Middleware/Authenticate.php:
--------------------------------------------------------------------------------
1 | */
19 | public function posts(): HasMany
20 | {
21 | return $this->hasMany(Post::class, 'blog_author_id');
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/Filament/Clusters/Products/Resources/BrandResource/Pages/EditBrand.php:
--------------------------------------------------------------------------------
1 | schema([
20 | TextInput::make('name')->required(),
21 | ]);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/database/factories/Shop/OrderItemFactory.php:
--------------------------------------------------------------------------------
1 | $this->faker->numberBetween(1, 10),
19 | 'unit_price' => $this->faker->randomFloat(2, 100, 500),
20 | ];
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/Filament/Clusters/Products/Resources/ProductResource/Pages/EditProduct.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/server.php:
--------------------------------------------------------------------------------
1 |
7 | */
8 | $uri = urldecode(
9 | parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)
10 | );
11 |
12 | // This file allows us to emulate Apache's "mod_rewrite" functionality from the
13 | // built-in PHP web server. This provides a convenient way to test a Laravel
14 | // application without having installed a "real" web server software here.
15 | if ($uri !== '/' && file_exists(__DIR__ . '/public' . $uri)) {
16 | return false;
17 | }
18 |
19 | require_once __DIR__ . '/public/index.php';
20 |
--------------------------------------------------------------------------------
/app/Filament/Resources/Blog/LinkResource/Pages/ViewLink.php:
--------------------------------------------------------------------------------
1 | '« Previous',
17 | 'next' => 'Next »',
18 |
19 | ];
20 |
--------------------------------------------------------------------------------
/routes/channels.php:
--------------------------------------------------------------------------------
1 | id === (int) $id;
18 | });
19 |
--------------------------------------------------------------------------------
/app/Filament/Resources/Blog/LinkResource/Pages/ListLinks.php:
--------------------------------------------------------------------------------
1 | strtolower($this->faker->countryCode()),
16 | 'street' => $this->faker->streetAddress(),
17 | 'state' => $this->faker->state(),
18 | 'city' => $this->faker->city(),
19 | 'zip' => $this->faker->postcode(),
20 | ];
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/routes/api.php:
--------------------------------------------------------------------------------
1 | get('/user', function (Request $request) {
18 | return $request->user();
19 | });
20 |
--------------------------------------------------------------------------------
/database/factories/Concerns/CanCreateImages.php:
--------------------------------------------------------------------------------
1 | getPathname());
16 |
17 | $filename = Str::uuid() . '.jpg';
18 |
19 | Storage::disk('public')->put($filename, $image);
20 |
21 | return $filename;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import laravel, { refreshPaths } from 'laravel-vite-plugin'
3 |
4 | export default defineConfig({
5 | plugins: [
6 | laravel.default({
7 | input: ['resources/css/app.css', 'resources/js/app.js'],
8 | refresh: [
9 | ...refreshPaths,
10 | 'app/Filament/**',
11 | 'app/Forms/Components/**',
12 | 'app/Livewire/**',
13 | 'app/Infolists/Components/**',
14 | 'app/Providers/Filament/**',
15 | 'app/Tables/Columns/**',
16 | ],
17 | }),
18 | ],
19 | })
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "filament-demo",
3 | "private": true,
4 | "type": "module",
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "vite build"
8 | },
9 | "devDependencies": {
10 | "@alpinejs/focus": "^3.10.3",
11 | "@tailwindcss/forms": "^0.5.3",
12 | "@tailwindcss/typography": "^0.5.9",
13 | "alpinejs": "^3.10.3",
14 | "autoprefixer": "^10.4.14",
15 | "laravel-vite-plugin": "^0.5.0",
16 | "postcss": "^8.4.28",
17 | "postcss-nesting": "^12.0.1",
18 | "tailwindcss": "^3.3.3",
19 | "tippy.js": "^6.3.7",
20 | "vite": "^3.0.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/public/js/filament/forms/components/textarea.js:
--------------------------------------------------------------------------------
1 | function r({initialHeight:t,shouldAutosize:i,state:s}){return{state:s,wrapperEl:null,init:function(){this.wrapperEl=this.$el.parentNode,this.setInitialHeight(),i?this.$watch("state",()=>{this.resize()}):this.setUpResizeObserver()},setInitialHeight:function(){this.$el.scrollHeight<=0||(this.wrapperEl.style.height=t+"rem")},resize:function(){if(this.setInitialHeight(),this.$el.scrollHeight<=0)return;let e=this.$el.scrollHeight+"px";this.wrapperEl.style.height!==e&&(this.wrapperEl.style.height=e)},setUpResizeObserver:function(){new ResizeObserver(()=>{this.wrapperEl.style.height=this.$el.style.height}).observe(this.$el)}}}export{r as default};
2 |
--------------------------------------------------------------------------------
/routes/console.php:
--------------------------------------------------------------------------------
1 | comment(Inspiring::quote());
19 | })->purpose('Display an inspiring quote');
20 |
--------------------------------------------------------------------------------
/app/Filament/Resources/Blog/LinkResource/Pages/EditLink.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | protected $policies = [
15 | // 'App\Models\Model' => 'App\Policies\ModelPolicy',
16 | ];
17 |
18 | /**
19 | * Register any authentication / authorization services.
20 | *
21 | * @return void
22 | */
23 | public function boot()
24 | {
25 | //
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/database/factories/Shop/OrderAddressFactory.php:
--------------------------------------------------------------------------------
1 | strtolower($this->faker->countryCode()),
16 | 'street' => $this->faker->streetAddress(),
17 | 'state' => $this->faker->state(),
18 | 'city' => $this->faker->city(),
19 | 'zip' => $this->faker->postcode(),
20 | ];
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/database/migrations/2022_06_01_232533_alter_order_items_add_sort_column.php:
--------------------------------------------------------------------------------
1 | unsignedInteger('sort')->default(0)->after('id');
13 | });
14 | }
15 |
16 | public function down()
17 | {
18 | Schema::table('shop_order_items', function (Blueprint $table) {
19 | $table->dropColumn('sort');
20 | });
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/public/.htaccess:
--------------------------------------------------------------------------------
1 |
2 |
3 | Options -MultiViews -Indexes
4 |
5 |
6 | RewriteEngine On
7 |
8 | # Handle Authorization Header
9 | RewriteCond %{HTTP:Authorization} .
10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
11 |
12 | # Redirect Trailing Slashes If Not A Folder...
13 | RewriteCond %{REQUEST_FILENAME} !-d
14 | RewriteCond %{REQUEST_URI} (.+)/$
15 | RewriteRule ^ %1 [L,R=301]
16 |
17 | # Send Requests To Front Controller...
18 | RewriteCond %{REQUEST_FILENAME} !-d
19 | RewriteCond %{REQUEST_FILENAME} !-f
20 | RewriteRule ^ index.php [L]
21 |
22 |
--------------------------------------------------------------------------------
/app/Filament/Resources/Blog/AuthorResource/Pages/ManageAuthors.php:
--------------------------------------------------------------------------------
1 | exporter(AuthorExporter::class),
19 | Actions\CreateAction::make(),
20 | ];
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/Filament/Resources/Blog/PostResource/Pages/ViewPost.php:
--------------------------------------------------------------------------------
1 | getRecord();
18 |
19 | return $record->title;
20 | }
21 |
22 | protected function getActions(): array
23 | {
24 | return [];
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/Filament/Resources/Blog/CategoryResource/Pages/ManageCategories.php:
--------------------------------------------------------------------------------
1 | importer(CategoryImporter::class),
19 | Actions\CreateAction::make(),
20 | ];
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/Console/Kernel.php:
--------------------------------------------------------------------------------
1 | load(__DIR__ . '/Commands');
28 |
29 | require base_path('routes/console.php');
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/Filament/Clusters/Products/Resources/BrandResource/Pages/ListBrands.php:
--------------------------------------------------------------------------------
1 | exporter(BrandExporter::class),
19 | Actions\CreateAction::make(),
20 | ];
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/Models/Blog/Category.php:
--------------------------------------------------------------------------------
1 |
20 | */
21 | protected $casts = [
22 | 'is_visible' => 'boolean',
23 | ];
24 |
25 | /** @return HasMany */
26 | public function posts(): HasMany
27 | {
28 | return $this->hasMany(Post::class, 'blog_category_id');
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/database/migrations/2023_11_29_144721_create_failed_import_rows_table.php:
--------------------------------------------------------------------------------
1 | id();
16 | $table->json('data');
17 | $table->foreignId('import_id')->constrained()->cascadeOnDelete();
18 | $table->text('validation_error')->nullable();
19 | $table->timestamps();
20 | });
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/app/Filament/Clusters/Products/Resources/CategoryResource/Pages/ListCategories.php:
--------------------------------------------------------------------------------
1 | importer(CategoryImporter::class),
19 | Actions\CreateAction::make(),
20 | ];
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/database/factories/CommentFactory.php:
--------------------------------------------------------------------------------
1 | $this->faker->catchPhrase(),
19 | 'content' => $this->faker->text(),
20 | 'is_visible' => $this->faker->boolean(),
21 | 'created_at' => $this->faker->dateTimeBetween('-1 year', '-6 month'),
22 | 'updated_at' => $this->faker->dateTimeBetween('-5 month', 'now'),
23 | ];
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/database/migrations/2021_12_13_072624_create_settings_table.php:
--------------------------------------------------------------------------------
1 | id();
13 |
14 | $table->string('group')->index();
15 | $table->string('name');
16 | $table->boolean('locked');
17 | $table->json('payload');
18 |
19 | $table->timestamps();
20 | });
21 | }
22 |
23 | public function down()
24 | {
25 | Schema::dropIfExists('settings');
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/app/Providers/AppServiceProvider.php:
--------------------------------------------------------------------------------
1 | environment('production')) {
31 | URL::forceScheme('https');
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/resources/lang/en/auth.php:
--------------------------------------------------------------------------------
1 | 'These credentials do not match our records.',
17 | 'password' => 'The provided password is incorrect.',
18 | 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
19 |
20 | ];
21 |
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 | tasks:
2 | - name: Setup environment and launch
3 | init: |
4 | cp .env.example .env
5 | sed -i "s#APP_URL=http://127.0.0.1:8000#APP_URL=$(gp url 8000)#g" .env
6 | composer install --ignore-platform-reqs
7 | php artisan key:generate
8 | php artisan storage:link
9 | touch database/database.sqlite
10 | php artisan migrate:fresh --seed
11 | php artisan serve
12 |
13 | # Configure vscode
14 | vscode:
15 | extensions:
16 | - bmewburn.vscode-intelephense-client
17 | - ms-azuretools.vscode-docker
18 | - ecmel.vscode-html-css
19 | - MehediDracula.php-namespace-resolver
20 | - Equinusocio.vsc-community-material-theme
21 | - EditorConfig.EditorConfig
22 | - streetsidesoftware.code-spell-checker
23 |
--------------------------------------------------------------------------------
/app/Models/Address.php:
--------------------------------------------------------------------------------
1 | */
18 | public function customers(): MorphToMany
19 | {
20 | return $this->morphedByMany(Customer::class, 'addressable');
21 | }
22 |
23 | /** @return MorphToMany */
24 | public function brands(): MorphToMany
25 | {
26 | return $this->morphedByMany(Brand::class, 'addressable');
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/database/migrations/2022_10_27_140431_create_teams_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->string('name');
19 | $table->timestamps();
20 | });
21 | }
22 |
23 | /**
24 | * Reverse the migrations.
25 | *
26 | * @return void
27 | */
28 | public function down()
29 | {
30 | Schema::dropIfExists('teams');
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/app/Filament/Resources/Blog/PostResource/Pages/EditPost.php:
--------------------------------------------------------------------------------
1 | getRecord();
19 |
20 | return $record->title;
21 | }
22 |
23 | protected function getActions(): array
24 | {
25 | return [
26 | Actions\DeleteAction::make(),
27 | ];
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/Http/Middleware/TrustProxies.php:
--------------------------------------------------------------------------------
1 | |string|null
14 | */
15 | protected $proxies = '*';
16 |
17 | /**
18 | * The headers that should be used to detect proxies.
19 | *
20 | * @var int
21 | */
22 | protected $headers =
23 | Request::HEADER_X_FORWARDED_FOR |
24 | Request::HEADER_X_FORWARDED_HOST |
25 | Request::HEADER_X_FORWARDED_PORT |
26 | Request::HEADER_X_FORWARDED_PROTO |
27 | Request::HEADER_X_FORWARDED_AWS_ELB;
28 | }
29 |
--------------------------------------------------------------------------------
/.github/workflows/pint.yml:
--------------------------------------------------------------------------------
1 | name: Check & fix styling
2 |
3 | on: [push]
4 |
5 | jobs:
6 | pint:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - name: Checkout code
10 | uses: actions/checkout@v2
11 | with:
12 | ref: ${{ github.head_ref }}
13 | - name: Setup PHP
14 | uses: shivammathur/setup-php@v2
15 | with:
16 | php-version: '8.2'
17 | - name: Install dependencies
18 | run: composer install --no-interaction
19 | - name: Run Pint
20 | run: ./vendor/bin/pint
21 | - name: Commit changes
22 | uses: stefanzweifel/git-auto-commit-action@v4
23 | with:
24 | commit_message: Fix styling
25 |
--------------------------------------------------------------------------------
/app/Filament/Clusters/Products/Resources/ProductResource/Pages/ListProducts.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/Providers/EventServiceProvider.php:
--------------------------------------------------------------------------------
1 | >
15 | */
16 | protected $listen = [
17 | Registered::class => [
18 | SendEmailVerificationNotification::class,
19 | ],
20 | ];
21 |
22 | /**
23 | * Register any events for your application.
24 | *
25 | * @return void
26 | */
27 | public function boot()
28 | {
29 | //
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/resources/lang/en/passwords.php:
--------------------------------------------------------------------------------
1 | 'Your password has been reset!',
17 | 'sent' => 'We have emailed your password reset link!',
18 | 'throttled' => 'Please wait before retrying.',
19 | 'token' => 'This password reset token is invalid.',
20 | 'user' => "We can't find a user with that email address.",
21 |
22 | ];
23 |
--------------------------------------------------------------------------------
/database/migrations/2014_10_12_100000_create_password_resets_table.php:
--------------------------------------------------------------------------------
1 | string('email')->index();
18 | $table->string('token');
19 | $table->timestamp('created_at')->nullable();
20 | });
21 | }
22 |
23 | /**
24 | * Reverse the migrations.
25 | *
26 | * @return void
27 | */
28 | public function down()
29 | {
30 | Schema::dropIfExists('password_resets');
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/public/js/filament/forms/components/tags-input.js:
--------------------------------------------------------------------------------
1 | function i({state:a,splitKeys:n}){return{newTag:"",state:a,createTag:function(){if(this.newTag=this.newTag.trim(),this.newTag!==""){if(this.state.includes(this.newTag)){this.newTag="";return}this.state.push(this.newTag),this.newTag=""}},deleteTag:function(t){this.state=this.state.filter(e=>e!==t)},reorderTags:function(t){let e=this.state.splice(t.oldIndex,1)[0];this.state.splice(t.newIndex,0,e),this.state=[...this.state]},input:{["x-on:blur"]:"createTag()",["x-model"]:"newTag",["x-on:keydown"](t){["Enter",...n].includes(t.key)&&(t.preventDefault(),t.stopPropagation(),this.createTag())},["x-on:paste"](){this.$nextTick(()=>{if(n.length===0){this.createTag();return}let t=n.map(e=>e.replace(/[/\-\\^$*+?.()|[\]{}]/g,"\\$&")).join("|");this.newTag.split(new RegExp(t,"g")).forEach(e=>{this.newTag=e,this.createTag()})})}}}}export{i as default};
2 |
--------------------------------------------------------------------------------
/resources/views/components/layouts/app.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | {{ config('app.name') }}
11 |
12 |
17 |
18 | @filamentStyles
19 | @vite('resources/css/app.css')
20 |
21 |
22 |
23 | {{ $slot }}
24 |
25 | @filamentScripts
26 | @vite('resources/js/app.js')
27 |
28 |
29 |
--------------------------------------------------------------------------------
/database/factories/Blog/CategoryFactory.php:
--------------------------------------------------------------------------------
1 | $name = $this->faker->unique()->words(3, true),
20 | 'slug' => Str::slug($name),
21 | 'description' => $this->faker->realText(),
22 | 'is_visible' => $this->faker->boolean(),
23 | 'created_at' => $this->faker->dateTimeBetween('-1 year', '-6 month'),
24 | 'updated_at' => $this->faker->dateTimeBetween('-5 month', 'now'),
25 | ];
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/database/factories/Shop/CategoryFactory.php:
--------------------------------------------------------------------------------
1 | $name = $this->faker->unique()->words(3, true),
20 | 'slug' => Str::slug($name),
21 | 'description' => $this->faker->realText(),
22 | 'is_visible' => $this->faker->boolean(),
23 | 'created_at' => $this->faker->dateTimeBetween('-1 year', '-6 month'),
24 | 'updated_at' => $this->faker->dateTimeBetween('-5 month', 'now'),
25 | ];
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/Models/Comment.php:
--------------------------------------------------------------------------------
1 | 'boolean',
21 | ];
22 |
23 | /** @return BelongsTo */
24 | public function customer(): BelongsTo
25 | {
26 | return $this->belongsTo(Customer::class);
27 | }
28 |
29 | /** @return MorphTo */
30 | public function commentable(): MorphTo
31 | {
32 | return $this->morphTo();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/database/factories/UserFactory.php:
--------------------------------------------------------------------------------
1 | $this->faker->name(),
14 | 'email' => $this->faker->unique()->safeEmail(),
15 | 'email_verified_at' => now(),
16 | 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
17 | 'remember_token' => Str::random(10),
18 | ];
19 | }
20 |
21 | public function unverified(): Factory
22 | {
23 | return $this->state(function (array $attributes) {
24 | return [
25 | 'email_verified_at' => null,
26 | ];
27 | });
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/database/factories/Blog/AuthorFactory.php:
--------------------------------------------------------------------------------
1 | $this->faker->name(),
19 | 'email' => $this->faker->unique()->safeEmail(),
20 | 'bio' => $this->faker->realTextBetween(),
21 | 'github_handle' => $this->faker->userName(),
22 | 'twitter_handle' => $this->faker->userName(),
23 | 'created_at' => $this->faker->dateTimeBetween('-1 year', '-6 month'),
24 | 'updated_at' => $this->faker->dateTimeBetween('-5 month', 'now'),
25 | ];
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/Filament/Widgets/OrdersChart.php:
--------------------------------------------------------------------------------
1 | [
22 | [
23 | 'label' => 'Orders',
24 | 'data' => [2433, 3454, 4566, 3300, 5545, 5765, 6787, 8767, 7565, 8576, 9686, 8996],
25 | 'fill' => 'start',
26 | ],
27 | ],
28 | 'labels' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
29 | ];
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/database/migrations/2021_12_13_164524_create_shop_category_product_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->foreignId('shop_category_id');
19 | $table->foreignId('shop_product_id');
20 | $table->timestamps();
21 | });
22 | }
23 |
24 | /**
25 | * Reverse the migrations.
26 | *
27 | * @return void
28 | */
29 | public function down()
30 | {
31 | Schema::dropIfExists('shop_category_product');
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/app/Filament/Widgets/CustomersChart.php:
--------------------------------------------------------------------------------
1 | [
22 | [
23 | 'label' => 'Customers',
24 | 'data' => [4344, 5676, 6798, 7890, 8987, 9388, 10343, 10524, 13664, 14345, 15753, 17332],
25 | 'fill' => 'start',
26 | ],
27 | ],
28 | 'labels' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
29 | ];
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/database/migrations/2022_06_09_092322_create_payments_table.php:
--------------------------------------------------------------------------------
1 | id();
14 |
15 | $table->foreignIdFor(Order::class);
16 |
17 | $table->string('reference');
18 |
19 | $table->string('provider');
20 |
21 | $table->string('method');
22 |
23 | $table->decimal('amount');
24 |
25 | $table->string('currency');
26 |
27 | $table->timestamps();
28 | });
29 | }
30 |
31 | public function down()
32 | {
33 | Schema::dropIfExists('shop_payments');
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/database/migrations/2023_12_17_112735_create_blog_links_table.php:
--------------------------------------------------------------------------------
1 | id();
16 | $table->string('url');
17 | $table->json('title');
18 | $table->json('description');
19 | $table->string('color');
20 | $table->string('image')->nullable();
21 | $table->timestamps();
22 | });
23 | }
24 |
25 | /**
26 | * Reverse the migrations.
27 | */
28 | public function down(): void
29 | {
30 | Schema::dropIfExists('blog_links');
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/database/factories/Shop/BrandFactory.php:
--------------------------------------------------------------------------------
1 | $name = $this->faker->unique()->company(),
20 | 'slug' => Str::slug($name),
21 | 'website' => 'https://www.' . $this->faker->domainName(),
22 | 'description' => $this->faker->realText(),
23 | 'is_visible' => $this->faker->boolean(),
24 | 'created_at' => $this->faker->dateTimeBetween('-1 year', '-6 month'),
25 | 'updated_at' => $this->faker->dateTimeBetween('-5 month', 'now'),
26 | ];
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/database/factories/Shop/CustomerFactory.php:
--------------------------------------------------------------------------------
1 | $this->faker->name(),
19 | 'email' => $this->faker->unique()->safeEmail(),
20 | 'phone' => $this->faker->phoneNumber(),
21 | 'birthday' => $this->faker->dateTimeBetween('-35 years', '-18 years'),
22 | 'gender' => $this->faker->randomElement(['male', 'female']),
23 | 'created_at' => $this->faker->dateTimeBetween('-1 year', '-6 month'),
24 | 'updated_at' => $this->faker->dateTimeBetween('-5 month', 'now'),
25 | ];
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/database/migrations/2022_09_10_131605_create_notifications_table.php:
--------------------------------------------------------------------------------
1 | uuid('id')->primary();
18 | $table->string('type');
19 | $table->morphs('notifiable');
20 | $table->text('data');
21 | $table->timestamp('read_at')->nullable();
22 | $table->timestamps();
23 | });
24 | }
25 |
26 | /**
27 | * Reverse the migrations.
28 | *
29 | * @return void
30 | */
31 | public function down()
32 | {
33 | Schema::dropIfExists('notifications');
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/app/Http/Middleware/RedirectIfAuthenticated.php:
--------------------------------------------------------------------------------
1 | check()) {
25 | return redirect(RouteServiceProvider::HOME);
26 | }
27 | }
28 |
29 | return $next($request);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/config/cors.php:
--------------------------------------------------------------------------------
1 | ['api/*', 'sanctum/csrf-cookie'],
19 |
20 | 'allowed_methods' => ['*'],
21 |
22 | 'allowed_origins' => ['*'],
23 |
24 | 'allowed_origins_patterns' => [],
25 |
26 | 'allowed_headers' => ['*'],
27 |
28 | 'exposed_headers' => [],
29 |
30 | 'max_age' => 0,
31 |
32 | 'supports_credentials' => false,
33 |
34 | ];
35 |
--------------------------------------------------------------------------------
/database/migrations/2014_10_12_000000_create_users_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->string('name');
19 | $table->string('email')->unique();
20 | $table->timestamp('email_verified_at')->nullable();
21 | $table->string('password');
22 | $table->rememberToken();
23 | $table->timestamps();
24 | });
25 | }
26 |
27 | /**
28 | * Reverse the migrations.
29 | *
30 | * @return void
31 | */
32 | public function down()
33 | {
34 | Schema::dropIfExists('users');
35 | }
36 | };
37 |
--------------------------------------------------------------------------------
/database/migrations/2023_11_29_144720_create_imports_table.php:
--------------------------------------------------------------------------------
1 | id();
16 | $table->timestamp('completed_at')->nullable();
17 | $table->string('file_name');
18 | $table->string('file_path');
19 | $table->string('importer');
20 | $table->unsignedInteger('processed_rows')->default(0);
21 | $table->unsignedInteger('total_rows');
22 | $table->unsignedInteger('successful_rows')->default(0);
23 | $table->foreignId('user_id')->constrained()->cascadeOnDelete();
24 | $table->timestamps();
25 | });
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/database/migrations/2022_06_09_091930_create_comments_table.php:
--------------------------------------------------------------------------------
1 | id();
16 |
17 | $table->foreignIdFor(Customer::class)->nullable()->constrained('shop_customers')->cascadeOnDelete();
18 | $table->morphs('commentable');
19 | $table->text('title')->nullable();
20 | $table->text('content')->nullable();
21 | $table->boolean('is_visible')->default(false);
22 |
23 | $table->timestamps();
24 | });
25 | }
26 |
27 | public function down()
28 | {
29 | Schema::dropIfExists('comments');
30 | }
31 | };
32 |
--------------------------------------------------------------------------------
/app/Providers/HorizonServiceProvider.php:
--------------------------------------------------------------------------------
1 | >
14 | */
15 | protected $dontReport = [
16 | //
17 | ];
18 |
19 | /**
20 | * A list of the inputs that are never flashed for validation exceptions.
21 | *
22 | * @var array
23 | */
24 | protected $dontFlash = [
25 | 'current_password',
26 | 'password',
27 | 'password_confirmation',
28 | ];
29 |
30 | /**
31 | * Register the exception handling callbacks for the application.
32 | *
33 | * @return void
34 | */
35 | public function register()
36 | {
37 | $this->reportable(function (Throwable $e) {
38 | //
39 | });
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/database/factories/Shop/PaymentFactory.php:
--------------------------------------------------------------------------------
1 | 'PAY' . $this->faker->unique()->randomNumber(6),
17 | 'currency' => $this->faker->randomElement(collect(Currency::getCurrencies())->keys()),
18 | 'amount' => $this->faker->randomFloat(2, 100, 2000),
19 | 'provider' => $this->faker->randomElement(['stripe', 'paypal']),
20 | 'method' => $this->faker->randomElement(['credit_card', 'bank_transfer', 'paypal']),
21 | 'created_at' => $this->faker->dateTimeBetween('-1 year', '-6 month'),
22 | 'updated_at' => $this->faker->dateTimeBetween('-5 month', 'now'),
23 | ];
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/database/migrations/2019_08_19_000000_create_failed_jobs_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->string('uuid')->unique();
19 | $table->text('connection');
20 | $table->text('queue');
21 | $table->longText('payload');
22 | $table->longText('exception');
23 | $table->timestamp('failed_at')->useCurrent();
24 | });
25 | }
26 |
27 | /**
28 | * Reverse the migrations.
29 | *
30 | * @return void
31 | */
32 | public function down()
33 | {
34 | Schema::dropIfExists('failed_jobs');
35 | }
36 | };
37 |
--------------------------------------------------------------------------------
/database/factories/Blog/PostFactory.php:
--------------------------------------------------------------------------------
1 | $title = $this->faker->unique()->sentence(4),
23 | 'slug' => Str::slug($title),
24 | 'content' => $this->faker->realText(),
25 | 'image' => $this->createImage(),
26 | 'published_at' => $this->faker->dateTimeBetween('-6 month', '+1 month'),
27 | 'created_at' => $this->faker->dateTimeBetween('-1 year', '-6 month'),
28 | 'updated_at' => $this->faker->dateTimeBetween('-5 month', 'now'),
29 | ];
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/public/js/filament/forms/components/key-value.js:
--------------------------------------------------------------------------------
1 | function r({state:o}){return{state:o,rows:[],shouldUpdateRows:!0,init:function(){this.updateRows(),this.rows.length<=0?this.rows.push({key:"",value:""}):this.updateState(),this.$watch("state",(t,e)=>{let s=i=>i===null?0:Array.isArray(i)?i.length:typeof i!="object"?0:Object.keys(i).length;s(t)===0&&s(e)===0||this.updateRows()})},addRow:function(){this.rows.push({key:"",value:""}),this.updateState()},deleteRow:function(t){this.rows.splice(t,1),this.rows.length<=0&&this.addRow(),this.updateState()},reorderRows:function(t){let e=Alpine.raw(this.rows);this.rows=[];let s=e.splice(t.oldIndex,1)[0];e.splice(t.newIndex,0,s),this.$nextTick(()=>{this.rows=e,this.updateState()})},updateRows:function(){if(!this.shouldUpdateRows){this.shouldUpdateRows=!0;return}let t=[];for(let[e,s]of Object.entries(this.state??{}))t.push({key:e,value:s});this.rows=t},updateState:function(){let t={};this.rows.forEach(e=>{e.key===""||e.key===null||(t[e.key]=e.value)}),this.shouldUpdateRows=!1,this.state=t}}}export{r as default};
2 |
--------------------------------------------------------------------------------
/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->morphs('tokenable');
19 | $table->string('name');
20 | $table->string('token', 64)->unique();
21 | $table->text('abilities')->nullable();
22 | $table->timestamp('last_used_at')->nullable();
23 | $table->timestamps();
24 | });
25 | }
26 |
27 | /**
28 | * Reverse the migrations.
29 | *
30 | * @return void
31 | */
32 | public function down()
33 | {
34 | Schema::dropIfExists('personal_access_tokens');
35 | }
36 | };
37 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | APP_NAME="Filament Demo"
2 | APP_ENV=local
3 | APP_KEY=
4 | APP_DEBUG=true
5 | APP_URL=http://127.0.0.1:8000
6 |
7 | LOG_CHANNEL=stack
8 | LOG_DEPRECATIONS_CHANNEL=null
9 | LOG_LEVEL=debug
10 |
11 | DB_CONNECTION=sqlite
12 |
13 | BROADCAST_DRIVER=log
14 | CACHE_DRIVER=file
15 | FILESYSTEM_DRIVER=local
16 | QUEUE_CONNECTION=sync
17 | SESSION_DRIVER=file
18 | SESSION_LIFETIME=120
19 |
20 | MEMCACHED_HOST=127.0.0.1
21 |
22 | REDIS_HOST=127.0.0.1
23 | REDIS_PASSWORD=null
24 | REDIS_PORT=6379
25 |
26 | MAIL_MAILER=smtp
27 | MAIL_HOST=mailhog
28 | MAIL_PORT=1025
29 | MAIL_USERNAME=null
30 | MAIL_PASSWORD=null
31 | MAIL_ENCRYPTION=null
32 | MAIL_FROM_ADDRESS=null
33 | MAIL_FROM_NAME="${APP_NAME}"
34 |
35 | AWS_ACCESS_KEY_ID=
36 | AWS_SECRET_ACCESS_KEY=
37 | AWS_DEFAULT_REGION=us-east-1
38 | AWS_BUCKET=
39 | AWS_USE_PATH_STYLE_ENDPOINT=false
40 |
41 | FLARE_KEY=
42 |
43 | PUSHER_APP_ID=
44 | PUSHER_APP_KEY=
45 | PUSHER_APP_SECRET=
46 | PUSHER_APP_CLUSTER=mt1
47 |
48 | VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
49 | VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
50 |
--------------------------------------------------------------------------------
/database/migrations/2021_12_13_182904_create_shop_order_items_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->foreignId('shop_order_id')->nullable()->constrained()->cascadeOnDelete();
19 | $table->foreignId('shop_product_id')->nullable()->constrained()->cascadeOnDelete();
20 | $table->integer('qty');
21 | $table->decimal('unit_price', 10, 2);
22 | $table->timestamps();
23 | });
24 | }
25 |
26 | /**
27 | * Reverse the migrations.
28 | *
29 | * @return void
30 | */
31 | public function down()
32 | {
33 | Schema::dropIfExists('shop_order_items');
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/app/Filament/Clusters/Products/Resources/ProductResource/Widgets/ProductStats.php:
--------------------------------------------------------------------------------
1 | getPageTableQuery()->count()),
25 | Stat::make('Product Inventory', $this->getPageTableQuery()->sum('qty')),
26 | Stat::make('Average price', number_format($this->getPageTableQuery()->avg('price'), 2)),
27 | ];
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/database/migrations/2021_12_13_073001_create_blog_authors_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->string('name');
19 | $table->string('email')->unique();
20 | $table->string('photo')->nullable();
21 | $table->longText('bio')->nullable();
22 | $table->string('github_handle')->nullable();
23 | $table->string('twitter_handle')->nullable();
24 | $table->timestamps();
25 | });
26 | }
27 |
28 | /**
29 | * Reverse the migrations.
30 | *
31 | * @return void
32 | */
33 | public function down()
34 | {
35 | Schema::dropIfExists('blog_authors');
36 | }
37 | };
38 |
--------------------------------------------------------------------------------
/config/services.php:
--------------------------------------------------------------------------------
1 | [
18 | 'domain' => env('MAILGUN_DOMAIN'),
19 | 'secret' => env('MAILGUN_SECRET'),
20 | 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
21 | ],
22 |
23 | 'postmark' => [
24 | 'token' => env('POSTMARK_TOKEN'),
25 | ],
26 |
27 | 'ses' => [
28 | 'key' => env('AWS_ACCESS_KEY_ID'),
29 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
30 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
31 | ],
32 |
33 | ];
34 |
--------------------------------------------------------------------------------
/database/migrations/2021_12_13_184540_create_addresses_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->morphs('addressable');
19 | $table->string('country')->nullable();
20 | $table->string('street')->nullable();
21 | $table->string('city')->nullable();
22 | $table->string('state')->nullable();
23 | $table->string('zip')->nullable();
24 | $table->timestamps();
25 | });
26 | }
27 |
28 | /**
29 | * Reverse the migrations.
30 | *
31 | * @return void
32 | */
33 | public function down()
34 | {
35 | Schema::dropIfExists('shop_order_addresses');
36 | }
37 | };
38 |
--------------------------------------------------------------------------------
/database/migrations/2021_12_13_072541_create_tag_tables.php:
--------------------------------------------------------------------------------
1 | id();
13 |
14 | $table->json('name');
15 | $table->json('slug');
16 | $table->string('type')->nullable();
17 | $table->integer('order_column')->nullable();
18 |
19 | $table->timestamps();
20 | });
21 |
22 | Schema::create('taggables', function (Blueprint $table) {
23 | $table->foreignId('tag_id')->constrained()->cascadeOnDelete();
24 |
25 | $table->morphs('taggable');
26 |
27 | $table->unique(['tag_id', 'taggable_id', 'taggable_type']);
28 | });
29 | }
30 |
31 | public function down()
32 | {
33 | Schema::dropIfExists('taggables');
34 | Schema::dropIfExists('tags');
35 | }
36 | };
37 |
--------------------------------------------------------------------------------
/database/migrations/2021_12_13_073006_create_blog_categories_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->string('name');
19 | $table->string('slug')->unique();
20 | $table->longText('description')->nullable();
21 | $table->boolean('is_visible')->default(false);
22 | $table->string('seo_title', 60)->nullable();
23 | $table->string('seo_description', 160)->nullable();
24 | $table->timestamps();
25 | });
26 | }
27 |
28 | /**
29 | * Reverse the migrations.
30 | *
31 | * @return void
32 | */
33 | public function down()
34 | {
35 | Schema::dropIfExists('blog_categories');
36 | }
37 | };
38 |
--------------------------------------------------------------------------------
/database/migrations/2021_12_13_153307_create_shop_customers_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->string('name');
19 | $table->string('email')->unique();
20 | $table->string('photo')->nullable();
21 | $table->enum('gender', ['male', 'female']);
22 | $table->string('phone')->nullable();
23 | $table->date('birthday')->nullable();
24 | $table->timestamps();
25 | $table->softDeletes();
26 | });
27 | }
28 |
29 | /**
30 | * Reverse the migrations.
31 | *
32 | * @return void
33 | */
34 | public function down()
35 | {
36 | Schema::dropIfExists('shop_customers');
37 | }
38 | };
39 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # MIT License
2 |
3 | Copyright (c) Filament
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/database/migrations/2023_11_29_144716_create_job_batches_table.php:
--------------------------------------------------------------------------------
1 | string('id')->primary();
16 | $table->string('name');
17 | $table->integer('total_jobs');
18 | $table->integer('pending_jobs');
19 | $table->integer('failed_jobs');
20 | $table->longText('failed_job_ids');
21 | $table->mediumText('options')->nullable();
22 | $table->integer('cancelled_at')->nullable();
23 | $table->integer('created_at');
24 | $table->integer('finished_at')->nullable();
25 | });
26 | }
27 |
28 | /**
29 | * Reverse the migrations.
30 | */
31 | public function down(): void
32 | {
33 | Schema::dropIfExists('job_batches');
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/app/Models/Shop/Brand.php:
--------------------------------------------------------------------------------
1 |
25 | */
26 | protected $casts = [
27 | 'is_visible' => 'boolean',
28 | ];
29 |
30 | /** @return MorphToMany */
31 | public function addresses(): MorphToMany
32 | {
33 | return $this->morphToMany(Address::class, 'addressable', 'addressables');
34 | }
35 |
36 | /** @return HasMany */
37 | public function products(): HasMany
38 | {
39 | return $this->hasMany(Product::class, 'shop_brand_id');
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/Filament/Pages/Dashboard.php:
--------------------------------------------------------------------------------
1 | schema([
20 | Section::make()
21 | ->schema([
22 | Select::make('businessCustomersOnly')
23 | ->boolean(),
24 | DatePicker::make('startDate')
25 | ->maxDate(fn (Get $get) => $get('endDate') ?: now()),
26 | DatePicker::make('endDate')
27 | ->minDate(fn (Get $get) => $get('startDate') ?: now())
28 | ->maxDate(now()),
29 | ])
30 | ->columns(3),
31 | ]);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/database/migrations/2024_01_01_105157_create_exports_table.php:
--------------------------------------------------------------------------------
1 | id();
16 | $table->timestamp('completed_at')->nullable();
17 | $table->string('file_disk');
18 | $table->string('file_name')->nullable();
19 | $table->string('exporter');
20 | $table->unsignedInteger('processed_rows')->default(0);
21 | $table->unsignedInteger('total_rows');
22 | $table->unsignedInteger('successful_rows')->default(0);
23 | $table->foreignId('user_id')->constrained()->cascadeOnDelete();
24 | $table->timestamps();
25 | });
26 | }
27 |
28 | /**
29 | * Reverse the migrations.
30 | */
31 | public function down(): void
32 | {
33 | Schema::dropIfExists('exports');
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/app/Filament/Clusters/Products/Resources/BrandResource/RelationManagers/ProductsRelationManager.php:
--------------------------------------------------------------------------------
1 | headerActions([
26 | Tables\Actions\CreateAction::make(),
27 | ])
28 | ->actions([
29 | Tables\Actions\DeleteAction::make(),
30 | ])
31 | ->groupedBulkActions([
32 | Tables\Actions\DeleteBulkAction::make(),
33 | ]);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/Filament/Clusters/Products/Resources/CategoryResource/RelationManagers/ProductsRelationManager.php:
--------------------------------------------------------------------------------
1 | headerActions([
26 | Tables\Actions\CreateAction::make(),
27 | ])
28 | ->actions([
29 | Tables\Actions\DeleteAction::make(),
30 | ])
31 | ->groupedBulkActions([
32 | Tables\Actions\DeleteBulkAction::make(),
33 | ]);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/config/view.php:
--------------------------------------------------------------------------------
1 | [
17 | resource_path('views'),
18 | ],
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Compiled View Path
23 | |--------------------------------------------------------------------------
24 | |
25 | | This option determines where all the compiled Blade templates will be
26 | | stored for your application. Typically, this is within the storage
27 | | directory. However, as usual, you are free to change this value.
28 | |
29 | */
30 |
31 | 'compiled' => env(
32 | 'VIEW_COMPILED_PATH',
33 | realpath(storage_path('framework/views'))
34 | ),
35 |
36 | ];
37 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | tests/Unit
10 |
11 |
12 | tests/Feature
13 |
14 |
15 |
16 |
17 | app
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/Models/Blog/Post.php:
--------------------------------------------------------------------------------
1 |
24 | */
25 | protected $casts = [
26 | 'published_at' => 'date',
27 | ];
28 |
29 | /** @return BelongsTo */
30 | public function author(): BelongsTo
31 | {
32 | return $this->belongsTo(Author::class, 'blog_author_id');
33 | }
34 |
35 | /** @return BelongsTo */
36 | public function category(): BelongsTo
37 | {
38 | return $this->belongsTo(Category::class, 'blog_category_id');
39 | }
40 |
41 | /** @return MorphMany */
42 | public function comments(): MorphMany
43 | {
44 | return $this->morphMany(Comment::class, 'commentable');
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/database/migrations/2021_12_13_164316_create_shop_brands_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->string('name');
19 | $table->string('slug')->unique();
20 | $table->string('website')->nullable();
21 | $table->longText('description')->nullable();
22 | $table->unsignedSmallInteger('position')->default(0);
23 | $table->boolean('is_visible')->default(false);
24 | $table->string('seo_title', 60)->nullable();
25 | $table->string('seo_description', 160)->nullable();
26 | $table->integer('sort')->nullable();
27 | $table->timestamps();
28 | });
29 | }
30 |
31 | /**
32 | * Reverse the migrations.
33 | *
34 | * @return void
35 | */
36 | public function down()
37 | {
38 | Schema::dropIfExists('shop_brands');
39 | }
40 | };
41 |
--------------------------------------------------------------------------------
/public/web.config:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/database/migrations/2021_12_13_155621_create_shop_categories_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->foreignId('parent_id')->nullable()->constrained('shop_categories')->cascadeOnDelete();
19 | $table->string('name');
20 | $table->string('slug')->unique();
21 | $table->longText('description')->nullable();
22 | $table->unsignedSmallInteger('position')->default(0);
23 | $table->boolean('is_visible')->default(false);
24 | $table->string('seo_title', 60)->nullable();
25 | $table->string('seo_description', 160)->nullable();
26 | $table->timestamps();
27 | });
28 | }
29 |
30 | /**
31 | * Reverse the migrations.
32 | *
33 | * @return void
34 | */
35 | public function down()
36 | {
37 | Schema::dropIfExists('shop_categories');
38 | }
39 | };
40 |
--------------------------------------------------------------------------------
/database/migrations/2021_12_13_073022_create_blog_posts_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->foreignId('blog_author_id')->nullable()->cascadeOnDelete();
19 | $table->foreignId('blog_category_id')->nullable()->nullOnDelete();
20 | $table->string('title');
21 | $table->string('slug')->unique();
22 | $table->longText('content');
23 | $table->date('published_at')->nullable();
24 | $table->string('seo_title', 60)->nullable();
25 | $table->string('seo_description', 160)->nullable();
26 | $table->string('image')->nullable();
27 | $table->timestamps();
28 | });
29 | }
30 |
31 | /**
32 | * Reverse the migrations.
33 | *
34 | * @return void
35 | */
36 | public function down()
37 | {
38 | Schema::dropIfExists('blog_posts');
39 | }
40 | };
41 |
--------------------------------------------------------------------------------
/config/filament.php:
--------------------------------------------------------------------------------
1 | [
18 |
19 | // 'echo' => [
20 | // 'broadcaster' => 'pusher',
21 | // 'key' => env('VITE_PUSHER_APP_KEY'),
22 | // 'cluster' => env('VITE_PUSHER_APP_CLUSTER'),
23 | // 'forceTLS' => true,
24 | // ],
25 |
26 | ],
27 |
28 | /*
29 | |--------------------------------------------------------------------------
30 | | Default Filesystem Disk
31 | |--------------------------------------------------------------------------
32 | |
33 | | This is the storage disk Filament will use to put media. You may use any
34 | | of the disks defined in the `config/filesystems.php`.
35 | |
36 | */
37 |
38 | 'default_filesystem_disk' => env('FILAMENT_FILESYSTEM_DISK', 'public'),
39 |
40 | ];
41 |
--------------------------------------------------------------------------------
/database/migrations/2021_12_13_055514_create_media_table.php:
--------------------------------------------------------------------------------
1 | bigIncrements('id');
13 |
14 | $table->morphs('model');
15 | $table->uuid('uuid')->nullable()->unique();
16 | $table->string('collection_name');
17 | $table->string('name');
18 | $table->string('file_name');
19 | $table->string('mime_type')->nullable();
20 | $table->string('disk');
21 | $table->string('conversions_disk')->nullable();
22 | $table->unsignedBigInteger('size');
23 | $table->json('manipulations');
24 | $table->json('custom_properties');
25 | $table->json('generated_conversions');
26 | $table->json('responsive_images');
27 | $table->unsignedInteger('order_column')->nullable();
28 |
29 | $table->nullableTimestamps();
30 | });
31 | }
32 |
33 | public function down()
34 | {
35 | Schema::dropIfExists('media');
36 | }
37 | };
38 |
--------------------------------------------------------------------------------
/app/Filament/Exports/Shop/BrandExporter.php:
--------------------------------------------------------------------------------
1 | label('ID'),
19 | ExportColumn::make('name'),
20 | ExportColumn::make('slug'),
21 | ExportColumn::make('website'),
22 | ExportColumn::make('created_at'),
23 | ExportColumn::make('updated_at'),
24 | ];
25 | }
26 |
27 | public static function getCompletedNotificationBody(Export $export): string
28 | {
29 | $body = 'Your brand export has completed and ' . number_format($export->successful_rows) . ' ' . str('row')->plural($export->successful_rows) . ' exported.';
30 |
31 | if ($failedRowsCount = $export->getFailedRowsCount()) {
32 | $body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to export.';
33 | }
34 |
35 | return $body;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/database/factories/Blog/LinkFactory.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | class LinkFactory extends Factory
14 | {
15 | use CanCreateImages;
16 |
17 | /**
18 | * Define the model's default state.
19 | *
20 | * @return array
21 | */
22 | public function definition(): array
23 | {
24 | return [
25 | 'url' => $this->faker->url(),
26 | 'title' => [
27 | 'en' => Str::title($this->faker->words(asText: true)),
28 | 'es' => Str::title($this->faker->words(asText: true)),
29 | 'nl' => Str::title($this->faker->words(asText: true)),
30 | ],
31 | 'description' => [
32 | 'en' => $this->faker->sentence(),
33 | 'es' => $this->faker->sentence(),
34 | 'nl' => $this->faker->sentence(),
35 | ],
36 | 'color' => $this->faker->hexColor(),
37 | 'image' => $this->createImage(LocalImages::SIZE_1280x720),
38 | ];
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/database/migrations/2021_12_13_165855_create_shop_orders_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->foreignId('shop_customer_id')->nullable()->constrained()->nullOnDelete();
19 | $table->string('number', 32)->unique();
20 | $table->decimal('total_price', 12, 2)->nullable();
21 | $table->enum('status', ['new', 'processing', 'shipped', 'delivered', 'cancelled'])->default('new');
22 | $table->string('currency');
23 | $table->decimal('shipping_price')->nullable();
24 | $table->string('shipping_method')->nullable();
25 | $table->text('notes')->nullable();
26 | $table->timestamps();
27 | $table->softDeletes();
28 | });
29 | }
30 |
31 | /**
32 | * Reverse the migrations.
33 | *
34 | * @return void
35 | */
36 | public function down()
37 | {
38 | Schema::dropIfExists('shop_orders');
39 | }
40 | };
41 |
--------------------------------------------------------------------------------
/app/Models/Shop/Customer.php:
--------------------------------------------------------------------------------
1 |
26 | */
27 | protected $casts = [
28 | 'birthday' => 'date',
29 | ];
30 |
31 | /** @return MorphToMany */
32 | public function addresses(): MorphToMany
33 | {
34 | return $this->morphToMany(Address::class, 'addressable');
35 | }
36 |
37 | /** @return HasMany */
38 | public function comments(): HasMany
39 | {
40 | return $this->hasMany(Comment::class);
41 | }
42 |
43 | /** @return HasManyThrough */
44 | public function payments(): HasManyThrough
45 | {
46 | return $this->hasManyThrough(Payment::class, Order::class, 'shop_customer_id');
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/Models/User.php:
--------------------------------------------------------------------------------
1 |
24 | */
25 | protected $hidden = [
26 | 'password',
27 | 'remember_token',
28 | ];
29 |
30 | /**
31 | * @var array
32 | */
33 | protected $casts = [
34 | 'email_verified_at' => 'datetime',
35 | ];
36 |
37 | public function canAccessPanel(Panel $panel): bool
38 | {
39 | return true;
40 | }
41 |
42 | public function canAccessTenant(Model $tenant): bool
43 | {
44 | return true;
45 | }
46 |
47 | /** @return Collection */
48 | public function getTenants(Panel $panel): Collection
49 | {
50 | return Team::all();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/database/factories/Shop/OrderFactory.php:
--------------------------------------------------------------------------------
1 | 'OR' . $this->faker->unique()->randomNumber(6),
19 | 'currency' => strtolower($this->faker->currencyCode()),
20 | 'total_price' => $this->faker->randomFloat(2, 100, 2000),
21 | 'status' => $this->faker->randomElement(['new', 'processing', 'shipped', 'delivered', 'cancelled']),
22 | 'shipping_price' => $this->faker->randomFloat(2, 100, 500),
23 | 'shipping_method' => $this->faker->randomElement(['free', 'flat', 'flat_rate', 'flat_rate_per_item']),
24 | 'notes' => $this->faker->realText(100),
25 | 'created_at' => $this->faker->dateTimeBetween('-1 year', 'now'),
26 | 'updated_at' => $this->faker->dateTimeBetween('-5 month', 'now'),
27 | ];
28 | }
29 |
30 | public function configure(): Factory
31 | {
32 | return $this->afterCreating(function (Order $order) {
33 | $order->address()->save(OrderAddressFactory::new()->make());
34 | });
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/Models/Shop/Category.php:
--------------------------------------------------------------------------------
1 |
25 | */
26 | protected $casts = [
27 | 'is_visible' => 'boolean',
28 | ];
29 |
30 | /** @return HasMany */
31 | public function children(): HasMany
32 | {
33 | return $this->hasMany(Category::class, 'parent_id');
34 | }
35 |
36 | /** @return BelongsTo */
37 | public function parent(): BelongsTo
38 | {
39 | return $this->belongsTo(Category::class, 'parent_id');
40 | }
41 |
42 | /** @return BelongsToMany */
43 | public function products(): BelongsToMany
44 | {
45 | return $this->belongsToMany(Product::class, 'shop_category_product', 'shop_category_id', 'shop_product_id');
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/Filament/Resources/Shop/OrderResource/Pages/ListOrders.php:
--------------------------------------------------------------------------------
1 | Tab::make('All'),
33 | 'new' => Tab::make()->query(fn ($query) => $query->where('status', 'new')),
34 | 'processing' => Tab::make()->query(fn ($query) => $query->where('status', 'processing')),
35 | 'shipped' => Tab::make()->query(fn ($query) => $query->where('status', 'shipped')),
36 | 'delivered' => Tab::make()->query(fn ($query) => $query->where('status', 'delivered')),
37 | 'cancelled' => Tab::make()->query(fn ($query) => $query->where('status', 'cancelled')),
38 | ];
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/Filament/Exports/Blog/AuthorExporter.php:
--------------------------------------------------------------------------------
1 | label('ID'),
19 | ExportColumn::make('name'),
20 | ExportColumn::make('email')
21 | ->label('Email address'),
22 | ExportColumn::make('github_handle')
23 | ->label('GitHub'),
24 | ExportColumn::make('twitter_handle')
25 | ->label('Twitter'),
26 | ExportColumn::make('created_at'),
27 | ExportColumn::make('updated_at'),
28 | ];
29 | }
30 |
31 | public static function getCompletedNotificationBody(Export $export): string
32 | {
33 | $body = 'Your author export has completed and ' . number_format($export->successful_rows) . ' ' . str('row')->plural($export->successful_rows) . ' exported.';
34 |
35 | if ($failedRowsCount = $export->getFailedRowsCount()) {
36 | $body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to export.';
37 | }
38 |
39 | return $body;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/Enums/OrderStatus.php:
--------------------------------------------------------------------------------
1 | 'New',
25 | self::Processing => 'Processing',
26 | self::Shipped => 'Shipped',
27 | self::Delivered => 'Delivered',
28 | self::Cancelled => 'Cancelled',
29 | };
30 | }
31 |
32 | public function getColor(): string | array | null
33 | {
34 | return match ($this) {
35 | self::New => 'info',
36 | self::Processing => 'warning',
37 | self::Shipped, self::Delivered => 'success',
38 | self::Cancelled => 'danger',
39 | };
40 | }
41 |
42 | public function getIcon(): ?string
43 | {
44 | return match ($this) {
45 | self::New => 'heroicon-m-sparkles',
46 | self::Processing => 'heroicon-m-arrow-path',
47 | self::Shipped => 'heroicon-m-truck',
48 | self::Delivered => 'heroicon-m-check-badge',
49 | self::Cancelled => 'heroicon-m-x-circle',
50 | };
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/Providers/RouteServiceProvider.php:
--------------------------------------------------------------------------------
1 | configureRateLimiting();
28 |
29 | $this->routes(function () {
30 | Route::prefix('api')
31 | ->middleware('api')
32 | ->namespace($this->namespace)
33 | ->group(base_path('routes/api.php'));
34 |
35 | Route::middleware('web')
36 | ->namespace($this->namespace)
37 | ->group(base_path('routes/web.php'));
38 | });
39 | }
40 |
41 | /**
42 | * Configure the rate limiters for the application.
43 | *
44 | * @return void
45 | */
46 | protected function configureRateLimiting()
47 | {
48 | RateLimiter::for('api', function (Request $request) {
49 | return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
50 | });
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/Filament/Resources/Shop/OrderResource/Widgets/OrderStats.php:
--------------------------------------------------------------------------------
1 | between(
28 | start: now()->subYear(),
29 | end: now(),
30 | )
31 | ->perMonth()
32 | ->count();
33 |
34 | return [
35 | Stat::make('Orders', $this->getPageTableQuery()->count())
36 | ->chart(
37 | $orderData
38 | ->map(fn (TrendValue $value) => $value->aggregate)
39 | ->toArray()
40 | ),
41 | Stat::make('Open orders', $this->getPageTableQuery()->whereIn('status', ['open', 'processing'])->count()),
42 | Stat::make('Average price', number_format($this->getPageTableQuery()->avg('total_price'), 2)),
43 | ];
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/Models/Shop/Product.php:
--------------------------------------------------------------------------------
1 |
26 | */
27 | protected $casts = [
28 | 'featured' => 'boolean',
29 | 'is_visible' => 'boolean',
30 | 'backorder' => 'boolean',
31 | 'requires_shipping' => 'boolean',
32 | 'published_at' => 'date',
33 | ];
34 |
35 | /** @return BelongsTo */
36 | public function brand(): BelongsTo
37 | {
38 | return $this->belongsTo(Brand::class, 'shop_brand_id');
39 | }
40 |
41 | /** @return BelongsToMany */
42 | public function categories(): BelongsToMany
43 | {
44 | return $this->belongsToMany(Category::class, 'shop_category_product', 'shop_product_id', 'shop_category_id')->withTimestamps();
45 | }
46 |
47 | /** @return MorphMany */
48 | public function comments(): MorphMany
49 | {
50 | return $this->morphMany(Comment::class, 'commentable');
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/Livewire/Form.php:
--------------------------------------------------------------------------------
1 | */
19 | public $data = [];
20 |
21 | public function mount(): void
22 | {
23 | if (! app()->environment('local')) {
24 | abort(404);
25 | }
26 |
27 | $this->form->fill();
28 | }
29 |
30 | /** @return Forms\Components\Component[] */
31 | protected function getFormSchema(): array
32 | {
33 | return [
34 | Forms\Components\Builder::make('test')
35 | ->blocks([
36 | Forms\Components\Builder\Block::make('one')
37 | ->schema([
38 | Forms\Components\TextInput::make('one'),
39 | ]),
40 | Forms\Components\Builder\Block::make('two')
41 | ->schema([
42 | Forms\Components\TextInput::make('two'),
43 | ]),
44 | ]),
45 | ];
46 | }
47 |
48 | public function submit(): never
49 | {
50 | dd($this->form->getState());
51 | }
52 |
53 | protected function getFormStatePath(): ?string
54 | {
55 | return 'data';
56 | }
57 |
58 | public function render(): View
59 | {
60 | return view('livewire.form');
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/app/Models/Shop/Order.php:
--------------------------------------------------------------------------------
1 |
25 | */
26 | protected $fillable = [
27 | 'number',
28 | 'total_price',
29 | 'status',
30 | 'currency',
31 | 'shipping_price',
32 | 'shipping_method',
33 | 'notes',
34 | ];
35 |
36 | protected $casts = [
37 | 'status' => OrderStatus::class,
38 | ];
39 |
40 | /** @return MorphOne */
41 | public function address(): MorphOne
42 | {
43 | return $this->morphOne(OrderAddress::class, 'addressable');
44 | }
45 |
46 | /** @return BelongsTo */
47 | public function customer(): BelongsTo
48 | {
49 | return $this->belongsTo(Customer::class, 'shop_customer_id');
50 | }
51 |
52 | /** @return HasMany */
53 | public function items(): HasMany
54 | {
55 | return $this->hasMany(OrderItem::class, 'shop_order_id');
56 | }
57 |
58 | /** @return HasMany */
59 | public function payments(): HasMany
60 | {
61 | return $this->hasMany(Payment::class);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/config/hashing.php:
--------------------------------------------------------------------------------
1 | 'bcrypt',
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Bcrypt Options
23 | |--------------------------------------------------------------------------
24 | |
25 | | Here you may specify the configuration options that should be used when
26 | | passwords are hashed using the Bcrypt algorithm. This will allow you
27 | | to control the amount of time it takes to hash the given password.
28 | |
29 | */
30 |
31 | 'bcrypt' => [
32 | 'rounds' => env('BCRYPT_ROUNDS', 10),
33 | ],
34 |
35 | /*
36 | |--------------------------------------------------------------------------
37 | | Argon Options
38 | |--------------------------------------------------------------------------
39 | |
40 | | Here you may specify the configuration options that should be used when
41 | | passwords are hashed using the Argon algorithm. These will allow you
42 | | to control the amount of time it takes to hash the given password.
43 | |
44 | */
45 |
46 | 'argon' => [
47 | 'memory' => 1024,
48 | 'threads' => 2,
49 | 'time' => 2,
50 | ],
51 |
52 | ];
53 |
--------------------------------------------------------------------------------
/bootstrap/app.php:
--------------------------------------------------------------------------------
1 | singleton(
30 | Illuminate\Contracts\Http\Kernel::class,
31 | App\Http\Kernel::class
32 | );
33 |
34 | $app->singleton(
35 | Illuminate\Contracts\Console\Kernel::class,
36 | App\Console\Kernel::class
37 | );
38 |
39 | $app->singleton(
40 | Illuminate\Contracts\Debug\ExceptionHandler::class,
41 | App\Exceptions\Handler::class
42 | );
43 |
44 | /*
45 | |--------------------------------------------------------------------------
46 | | Return The Application
47 | |--------------------------------------------------------------------------
48 | |
49 | | This script returns the application instance. The instance is given to
50 | | the calling script so we can separate the building of the instances
51 | | from the actual running of the application and sending responses.
52 | |
53 | */
54 |
55 | return $app;
56 |
--------------------------------------------------------------------------------
/database/migrations/2022_06_09_155042_create_addressable_table.php:
--------------------------------------------------------------------------------
1 | id();
13 | $table->string('country')->nullable();
14 | $table->string('street')->nullable();
15 | $table->string('city')->nullable();
16 | $table->string('state')->nullable();
17 | $table->string('zip')->nullable();
18 |
19 | // Determine database type and use appropriate syntax for generated columns
20 | if (DB::getDriverName() === 'pgsql') {
21 | // PostgreSQL requires stored columns with || for concatenation
22 | $table->string('full_address')->storedAs("street || ', ' || zip || ' ' || city");
23 | } elseif (DB::getDriverName() === 'sqlite') {
24 | // SQLite uses || for concatenation, virtualAs is used
25 | $table->string('full_address')->virtualAs("street || ', ' || zip || ' ' || city");
26 | } else {
27 | // MySQL uses CONCAT for string concatenation
28 | $table->string('full_address')->virtualAs("CONCAT(street, ', ', zip, ' ', city)");
29 | }
30 |
31 | $table->timestamps();
32 | });
33 |
34 | Schema::create('addressables', function (Blueprint $table) {
35 | $table->foreignId('address_id');
36 | $table->morphs('addressable');
37 | });
38 | }
39 |
40 | public function down()
41 | {
42 | Schema::dropIfExists('addresses');
43 | Schema::dropIfExists('addressables');
44 | }
45 | };
46 |
--------------------------------------------------------------------------------
/artisan:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | make(Illuminate\Contracts\Console\Kernel::class);
34 |
35 | $status = $kernel->handle(
36 | $input = new Symfony\Component\Console\Input\ArgvInput,
37 | new Symfony\Component\Console\Output\ConsoleOutput
38 | );
39 |
40 | /*
41 | |--------------------------------------------------------------------------
42 | | Shutdown The Application
43 | |--------------------------------------------------------------------------
44 | |
45 | | Once Artisan has finished running, we will fire off the shutdown events
46 | | so that any final work may be done by the application before we shut
47 | | down the process. This is the last thing to happen to the request.
48 | |
49 | */
50 |
51 | $kernel->terminate($input, $status);
52 |
53 | exit($status);
54 |
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 | make(Kernel::class);
50 |
51 | $response = $kernel->handle(
52 | $request = Request::capture()
53 | )->send();
54 |
55 | $kernel->terminate($request, $response);
56 |
--------------------------------------------------------------------------------
/config/broadcasting.php:
--------------------------------------------------------------------------------
1 | env('BROADCAST_DRIVER', 'null'),
19 |
20 | /*
21 | |--------------------------------------------------------------------------
22 | | Broadcast Connections
23 | |--------------------------------------------------------------------------
24 | |
25 | | Here you may define all of the broadcast connections that will be used
26 | | to broadcast events to other systems or over websockets. Samples of
27 | | each available type of connection are provided inside this array.
28 | |
29 | */
30 |
31 | 'connections' => [
32 |
33 | 'pusher' => [
34 | 'driver' => 'pusher',
35 | 'key' => env('PUSHER_APP_KEY'),
36 | 'secret' => env('PUSHER_APP_SECRET'),
37 | 'app_id' => env('PUSHER_APP_ID'),
38 | 'options' => [
39 | 'cluster' => env('PUSHER_APP_CLUSTER'),
40 | 'useTLS' => true,
41 | ],
42 | ],
43 |
44 | 'ably' => [
45 | 'driver' => 'ably',
46 | 'key' => env('ABLY_KEY'),
47 | ],
48 |
49 | 'redis' => [
50 | 'driver' => 'redis',
51 | 'connection' => 'default',
52 | ],
53 |
54 | 'log' => [
55 | 'driver' => 'log',
56 | ],
57 |
58 | 'null' => [
59 | 'driver' => 'null',
60 | ],
61 |
62 | ],
63 |
64 | ];
65 |
--------------------------------------------------------------------------------
/database/factories/Shop/ProductFactory.php:
--------------------------------------------------------------------------------
1 | $name = $this->faker->unique()->catchPhrase(),
22 | 'slug' => Str::slug($name),
23 | 'sku' => $this->faker->unique()->ean8(),
24 | 'barcode' => $this->faker->ean13(),
25 | 'description' => $this->faker->realText(),
26 | 'qty' => $this->faker->randomDigitNotNull(),
27 | 'security_stock' => $this->faker->randomDigitNotNull(),
28 | 'featured' => $this->faker->boolean(),
29 | 'is_visible' => $this->faker->boolean(),
30 | 'old_price' => $this->faker->randomFloat(2, 100, 500),
31 | 'price' => $this->faker->randomFloat(2, 80, 400),
32 | 'cost' => $this->faker->randomFloat(2, 50, 200),
33 | 'type' => $this->faker->randomElement(['deliverable', 'downloadable']),
34 | 'published_at' => $this->faker->dateTimeBetween('-1 year', '+1 year'),
35 | 'created_at' => $this->faker->dateTimeBetween('-1 year', '-6 month'),
36 | 'updated_at' => $this->faker->dateTimeBetween('-5 month', 'now'),
37 | ];
38 | }
39 |
40 | public function configure(): ProductFactory
41 | {
42 | return $this->afterCreating(function (Product $product) {
43 | try {
44 | $product
45 | ->addMedia(LocalImages::getRandomFile())
46 | ->preservingOriginal()
47 | ->toMediaCollection('product-images');
48 | } catch (UnreachableUrl $exception) {
49 | return;
50 | }
51 | });
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/Filament/Widgets/LatestOrders.php:
--------------------------------------------------------------------------------
1 | query(OrderResource::getEloquentQuery())
22 | ->defaultPaginationPageOption(5)
23 | ->defaultSort('created_at', 'desc')
24 | ->columns([
25 | Tables\Columns\TextColumn::make('created_at')
26 | ->label('Order Date')
27 | ->date()
28 | ->sortable(),
29 | Tables\Columns\TextColumn::make('number')
30 | ->searchable()
31 | ->sortable(),
32 | Tables\Columns\TextColumn::make('customer.name')
33 | ->searchable()
34 | ->sortable(),
35 | Tables\Columns\TextColumn::make('status')
36 | ->badge(),
37 | Tables\Columns\TextColumn::make('currency')
38 | ->getStateUsing(fn ($record): ?string => Currency::find($record->currency)?->name ?? null)
39 | ->searchable()
40 | ->sortable(),
41 | Tables\Columns\TextColumn::make('total_price')
42 | ->searchable()
43 | ->sortable(),
44 | Tables\Columns\TextColumn::make('shipping_price')
45 | ->label('Shipping cost')
46 | ->searchable()
47 | ->sortable(),
48 | ])
49 | ->actions([
50 | Tables\Actions\Action::make('open')
51 | ->url(fn (Order $record): string => OrderResource::getUrl('edit', ['record' => $record])),
52 | ]);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/Providers/Filament/AppPanelProvider.php:
--------------------------------------------------------------------------------
1 | id('app')
27 | ->path('app')
28 | ->login(Login::class)
29 | ->registration()
30 | ->passwordReset()
31 | ->emailVerification()
32 | ->tenant(Team::class)
33 | ->tenantRegistration(RegisterTeam::class)
34 | ->discoverResources(in: app_path('Filament/App/Resources'), for: 'App\\Filament\\App\\Resources')
35 | ->discoverPages(in: app_path('Filament/App/Pages'), for: 'App\\Filament\\App\\Pages')
36 | ->discoverWidgets(in: app_path('Filament/App/Widgets'), for: 'App\\Filament\\App\\Widgets')
37 | ->middleware([
38 | EncryptCookies::class,
39 | AddQueuedCookiesToResponse::class,
40 | StartSession::class,
41 | AuthenticateSession::class,
42 | ShareErrorsFromSession::class,
43 | VerifyCsrfToken::class,
44 | SubstituteBindings::class,
45 | DisableBladeIconComponents::class,
46 | DispatchServingFilamentEvent::class,
47 | ])
48 | ->authMiddleware([
49 | Authenticate::class,
50 | ]);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/Filament/Imports/Blog/CategoryImporter.php:
--------------------------------------------------------------------------------
1 | requiredMapping()
19 | ->rules(['required', 'max:255'])
20 | ->example('Category A'),
21 | ImportColumn::make('slug')
22 | ->requiredMapping()
23 | ->rules(['required', 'max:255'])
24 | ->example('category-a'),
25 | ImportColumn::make('description')
26 | ->example('This is the description for Category A.'),
27 | ImportColumn::make('is_visible')
28 | ->label('Visibility')
29 | ->requiredMapping()
30 | ->boolean()
31 | ->rules(['required', 'boolean'])
32 | ->example('yes'),
33 | ImportColumn::make('seo_title')
34 | ->label('SEO title')
35 | ->rules(['max:60'])
36 | ->example('Awesome Category A'),
37 | ImportColumn::make('seo_description')
38 | ->label('SEO description')
39 | ->rules(['max:160'])
40 | ->example('Wow! It\'s just so amazing.'),
41 | ];
42 | }
43 |
44 | public function resolveRecord(): ?Category
45 | {
46 | return Category::firstOrNew([
47 | 'slug' => $this->data['slug'],
48 | ]);
49 | }
50 |
51 | public static function getCompletedNotificationBody(Import $import): string
52 | {
53 | $body = 'Your blog category import has completed and ' . number_format($import->successful_rows) . ' ' . str('row')->plural($import->successful_rows) . ' imported.';
54 |
55 | if ($failedRowsCount = $import->getFailedRowsCount()) {
56 | $body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to import.';
57 | }
58 |
59 | return $body;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/app/Filament/Resources/Shop/CustomerResource/RelationManagers/AddressesRelationManager.php:
--------------------------------------------------------------------------------
1 | schema([
22 | Forms\Components\TextInput::make('street'),
23 |
24 | Forms\Components\TextInput::make('zip'),
25 |
26 | Forms\Components\TextInput::make('city'),
27 |
28 | Forms\Components\TextInput::make('state'),
29 |
30 | Forms\Components\Select::make('country')
31 | ->searchable()
32 | ->getSearchResultsUsing(fn (string $query) => Country::where('name', 'like', "%{$query}%")->pluck('name', 'id'))
33 | ->getOptionLabelUsing(fn ($value): ?string => Country::firstWhere('id', $value)?->getAttribute('name')),
34 | ]);
35 | }
36 |
37 | public function table(Table $table): Table
38 | {
39 | return $table
40 | ->columns([
41 | Tables\Columns\TextColumn::make('street'),
42 |
43 | Tables\Columns\TextColumn::make('zip'),
44 |
45 | Tables\Columns\TextColumn::make('city'),
46 |
47 | Tables\Columns\TextColumn::make('country')
48 | ->formatStateUsing(fn ($state): ?string => Country::find($state)?->name ?? null),
49 | ])
50 | ->filters([
51 | //
52 | ])
53 | ->headerActions([
54 | Tables\Actions\AttachAction::make(),
55 | Tables\Actions\CreateAction::make(),
56 | ])
57 | ->actions([
58 | Tables\Actions\EditAction::make(),
59 | Tables\Actions\DetachAction::make(),
60 | Tables\Actions\DeleteAction::make(),
61 | ])
62 | ->groupedBulkActions([
63 | Tables\Actions\DetachBulkAction::make(),
64 | Tables\Actions\DeleteBulkAction::make(),
65 | ]);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/Filament/Clusters/Products/Resources/BrandResource/RelationManagers/AddressesRelationManager.php:
--------------------------------------------------------------------------------
1 | schema([
22 | Forms\Components\TextInput::make('street'),
23 |
24 | Forms\Components\TextInput::make('zip'),
25 |
26 | Forms\Components\TextInput::make('city'),
27 |
28 | Forms\Components\TextInput::make('state'),
29 |
30 | Forms\Components\Select::make('country')
31 | ->searchable()
32 | ->getSearchResultsUsing(fn (string $query) => Country::where('name', 'like', "%{$query}%")->pluck('name', 'id'))
33 | ->getOptionLabelUsing(fn ($value): ?string => Country::firstWhere('id', $value)?->getAttribute('name')),
34 | ]);
35 | }
36 |
37 | public function table(Table $table): Table
38 | {
39 | return $table
40 | ->columns([
41 | Tables\Columns\TextColumn::make('street'),
42 |
43 | Tables\Columns\TextColumn::make('zip'),
44 |
45 | Tables\Columns\TextColumn::make('city'),
46 |
47 | Tables\Columns\TextColumn::make('country')
48 | ->formatStateUsing(fn ($state): ?string => Country::find($state)?->name ?? null),
49 | ])
50 | ->filters([
51 | //
52 | ])
53 | ->headerActions([
54 | Tables\Actions\AttachAction::make(),
55 | Tables\Actions\CreateAction::make(),
56 | ])
57 | ->actions([
58 | Tables\Actions\EditAction::make(),
59 | Tables\Actions\DetachAction::make(),
60 | Tables\Actions\DeleteAction::make(),
61 | ])
62 | ->groupedBulkActions([
63 | Tables\Actions\DetachBulkAction::make(),
64 | Tables\Actions\DeleteBulkAction::make(),
65 | ]);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/Filament/Resources/Shop/OrderResource/Pages/CreateOrder.php:
--------------------------------------------------------------------------------
1 | schema([
27 | Wizard::make($this->getSteps())
28 | ->startOnStep($this->getStartStep())
29 | ->cancelAction($this->getCancelFormAction())
30 | ->submitAction($this->getSubmitFormAction())
31 | ->skippable($this->hasSkippableSteps())
32 | ->contained(false),
33 | ])
34 | ->columns(null);
35 | }
36 |
37 | protected function afterCreate(): void
38 | {
39 | /** @var Order $order */
40 | $order = $this->record;
41 |
42 | /** @var User $user */
43 | $user = auth()->user();
44 |
45 | Notification::make()
46 | ->title('New order')
47 | ->icon('heroicon-o-shopping-bag')
48 | ->body("**{$order->customer?->name} ordered {$order->items->count()} products.**")
49 | ->actions([
50 | Action::make('View')
51 | ->url(OrderResource::getUrl('edit', ['record' => $order])),
52 | ])
53 | ->sendToDatabase($user);
54 | }
55 |
56 | /** @return Step[] */
57 | protected function getSteps(): array
58 | {
59 | return [
60 | Step::make('Order Details')
61 | ->schema([
62 | Section::make()->schema(OrderResource::getDetailsFormSchema())->columns(),
63 | ]),
64 |
65 | Step::make('Order Items')
66 | ->schema([
67 | Section::make()->schema([
68 | OrderResource::getItemsRepeater(),
69 | ]),
70 | ]),
71 | ];
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/config/sanctum.php:
--------------------------------------------------------------------------------
1 | explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
17 | '%s%s',
18 | 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
19 | env('APP_URL') ? ',' . parse_url(env('APP_URL'), PHP_URL_HOST) : ''
20 | ))),
21 |
22 | /*
23 | |--------------------------------------------------------------------------
24 | | Sanctum Guards
25 | |--------------------------------------------------------------------------
26 | |
27 | | This array contains the authentication guards that will be checked when
28 | | Sanctum is trying to authenticate a request. If none of these guards
29 | | are able to authenticate the request, Sanctum will use the bearer
30 | | token that's present on an incoming request for authentication.
31 | |
32 | */
33 |
34 | 'guard' => ['web'],
35 |
36 | /*
37 | |--------------------------------------------------------------------------
38 | | Expiration Minutes
39 | |--------------------------------------------------------------------------
40 | |
41 | | This value controls the number of minutes until an issued token will be
42 | | considered expired. If this value is null, personal access tokens do
43 | | not expire. This won't tweak the lifetime of first-party sessions.
44 | |
45 | */
46 |
47 | 'expiration' => null,
48 |
49 | /*
50 | |--------------------------------------------------------------------------
51 | | Sanctum Middleware
52 | |--------------------------------------------------------------------------
53 | |
54 | | When authenticating your first-party SPA with Sanctum you may need to
55 | | customize some of the middleware Sanctum uses while processing the
56 | | request. You may change the middleware listed below as required.
57 | |
58 | */
59 |
60 | 'middleware' => [
61 | 'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class,
62 | 'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class,
63 | ],
64 |
65 | ];
66 |
--------------------------------------------------------------------------------
/config/filesystems.php:
--------------------------------------------------------------------------------
1 | env('FILESYSTEM_DRIVER', 'local'),
17 |
18 | /*
19 | |--------------------------------------------------------------------------
20 | | Filesystem Disks
21 | |--------------------------------------------------------------------------
22 | |
23 | | Here you may configure as many filesystem "disks" as you wish, and you
24 | | may even configure multiple disks of the same driver. Defaults have
25 | | been setup for each driver as an example of the required options.
26 | |
27 | | Supported Drivers: "local", "ftp", "sftp", "s3"
28 | |
29 | */
30 |
31 | 'disks' => [
32 |
33 | 'local' => [
34 | 'driver' => 'local',
35 | 'root' => storage_path('app'),
36 | ],
37 |
38 | 'public' => [
39 | 'driver' => 'local',
40 | 'root' => storage_path('app/public'),
41 | 'url' => env('APP_URL') . '/storage',
42 | 'visibility' => 'public',
43 | ],
44 |
45 | 's3' => [
46 | 'driver' => 's3',
47 | 'key' => env('AWS_ACCESS_KEY_ID'),
48 | 'secret' => env('AWS_SECRET_ACCESS_KEY'),
49 | 'region' => env('AWS_DEFAULT_REGION'),
50 | 'bucket' => env('AWS_BUCKET'),
51 | 'url' => env('AWS_URL'),
52 | 'endpoint' => env('AWS_ENDPOINT'),
53 | 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
54 | ],
55 |
56 | ],
57 |
58 | /*
59 | |--------------------------------------------------------------------------
60 | | Symbolic Links
61 | |--------------------------------------------------------------------------
62 | |
63 | | Here you may configure the symbolic links that will be created when the
64 | | `storage:link` Artisan command is executed. The array keys should be
65 | | the locations of the links and the values should be their targets.
66 | |
67 | */
68 |
69 | 'links' => [
70 | public_path('storage') => storage_path('app/public'),
71 | ],
72 |
73 | ];
74 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Filament Demo App
2 |
3 | A demo application to illustrate how Filament Admin works.
4 |
5 | 
6 |
7 | [Open in Gitpod](https://gitpod.io/#https://github.com/filamentphp/demo) to edit it and preview your changes with no setup required.
8 |
9 | ## Installation
10 |
11 | Clone the repo locally:
12 |
13 | ```sh
14 | git clone https://github.com/laravel-filament/demo.git filament-demo && cd filament-demo
15 | ```
16 |
17 | Install PHP dependencies:
18 |
19 | ```sh
20 | composer install
21 | ```
22 |
23 | Setup configuration:
24 |
25 | ```sh
26 | cp .env.example .env
27 | ```
28 |
29 | Generate application key:
30 |
31 | ```sh
32 | php artisan key:generate
33 | ```
34 |
35 | Create an SQLite database. You can also use another database (MySQL, Postgres), simply update your configuration accordingly.
36 |
37 | ```sh
38 | touch database/database.sqlite
39 | ```
40 |
41 | Run database migrations:
42 |
43 | ```sh
44 | php artisan migrate
45 | ```
46 |
47 | Run database seeder:
48 |
49 | ```sh
50 | php artisan db:seed
51 | ```
52 |
53 | > **Note**
54 | > If you get an "Invalid datetime format (1292)" error, this is probably related to the timezone setting of your database.
55 | > Please see https://dba.stackexchange.com/questions/234270/incorrect-datetime-value-mysql
56 |
57 |
58 | Create a symlink to the storage:
59 |
60 | ```sh
61 | php artisan storage:link
62 | ```
63 |
64 | Run the dev server (the output will give the address):
65 |
66 | ```sh
67 | php artisan serve
68 | ```
69 |
70 | You're ready to go! Visit the url in your browser, and login with:
71 |
72 | - **Username:** admin@filamentphp.com
73 | - **Password:** password
74 |
75 | ## Features to explore
76 |
77 | ### Relations
78 |
79 | #### BelongsTo
80 | - ProductResource
81 | - OrderResource
82 | - PostResource
83 |
84 | #### BelongsToMany
85 | - CategoryResource\RelationManagers\ProductsRelationManager
86 |
87 | #### HasMany
88 | - OrderResource\RelationManagers\PaymentsRelationManager
89 |
90 | #### HasManyThrough
91 | - CustomerResource\RelationManagers\PaymentsRelationManager
92 |
93 | #### MorphOne
94 | - OrderResource -> Address
95 |
96 | #### MorphMany
97 | - ProductResource\RelationManagers\CommentsRelationManager
98 | - PostResource\RelationManagers\CommentsRelationManager
99 |
100 | #### MorphToMany
101 | - BrandResource\RelationManagers\AddressRelationManager
102 | - CustomerResource\RelationManagers\AddressRelationManager
103 |
--------------------------------------------------------------------------------
/app/Filament/Imports/Shop/CategoryImporter.php:
--------------------------------------------------------------------------------
1 | requiredMapping()
19 | ->rules(['required', 'max:255'])
20 | ->example('Category A'),
21 | ImportColumn::make('slug')
22 | ->requiredMapping()
23 | ->rules(['required', 'max:255'])
24 | ->example('category-a'),
25 | ImportColumn::make('parent')
26 | ->relationship(resolveUsing: ['name', 'slug'])
27 | ->example('Category B'),
28 | ImportColumn::make('description')
29 | ->example('This is the description for Category A.'),
30 | ImportColumn::make('position')
31 | ->requiredMapping()
32 | ->numeric()
33 | ->rules(['required', 'integer'])
34 | ->example('1'),
35 | ImportColumn::make('is_visible')
36 | ->label('Visibility')
37 | ->requiredMapping()
38 | ->boolean()
39 | ->rules(['required', 'boolean'])
40 | ->example('yes'),
41 | ImportColumn::make('seo_title')
42 | ->label('SEO title')
43 | ->rules(['max:60'])
44 | ->example('Awesome Category A'),
45 | ImportColumn::make('seo_description')
46 | ->label('SEO description')
47 | ->rules(['max:160'])
48 | ->example('Wow! It\'s just so amazing.'),
49 | ];
50 | }
51 |
52 | public function resolveRecord(): ?Category
53 | {
54 | return Category::firstOrNew([
55 | 'slug' => $this->data['slug'],
56 | ]);
57 | }
58 |
59 | public static function getCompletedNotificationBody(Import $import): string
60 | {
61 | $body = 'Your shop category import has completed and ' . number_format($import->successful_rows) . ' ' . str('row')->plural($import->successful_rows) . ' imported.';
62 |
63 | if ($failedRowsCount = $import->getFailedRowsCount()) {
64 | $body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to import.';
65 | }
66 |
67 | return $body;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app/Http/Kernel.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | protected $middleware = [
17 | // \App\Http\Middleware\TrustHosts::class,
18 | \App\Http\Middleware\TrustProxies::class,
19 | \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
20 | \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
21 | \App\Http\Middleware\TrimStrings::class,
22 | \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
23 | ];
24 |
25 | /**
26 | * The application's route middleware groups.
27 | *
28 | * @var array>
29 | */
30 | protected $middlewareGroups = [
31 | 'web' => [
32 | \App\Http\Middleware\EncryptCookies::class,
33 | \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
34 | \Illuminate\Session\Middleware\StartSession::class,
35 | // \Illuminate\Session\Middleware\AuthenticateSession::class,
36 | \Illuminate\View\Middleware\ShareErrorsFromSession::class,
37 | \App\Http\Middleware\VerifyCsrfToken::class,
38 | \Illuminate\Routing\Middleware\SubstituteBindings::class,
39 | ],
40 |
41 | 'api' => [
42 | // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
43 | 'throttle:api',
44 | \Illuminate\Routing\Middleware\SubstituteBindings::class,
45 | ],
46 | ];
47 |
48 | /**
49 | * The application's route middleware.
50 | *
51 | * These middleware may be assigned to groups or used individually.
52 | *
53 | * @var array
54 | */
55 | protected $routeMiddleware = [
56 | 'auth' => \App\Http\Middleware\Authenticate::class,
57 | 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
58 | 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
59 | 'can' => \Illuminate\Auth\Middleware\Authorize::class,
60 | 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
61 | 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
62 | 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
63 | 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
64 | 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
65 | ];
66 | }
67 |
--------------------------------------------------------------------------------
/app/Providers/Filament/AdminPanelProvider.php:
--------------------------------------------------------------------------------
1 | default()
28 | ->id('admin')
29 | ->login(Login::class)
30 | ->discoverClusters(in: app_path('Filament/Clusters'), for: 'App\\Filament\\Clusters')
31 | ->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
32 | ->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
33 | ->pages([
34 | Dashboard::class,
35 | ])
36 | ->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
37 | ->widgets([
38 | Widgets\AccountWidget::class,
39 | Widgets\FilamentInfoWidget::class,
40 | ])
41 | ->unsavedChangesAlerts()
42 | ->brandLogo(fn () => view('filament.app.logo'))
43 | ->brandLogoHeight('1.25rem')
44 | ->navigationGroups([
45 | 'Shop',
46 | 'Blog',
47 | ])
48 | ->databaseNotifications()
49 | ->middleware([
50 | EncryptCookies::class,
51 | AddQueuedCookiesToResponse::class,
52 | StartSession::class,
53 | AuthenticateSession::class,
54 | ShareErrorsFromSession::class,
55 | VerifyCsrfToken::class,
56 | SubstituteBindings::class,
57 | DisableBladeIconComponents::class,
58 | DispatchServingFilamentEvent::class,
59 | ])
60 | ->authMiddleware([
61 | Authenticate::class,
62 | ])
63 | ->plugin(
64 | SpatieLaravelTranslatablePlugin::make()
65 | ->defaultLocales(['en', 'es', 'nl']),
66 | );
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/app/Filament/Widgets/StatsOverviewWidget.php:
--------------------------------------------------------------------------------
1 | filters['startDate'] ?? null) ?
21 | Carbon::parse($this->filters['startDate']) :
22 | null;
23 |
24 | $endDate = ! is_null($this->filters['endDate'] ?? null) ?
25 | Carbon::parse($this->filters['endDate']) :
26 | now();
27 |
28 | $isBusinessCustomersOnly = $this->filters['businessCustomersOnly'] ?? null;
29 | $businessCustomerMultiplier = match (true) {
30 | boolval($isBusinessCustomersOnly) => 2 / 3,
31 | blank($isBusinessCustomersOnly) => 1,
32 | default => 1 / 3,
33 | };
34 |
35 | $diffInDays = $startDate ? $startDate->diffInDays($endDate) : 0;
36 |
37 | $revenue = (int) (($startDate ? ($diffInDays * 137) : 192100) * $businessCustomerMultiplier);
38 | $newCustomers = (int) (($startDate ? ($diffInDays * 7) : 1340) * $businessCustomerMultiplier);
39 | $newOrders = (int) (($startDate ? ($diffInDays * 13) : 3543) * $businessCustomerMultiplier);
40 |
41 | $formatNumber = function (int $number): string {
42 | if ($number < 1000) {
43 | return (string) Number::format($number, 0);
44 | }
45 |
46 | if ($number < 1000000) {
47 | return Number::format($number / 1000, 2) . 'k';
48 | }
49 |
50 | return Number::format($number / 1000000, 2) . 'm';
51 | };
52 |
53 | return [
54 | Stat::make('Revenue', '$' . $formatNumber($revenue))
55 | ->description('32k increase')
56 | ->descriptionIcon('heroicon-m-arrow-trending-up')
57 | ->chart([7, 2, 10, 3, 15, 4, 17])
58 | ->color('success'),
59 | Stat::make('New customers', $formatNumber($newCustomers))
60 | ->description('3% decrease')
61 | ->descriptionIcon('heroicon-m-arrow-trending-down')
62 | ->chart([17, 16, 14, 15, 14, 13, 12])
63 | ->color('danger'),
64 | Stat::make('New orders', $formatNumber($newOrders))
65 | ->description('7% increase')
66 | ->descriptionIcon('heroicon-m-arrow-trending-up')
67 | ->chart([15, 4, 10, 2, 12, 4, 12])
68 | ->color('success'),
69 | ];
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/database/migrations/2021_12_13_164519_create_shop_products_table.php:
--------------------------------------------------------------------------------
1 | id();
18 | $table->foreignId('shop_brand_id')->nullable()->constrained()->nullOnDelete();
19 | $table->string('name');
20 | $table->string('slug')->unique()->nullable();
21 | $table->string('sku')->unique()->nullable();
22 | $table->string('barcode')->unique()->nullable();
23 | $table->longText('description')->nullable();
24 | $table->unsignedBigInteger('qty')->default(0);
25 | $table->unsignedBigInteger('security_stock')->default(0);
26 | $table->boolean('featured')->default(false);
27 | $table->boolean('is_visible')->default(false);
28 | $table->decimal('old_price', 10, 2)->nullable();
29 | $table->decimal('price', 10, 2)->nullable();
30 | $table->decimal('cost', 10, 2)->nullable();
31 | $table->enum('type', ['deliverable', 'downloadable'])->nullable();
32 | $table->boolean('backorder')->default(false);
33 | $table->boolean('requires_shipping')->default(false);
34 | $table->date('published_at')->nullable();
35 | $table->string('seo_title', 60)->nullable();
36 | $table->string('seo_description', 160)->nullable();
37 | $table->decimal('weight_value', 10, 2)->nullable()
38 | ->default(0.00)
39 | ->unsigned();
40 | $table->string('weight_unit')->default('kg');
41 | $table->decimal('height_value', 10, 2)->nullable()
42 | ->default(0.00)
43 | ->unsigned();
44 | $table->string('height_unit')->default('cm');
45 | $table->decimal('width_value', 10, 2)->nullable()
46 | ->default(0.00)
47 | ->unsigned();
48 | $table->string('width_unit')->default('cm');
49 | $table->decimal('depth_value', 10, 2)->nullable()
50 | ->default(0.00)
51 | ->unsigned();
52 | $table->string('depth_unit')->default('cm');
53 | $table->decimal('volume_value', 10, 2)->nullable()
54 | ->default(0.00)
55 | ->unsigned();
56 | $table->string('volume_unit')->default('l');
57 | $table->timestamps();
58 | });
59 | }
60 |
61 | /**
62 | * Reverse the migrations.
63 | *
64 | * @return void
65 | */
66 | public function down()
67 | {
68 | Schema::dropIfExists('shop_products');
69 | }
70 | };
71 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "laravel/laravel",
3 | "type": "project",
4 | "description": "The Laravel Framework.",
5 | "keywords": [
6 | "framework",
7 | "laravel"
8 | ],
9 | "license": "MIT",
10 | "require": {
11 | "php": "^8.2",
12 | "filament/filament": "^3.0",
13 | "filament/spatie-laravel-media-library-plugin": "^3.0",
14 | "filament/spatie-laravel-settings-plugin": "^3.0",
15 | "filament/spatie-laravel-tags-plugin": "^3.0",
16 | "filament/spatie-laravel-translatable-plugin": "^3.0",
17 | "flowframe/laravel-trend": "^0.2.0",
18 | "guzzlehttp/guzzle": "^7.2",
19 | "laravel/framework": "^11.0",
20 | "laravel/horizon": "^5.21",
21 | "laravel/sanctum": "^4.0",
22 | "laravel/tinker": "^2.8",
23 | "squirephp/countries-en": "^3.3",
24 | "squirephp/currencies-en": "^3.3"
25 | },
26 | "require-dev": {
27 | "barryvdh/laravel-debugbar": "^3.6",
28 | "fakerphp/faker": "^1.9.1",
29 | "laravel/pint": "^1.0",
30 | "laravel/sail": "^1.18",
31 | "mockery/mockery": "^1.4.4",
32 | "nunomaduro/collision": "^8.1",
33 | "larastan/larastan": "^2.1",
34 | "phpstan/phpstan-deprecation-rules": "^1.1",
35 | "phpunit/phpunit": "^10.1",
36 | "spatie/laravel-ignition": "^2.0"
37 | },
38 | "autoload": {
39 | "psr-4": {
40 | "App\\": "app/",
41 | "Database\\Factories\\": "database/factories/",
42 | "Database\\Seeders\\": "database/seeders/"
43 | }
44 | },
45 | "autoload-dev": {
46 | "psr-4": {
47 | "Tests\\": "tests/"
48 | }
49 | },
50 | "scripts": {
51 | "post-autoload-dump": [
52 | "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
53 | "@php artisan package:discover --ansi",
54 | "@php artisan filament:upgrade"
55 | ],
56 | "post-update-cmd": [
57 | "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
58 | ],
59 | "post-root-package-install": [
60 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
61 | ],
62 | "post-create-project-cmd": [
63 | "@php artisan key:generate --ansi"
64 | ],
65 | "cs": [
66 | "pint"
67 | ],
68 | "pint": "pint",
69 | "test:phpstan": "phpstan analyse",
70 | "test": [
71 | "@test:phpstan"
72 | ]
73 | },
74 | "extra": {
75 | "laravel": {
76 | "dont-discover": []
77 | }
78 | },
79 | "config": {
80 | "optimize-autoloader": true,
81 | "preferred-install": "dist",
82 | "sort-packages": true,
83 | "allow-plugins": {
84 | "composer/package-versions-deprecated": true
85 | }
86 | },
87 | "minimum-stability": "dev",
88 | "prefer-stable": true
89 | }
90 |
--------------------------------------------------------------------------------
/public/css/filament/support/support.css:
--------------------------------------------------------------------------------
1 | .fi-pagination-items,.fi-pagination-overview,.fi-pagination-records-per-page-select:not(.fi-compact){display:none}@supports (container-type:inline-size){.fi-pagination{container-type:inline-size}@container (min-width: 28rem){.fi-pagination-records-per-page-select.fi-compact{display:none}.fi-pagination-records-per-page-select:not(.fi-compact){display:inline}}@container (min-width: 56rem){.fi-pagination:not(.fi-simple)>.fi-pagination-previous-btn{display:none}.fi-pagination-overview{display:inline}.fi-pagination:not(.fi-simple)>.fi-pagination-next-btn{display:none}.fi-pagination-items{display:flex}}}@supports not (container-type:inline-size){@media (min-width:640px){.fi-pagination-records-per-page-select.fi-compact{display:none}.fi-pagination-records-per-page-select:not(.fi-compact){display:inline}}@media (min-width:768px){.fi-pagination:not(.fi-simple)>.fi-pagination-previous-btn{display:none}.fi-pagination-overview{display:inline}.fi-pagination:not(.fi-simple)>.fi-pagination-next-btn{display:none}.fi-pagination-items{display:flex}}}.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{background-color:#333;border-radius:4px;color:#fff;font-size:14px;line-height:1.4;outline:0;position:relative;transition-property:transform,visibility,opacity;white-space:normal}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{border-top-color:initial;border-width:8px 8px 0;bottom:-7px;left:0;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:initial;border-width:0 8px 8px;left:0;top:-7px;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-left-color:initial;border-width:8px 0 8px 8px;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{border-right-color:initial;border-width:8px 8px 8px 0;left:-7px;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{color:#333;height:16px;width:16px}.tippy-arrow:before{border-color:transparent;border-style:solid;content:"";position:absolute}.tippy-content{padding:5px 9px;position:relative;z-index:1}.tippy-box[data-theme~=light]{background-color:#fff;box-shadow:0 0 20px 4px #9aa1b126,0 4px 80px -8px #24282f40,0 4px 4px -2px #5b5e6926;color:#26323d}.tippy-box[data-theme~=light][data-placement^=top]>.tippy-arrow:before{border-top-color:#fff}.tippy-box[data-theme~=light][data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:#fff}.tippy-box[data-theme~=light][data-placement^=left]>.tippy-arrow:before{border-left-color:#fff}.tippy-box[data-theme~=light][data-placement^=right]>.tippy-arrow:before{border-right-color:#fff}.tippy-box[data-theme~=light]>.tippy-backdrop{background-color:#fff}.tippy-box[data-theme~=light]>.tippy-svg-arrow{fill:#fff}.fi-sortable-ghost{opacity:.3}
--------------------------------------------------------------------------------