diff --git a/app/Filament/Resources/Blog/LinkResource.php b/app/Filament/Resources/Blog/LinkResource.php new file mode 100644 index 000000000..b17e649f8 --- /dev/null +++ b/app/Filament/Resources/Blog/LinkResource.php @@ -0,0 +1,152 @@ +schema([ + Forms\Components\TextInput::make('title') + ->maxLength(255) + ->required(), + Forms\Components\ColorPicker::make('color') + ->required() + ->hex() + ->hexColor(), + Forms\Components\Textarea::make('description') + ->maxLength(1024) + ->required() + ->columnSpanFull(), + Forms\Components\TextInput::make('url') + ->label('URL') + ->required() + ->maxLength(255) + ->columnSpanFull(), + Forms\Components\FileUpload::make('image') + ->image(), + ]); + } + + public static function infolist(Infolist $infolist): Infolist + { + return $infolist + ->schema([ + TextEntry::make('title'), + ColorEntry::make('color'), + TextEntry::make('description') + ->columnSpanFull(), + TextEntry::make('url') + ->label('URL') + ->columnSpanFull() + ->url(fn (Link $record): string => '#' . urlencode($record->url)), + ImageEntry::make('image'), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\Layout\Stack::make([ + Tables\Columns\ImageColumn::make('image') + ->height('100%') + ->width('100%'), + Tables\Columns\Layout\Stack::make([ + Tables\Columns\TextColumn::make('title') + ->weight(FontWeight::Bold), + Tables\Columns\TextColumn::make('url') + ->formatStateUsing(fn (string $state): string => str($state)->after('://')->ltrim('www.')->trim('/')) + ->color('gray') + ->limit(30), + ]), + ])->space(3), + Tables\Columns\Layout\Panel::make([ + Tables\Columns\Layout\Split::make([ + Tables\Columns\ColorColumn::make('color') + ->grow(false), + Tables\Columns\TextColumn::make('description') + ->color('gray'), + ]), + ])->collapsible(), + ]) + ->filters([ + // + ]) + ->contentGrid([ + 'md' => 2, + 'xl' => 3, + ]) + ->paginated([ + 18, + 36, + 72, + 'all', + ]) + ->actions([ + Tables\Actions\Action::make('visit') + ->label('Visit link') + ->icon('heroicon-m-arrow-top-right-on-square') + ->color('gray') + ->url(fn (Link $record): string => '#' . urlencode($record->url)), + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make() + ->action(function () { + Notification::make() + ->title('Now, now, don\'t be cheeky, leave some records for others to play with!') + ->warning() + ->send(); + }), + ]), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListLinks::route('/'), + 'create' => Pages\CreateLink::route('/create'), + 'view' => Pages\ViewLink::route('/{record}'), + 'edit' => Pages\EditLink::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/Resources/Blog/LinkResource/Pages/CreateLink.php b/app/Filament/Resources/Blog/LinkResource/Pages/CreateLink.php new file mode 100644 index 000000000..1613c7493 --- /dev/null +++ b/app/Filament/Resources/Blog/LinkResource/Pages/CreateLink.php @@ -0,0 +1,21 @@ +schema([ Forms\Components\FileUpload::make('image') - ->label('Image') ->image() ->hiddenLabel(), ]) diff --git a/app/Models/Blog/Link.php b/app/Models/Blog/Link.php new file mode 100644 index 000000000..0025c0580 --- /dev/null +++ b/app/Models/Blog/Link.php @@ -0,0 +1,20 @@ +authMiddleware([ Authenticate::class, - ]); + ]) + ->plugin( + SpatieLaravelTranslatablePlugin::make() + ->defaultLocales(['en', 'es', 'nl']), + ); } } diff --git a/database/factories/Blog/LinkFactory.php b/database/factories/Blog/LinkFactory.php new file mode 100644 index 000000000..089791101 --- /dev/null +++ b/database/factories/Blog/LinkFactory.php @@ -0,0 +1,41 @@ + + */ +class LinkFactory extends Factory +{ + use CanCreateImages; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'url' => $this->faker->url(), + 'title' => [ + 'en' => Str::title($this->faker->words(asText: true)), + 'es' => Str::title($this->faker->words(asText: true)), + 'nl' => Str::title($this->faker->words(asText: true)), + ], + 'description' => [ + 'en' => $this->faker->sentence(), + 'es' => $this->faker->sentence(), + 'nl' => $this->faker->sentence(), + ], + 'color' => $this->faker->hexColor(), + 'image' => $this->createImage('https://source.unsplash.com/random/1280x720/?img=1'), + ]; + } +} diff --git a/database/factories/Blog/PostFactory.php b/database/factories/Blog/PostFactory.php index fe155e22a..87304c912 100644 --- a/database/factories/Blog/PostFactory.php +++ b/database/factories/Blog/PostFactory.php @@ -3,6 +3,7 @@ namespace Database\Factories\Blog; use App\Models\Blog\Post; +use Database\Factories\Concerns\CanCreateImages; use Database\Seeders\DatabaseSeeder; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Facades\Storage; @@ -11,6 +12,8 @@ class PostFactory extends Factory { + use CanCreateImages; + /** * @var string */ @@ -28,19 +31,4 @@ public function definition(): array 'updated_at' => $this->faker->dateTimeBetween('-5 month', 'now'), ]; } - - public function createImage(): ?string - { - try { - $image = file_get_contents(DatabaseSeeder::IMAGE_URL); - } catch (Throwable $exception) { - return null; - } - - $filename = Str::uuid() . '.jpg'; - - Storage::disk('public')->put($filename, $image); - - return $filename; - } } diff --git a/database/factories/Concerns/CanCreateImages.php b/database/factories/Concerns/CanCreateImages.php new file mode 100644 index 000000000..bade2f09f --- /dev/null +++ b/database/factories/Concerns/CanCreateImages.php @@ -0,0 +1,25 @@ +put($filename, $image); + + return $filename; + } +} diff --git a/database/migrations/2023_12_17_112735_create_blog_links_table.php b/database/migrations/2023_12_17_112735_create_blog_links_table.php new file mode 100644 index 000000000..e756b4bdb --- /dev/null +++ b/database/migrations/2023_12_17_112735_create_blog_links_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('url'); + $table->json('title'); + $table->json('description'); + $table->string('color'); + $table->string('image')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('links'); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index c3bf21af1..3570cff4d 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -6,6 +6,7 @@ use App\Models\Address; use App\Models\Blog\Author; use App\Models\Blog\Category as BlogCategory; +use App\Models\Blog\Link; use App\Models\Blog\Post; use App\Models\Comment; use App\Models\Shop\Brand; @@ -30,93 +31,99 @@ class DatabaseSeeder extends Seeder public function run(): void { - // Clear images - Storage::deleteDirectory('public'); - - // Admin - $this->command->warn(PHP_EOL . 'Creating admin user...'); - $user = $this->withProgressBar(1, fn () => User::factory(1)->create([ - 'name' => 'Demo User', - 'email' => 'admin@filamentphp.com', - ])); - $this->command->info('Admin user created.'); - - // Shop - $this->command->warn(PHP_EOL . 'Creating shop brands...'); - $brands = $this->withProgressBar(20, fn () => Brand::factory()->count(20) - ->has(Address::factory()->count(rand(1, 3))) - ->create()); - $this->command->info('Shop brands created.'); - - $this->command->warn(PHP_EOL . 'Creating shop categories...'); - $categories = $this->withProgressBar(20, fn () => ShopCategory::factory(1) - ->has( - ShopCategory::factory()->count(3), - 'children' - )->create()); - $this->command->info('Shop categories created.'); - - $this->command->warn(PHP_EOL . 'Creating shop customers...'); - $customers = $this->withProgressBar(1000, fn () => Customer::factory(1) - ->has(Address::factory()->count(rand(1, 3))) - ->create()); - $this->command->info('Shop customers created.'); - - $this->command->warn(PHP_EOL . 'Creating shop products...'); - $products = $this->withProgressBar(50, fn () => Product::factory(1) - ->sequence(fn ($sequence) => ['shop_brand_id' => $brands->random(1)->first()->id]) - ->hasAttached($categories->random(rand(3, 6)), ['created_at' => now(), 'updated_at' => now()]) - ->has( - Comment::factory()->count(rand(10, 20)) - ->state(fn (array $attributes, Product $product) => ['customer_id' => $customers->random(1)->first()->id]), - ) - ->create()); - $this->command->info('Shop products created.'); - - $this->command->warn(PHP_EOL . 'Creating orders...'); - $orders = $this->withProgressBar(1000, fn () => Order::factory(1) - ->sequence(fn ($sequence) => ['shop_customer_id' => $customers->random(1)->first()->id]) - ->has(Payment::factory()->count(rand(1, 3))) - ->has( - OrderItem::factory()->count(rand(2, 5)) - ->state(fn (array $attributes, Order $order) => ['shop_product_id' => $products->random(1)->first()->id]), - 'items' - ) - ->create()); - - foreach ($orders->random(rand(5, 8)) as $order) { - Notification::make() - ->title('New order') - ->icon('heroicon-o-shopping-bag') - ->body("{$order->customer->name} ordered {$order->items->count()} products.") - ->actions([ - Action::make('View') - ->url(OrderResource::getUrl('edit', ['record' => $order])), - ]) - ->sendToDatabase($user); - } - $this->command->info('Shop orders created.'); - - // Blog - $this->command->warn(PHP_EOL . 'Creating blog categories...'); - $blogCategories = $this->withProgressBar(20, fn () => BlogCategory::factory(1) +// // Clear images +// Storage::deleteDirectory('public'); +// +// // Admin +// $this->command->warn(PHP_EOL . 'Creating admin user...'); +// $user = $this->withProgressBar(1, fn () => User::factory(1)->create([ +// 'name' => 'Demo User', +// 'email' => 'admin@filamentphp.com', +// ])); +// $this->command->info('Admin user created.'); +// +// // Shop +// $this->command->warn(PHP_EOL . 'Creating shop brands...'); +// $brands = $this->withProgressBar(20, fn () => Brand::factory()->count(20) +// ->has(Address::factory()->count(rand(1, 3))) +// ->create()); +// $this->command->info('Shop brands created.'); +// +// $this->command->warn(PHP_EOL . 'Creating shop categories...'); +// $categories = $this->withProgressBar(20, fn () => ShopCategory::factory(1) +// ->has( +// ShopCategory::factory()->count(3), +// 'children' +// )->create()); +// $this->command->info('Shop categories created.'); +// +// $this->command->warn(PHP_EOL . 'Creating shop customers...'); +// $customers = $this->withProgressBar(1000, fn () => Customer::factory(1) +// ->has(Address::factory()->count(rand(1, 3))) +// ->create()); +// $this->command->info('Shop customers created.'); +// +// $this->command->warn(PHP_EOL . 'Creating shop products...'); +// $products = $this->withProgressBar(50, fn () => Product::factory(1) +// ->sequence(fn ($sequence) => ['shop_brand_id' => $brands->random(1)->first()->id]) +// ->hasAttached($categories->random(rand(3, 6)), ['created_at' => now(), 'updated_at' => now()]) +// ->has( +// Comment::factory()->count(rand(10, 20)) +// ->state(fn (array $attributes, Product $product) => ['customer_id' => $customers->random(1)->first()->id]), +// ) +// ->create()); +// $this->command->info('Shop products created.'); +// +// $this->command->warn(PHP_EOL . 'Creating orders...'); +// $orders = $this->withProgressBar(1000, fn () => Order::factory(1) +// ->sequence(fn ($sequence) => ['shop_customer_id' => $customers->random(1)->first()->id]) +// ->has(Payment::factory()->count(rand(1, 3))) +// ->has( +// OrderItem::factory()->count(rand(2, 5)) +// ->state(fn (array $attributes, Order $order) => ['shop_product_id' => $products->random(1)->first()->id]), +// 'items' +// ) +// ->create()); +// +// foreach ($orders->random(rand(5, 8)) as $order) { +// Notification::make() +// ->title('New order') +// ->icon('heroicon-o-shopping-bag') +// ->body("{$order->customer->name} ordered {$order->items->count()} products.") +// ->actions([ +// Action::make('View') +// ->url(OrderResource::getUrl('edit', ['record' => $order])), +// ]) +// ->sendToDatabase($user); +// } +// $this->command->info('Shop orders created.'); +// +// // Blog +// $this->command->warn(PHP_EOL . 'Creating blog categories...'); +// $blogCategories = $this->withProgressBar(20, fn () => BlogCategory::factory(1) +// ->count(20) +// ->create()); +// $this->command->info('Blog categories created.'); +// +// $this->command->warn(PHP_EOL . 'Creating blog authors and posts...'); +// $this->withProgressBar(20, fn () => Author::factory(1) +// ->has( +// Post::factory()->count(5) +// ->has( +// Comment::factory()->count(rand(5, 10)) +// ->state(fn (array $attributes, Post $post) => ['customer_id' => $customers->random(1)->first()->id]), +// ) +// ->state(fn (array $attributes, Author $author) => ['blog_category_id' => $blogCategories->random(1)->first()->id]), +// 'posts' +// ) +// ->create()); +// $this->command->info('Blog authors and posts created.'); + + $this->command->warn(PHP_EOL . 'Creating blog links...'); + $this->withProgressBar(20, fn () => Link::factory(1) ->count(20) ->create()); - $this->command->info('Blog categories created.'); - - $this->command->warn(PHP_EOL . 'Creating blog authors and posts...'); - $this->withProgressBar(20, fn () => Author::factory(1) - ->has( - Post::factory()->count(5) - ->has( - Comment::factory()->count(rand(5, 10)) - ->state(fn (array $attributes, Post $post) => ['customer_id' => $customers->random(1)->first()->id]), - ) - ->state(fn (array $attributes, Author $author) => ['blog_category_id' => $blogCategories->random(1)->first()->id]), - 'posts' - ) - ->create()); - $this->command->info('Blog authors and posts created.'); + $this->command->info('Blog links created.'); } protected function withProgressBar(int $amount, Closure $createCollectionOfOne): Collection