Skip to content

Commit 73e7f84

Browse files
committed
fix: complete admin workflows and frontend QA issues
1 parent 1d60151 commit 73e7f84

71 files changed

Lines changed: 2327 additions & 319 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/Filament/Resources/Galleries/GalleryResource.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use App\Filament\Resources\Galleries\Schemas\GalleryForm;
1010
use App\Filament\Resources\Galleries\Schemas\GalleryInfolist;
1111
use App\Filament\Resources\Galleries\Tables\GalleriesTable;
12+
use App\Filament\Support\RequiresPermission;
1213
use App\Models\Gallery;
1314
use BackedEnum;
1415
use Filament\Resources\Resource;
@@ -18,6 +19,8 @@
1819

1920
class GalleryResource extends Resource
2021
{
22+
use RequiresPermission;
23+
2124
protected static ?string $model = Gallery::class;
2225

2326
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedPhoto;

app/Filament/Resources/Galleries/Schemas/GalleryForm.php

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
use App\Filament\Support\SecureUpload;
66
use App\Filament\Support\SeoForm;
7+
use App\Models\Gallery;
8+
use App\Support\SortOrder;
79
use Filament\Forms\Components\FileUpload;
810
use Filament\Forms\Components\Repeater;
911
use Filament\Forms\Components\Select;
@@ -27,9 +29,13 @@ public static function configure(Schema $schema): Schema
2729
TextInput::make('slug')->required()->unique(ignoreRecord: true),
2830
Select::make('status')->options(['draft' => 'Draft', 'published' => 'Published'])->default('draft')->required(),
2931
Toggle::make('is_featured')->label('Featured gallery'),
30-
TextInput::make('sort_order')->numeric()->default(0),
32+
TextInput::make('sort_order')
33+
->numeric()
34+
->default(fn (): int => SortOrder::next(Gallery::class))
35+
->helperText('Used to order galleries. New galleries get the next order automatically.'),
3136
SecureUpload::image(FileUpload::make('cover_image'), 'galleries')
3237
->imagePreviewHeight('180')
38+
->helperText('Cover image. Allowed: JPG, JPEG, PNG, WEBP. Max size: 5 MB. Recommended: 1600x900 or wider.')
3339
->columnSpanFull(),
3440
])
3541
->columns(2),
@@ -42,18 +48,27 @@ public static function configure(Schema $schema): Schema
4248
]),
4349

4450
Section::make('Images')
45-
->description('Add multiple images with optional captions for responsive public grids.')
51+
->description('Add multiple image rows for responsive public grids. Allowed: JPG, JPEG, PNG, WEBP. Max size: 5 MB per image. Recommended: 1200px wide or larger. Server php.ini upload_max_filesize and post_max_size must also allow the selected files.')
4652
->schema([
4753
Repeater::make('images')
4854
->schema([
4955
SecureUpload::image(FileUpload::make('path'), 'galleries/items')
50-
->imagePreviewHeight('140'),
56+
->label('Image file')
57+
->imagePreviewHeight('140')
58+
->helperText('Upload one image for this row. Add more rows for multiple images.'),
5159
TextInput::make('url')
5260
->url()
53-
->helperText('Optional external image URL.'),
61+
->helperText('Optional external image URL. Use either an upload or an external URL.'),
5462
TextInput::make('caption'),
63+
TextInput::make('alt')
64+
->label('Alt text')
65+
->helperText('Optional accessible image description.'),
66+
TextInput::make('sort_order')
67+
->numeric()
68+
->helperText('Optional. Rows are auto-ordered when left empty.'),
5569
])
5670
->columns(3)
71+
->maxItems(50)
5772
->reorderable()
5873
->addActionLabel('Add image')
5974
->columnSpanFull(),

app/Filament/Resources/Galleries/Tables/GalleriesTable.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class GalleriesTable
2222
public static function configure(Table $table): Table
2323
{
2424
return TableUx::apply($table
25+
->defaultSort('sort_order')
2526
->columns([
2627
ImageColumn::make('cover_url')
2728
->label('Cover')

app/Filament/Resources/MediaItems/MediaItemResource.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use App\Filament\Resources\MediaItems\Schemas\MediaItemForm;
1010
use App\Filament\Resources\MediaItems\Schemas\MediaItemInfolist;
1111
use App\Filament\Resources\MediaItems\Tables\MediaItemsTable;
12+
use App\Filament\Support\RequiresPermission;
1213
use App\Models\MediaItem;
1314
use BackedEnum;
1415
use Filament\Resources\Resource;
@@ -18,6 +19,8 @@
1819

1920
class MediaItemResource extends Resource
2021
{
22+
use RequiresPermission;
23+
2124
protected static ?string $model = MediaItem::class;
2225

2326
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedPhoto;

app/Filament/Resources/MediaItems/Schemas/MediaItemForm.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace App\Filament\Resources\MediaItems\Schemas;
44

55
use App\Filament\Support\SecureUpload;
6+
use App\Models\MediaItem;
7+
use App\Support\SortOrder;
68
use Filament\Forms\Components\FileUpload;
79
use Filament\Forms\Components\Select;
810
use Filament\Forms\Components\Textarea;
@@ -28,7 +30,10 @@ public static function configure(Schema $schema): Schema
2830
->required(),
2931
Select::make('type')->options(['image' => 'Image', 'document' => 'Document'])->default('image')->required(),
3032
Select::make('status')->options(['draft' => 'Draft', 'published' => 'Published'])->default('published')->required(),
31-
TextInput::make('sort_order')->numeric()->default(0),
33+
TextInput::make('sort_order')
34+
->numeric()
35+
->default(fn (): int => SortOrder::next(MediaItem::class))
36+
->helperText('Used to order media in public grids. New media gets the next order automatically.'),
3237
])
3338
->columns(2),
3439

app/Filament/Resources/MediaItems/Tables/MediaItemsTable.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class MediaItemsTable
2020
public static function configure(Table $table): Table
2121
{
2222
return TableUx::apply($table
23+
->defaultSort('sort_order')
2324
->columns([
2425
ImageColumn::make('url')
2526
->label('Preview')

app/Filament/Resources/Menus/MenuResource.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use App\Filament\Resources\Menus\Schemas\MenuForm;
1010
use App\Filament\Resources\Menus\Schemas\MenuInfolist;
1111
use App\Filament\Resources\Menus\Tables\MenusTable;
12+
use App\Filament\Support\RequiresPermission;
1213
use App\Models\Menu;
1314
use BackedEnum;
1415
use Filament\Resources\Resource;
@@ -18,6 +19,8 @@
1819

1920
class MenuResource extends Resource
2021
{
22+
use RequiresPermission;
23+
2124
protected static ?string $model = Menu::class;
2225

2326
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedBars3;

app/Filament/Resources/Menus/Schemas/MenuForm.php

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace App\Filament\Resources\Menus\Schemas;
44

5+
use App\Models\Menu;
6+
use App\Support\SortOrder;
57
use Filament\Forms\Components\Repeater;
68
use Filament\Forms\Components\Select;
79
use Filament\Forms\Components\TextInput;
@@ -16,39 +18,71 @@ public static function configure(Schema $schema): Schema
1618
return $schema
1719
->components([
1820
Section::make('Menu settings')
19-
->description('Only one active menu is used for each location. Saving an active menu deactivates other menus in the same location.')
21+
->description('Only one active menu per location is used. Saving an active header, footer, mobile, or sidebar menu automatically deactivates other active menus in that location.')
2022
->schema([
2123
TextInput::make('name')->required(),
2224
Select::make('location')
23-
->options(['header' => 'Header', 'footer' => 'Footer'])
25+
->options([
26+
'header' => 'Header',
27+
'footer' => 'Footer',
28+
'mobile' => 'Mobile',
29+
'sidebar' => 'Sidebar',
30+
])
31+
->helperText('Header menu appears in the website header. Footer menu appears in the footer. Children appear as dropdown/submenu items.')
2432
->required(),
2533
Toggle::make('is_active')->default(true),
26-
TextInput::make('sort_order')->numeric()->default(0),
34+
TextInput::make('sort_order')
35+
->numeric()
36+
->default(fn (): int => SortOrder::next(Menu::class))
37+
->helperText('Used when choosing the active menu. New menus get the next order automatically.'),
2738
])
2839
->columns(2),
2940

3041
Section::make('Menu items')
31-
->description('Use routes for system pages, page slugs for CMS pages, or full URLs for external links. Inactive items are hidden on the public site.')
42+
->description('Create parent, child, and grandchild items. Use routes for system pages, page slugs for CMS pages, or URLs for external links. Inactive items are hidden on the public site.')
3243
->schema([
3344
Repeater::make('items')
34-
->schema([
35-
Toggle::make('is_active')->label('Visible')->default(true),
36-
TextInput::make('label.en')->label('English label')->required(),
37-
TextInput::make('label.fa')->label('Farsi label'),
38-
TextInput::make('label.ar')->label('Arabic label'),
39-
Select::make('type')
40-
->options(['route' => 'System route', 'page' => 'CMS page', 'url' => 'URL'])
41-
->default('route')
42-
->required(),
43-
TextInput::make('target')
44-
->required()
45-
->helperText('Examples: home, news.index, gallery.index, about, https://example.com.'),
46-
])
45+
->schema(self::itemSchema(1))
4746
->columns(2)
4847
->reorderable()
49-
->addActionLabel('Add menu item')
48+
->addActionLabel('Add top-level item')
5049
->columnSpanFull(),
5150
]),
5251
]);
5352
}
53+
54+
/**
55+
* @return array<int, mixed>
56+
*/
57+
private static function itemSchema(int $level): array
58+
{
59+
$schema = [
60+
Toggle::make('is_active')->label('Visible')->default(true),
61+
TextInput::make('sort_order')
62+
->numeric()
63+
->helperText('Optional. Items are auto-ordered when left empty.'),
64+
TextInput::make('label.en')->label('English label')->helperText('Leave empty only if you want the frontend fallback label.'),
65+
TextInput::make('label.fa')->label('Farsi label'),
66+
TextInput::make('label.ar')->label('Arabic label'),
67+
Select::make('type')
68+
->options(['route' => 'System route', 'page' => 'CMS page', 'url' => 'URL'])
69+
->default('route')
70+
->required(),
71+
TextInput::make('target')
72+
->helperText('Examples: home, news.index, gallery.index, about, google.com, https://example.com.')
73+
->required(),
74+
];
75+
76+
if ($level < 3) {
77+
$schema[] = Repeater::make('children')
78+
->label($level === 1 ? 'Child items' : 'Grandchild items')
79+
->schema(self::itemSchema($level + 1))
80+
->columns(2)
81+
->reorderable()
82+
->addActionLabel($level === 1 ? 'Add child item' : 'Add grandchild item')
83+
->columnSpanFull();
84+
}
85+
86+
return $schema;
87+
}
5488
}

app/Filament/Resources/Menus/Tables/MenusTable.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class MenusTable
2121
public static function configure(Table $table): Table
2222
{
2323
return TableUx::apply($table
24+
->defaultSort('sort_order')
2425
->columns([
2526
TextColumn::make('name')->searchable()->sortable()->wrap()->lineClamp(2),
2627
TextColumn::make('location')->badge(),

app/Filament/Resources/NewsPosts/NewsPostResource.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use App\Filament\Resources\NewsPosts\Schemas\NewsPostForm;
1010
use App\Filament\Resources\NewsPosts\Schemas\NewsPostInfolist;
1111
use App\Filament\Resources\NewsPosts\Tables\NewsPostsTable;
12+
use App\Filament\Support\RequiresPermission;
1213
use App\Models\NewsPost;
1314
use BackedEnum;
1415
use Filament\Resources\Resource;
@@ -18,6 +19,8 @@
1819

1920
class NewsPostResource extends Resource
2021
{
22+
use RequiresPermission;
23+
2124
protected static ?string $model = NewsPost::class;
2225

2326
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedNewspaper;

0 commit comments

Comments
 (0)