diff --git a/app/Filament/Resources/AiModelResource/Pages/ListAiModels.php b/app/Filament/Resources/AiModelResource/Pages/ListAiModels.php
index d3cc0ce..157e229 100644
--- a/app/Filament/Resources/AiModelResource/Pages/ListAiModels.php
+++ b/app/Filament/Resources/AiModelResource/Pages/ListAiModels.php
@@ -5,6 +5,8 @@ namespace App\Filament\Resources\AiModelResource\Pages;
use App\Filament\Resources\AiModelResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
+use Illuminate\Contracts\Pagination\Paginator;
+use Illuminate\Database\Eloquent\Builder;
class ListAiModels extends ListRecords
{
@@ -16,4 +18,35 @@ class ListAiModels extends ListRecords
Actions\CreateAction::make(),
];
}
+
+ protected function shouldPersistTableFiltersInSession(): bool
+ {
+ return true;
+ }
+
+ protected function shouldPersistTableSortInSession(): bool
+ {
+ return true;
+ }
+
+ protected function shouldPersistTableSearchInSession(): bool
+ {
+ return true;
+ }
+
+ protected function paginateTableQuery(Builder $query): Paginator
+ {
+ $paginator = parent::paginateTableQuery($query);
+ return $paginator;
+ }
+
+ public function updatedTablePage($page)
+ {
+ $this->dispatch('table-pagination-updated', ['tableId' => $this->id, 'page' => $page]);
+ }
+
+ protected function getTableQueryStringIdentifier(): ?string
+ {
+ return 'ai-models-table';
+ }
}
diff --git a/app/Filament/Resources/ApiProviderResource/Pages/ListApiProviders.php b/app/Filament/Resources/ApiProviderResource/Pages/ListApiProviders.php
index 41b162a..2c92f0a 100644
--- a/app/Filament/Resources/ApiProviderResource/Pages/ListApiProviders.php
+++ b/app/Filament/Resources/ApiProviderResource/Pages/ListApiProviders.php
@@ -5,6 +5,8 @@ namespace App\Filament\Resources\ApiProviderResource\Pages;
use App\Filament\Resources\ApiProviderResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
+use Illuminate\Contracts\Pagination\Paginator;
+use Illuminate\Database\Eloquent\Builder;
class ListApiProviders extends ListRecords
{
@@ -16,4 +18,35 @@ class ListApiProviders extends ListRecords
Actions\CreateAction::make(),
];
}
+
+ protected function shouldPersistTableFiltersInSession(): bool
+ {
+ return true;
+ }
+
+ protected function shouldPersistTableSortInSession(): bool
+ {
+ return true;
+ }
+
+ protected function shouldPersistTableSearchInSession(): bool
+ {
+ return true;
+ }
+
+ protected function paginateTableQuery(Builder $query): Paginator
+ {
+ $paginator = parent::paginateTableQuery($query);
+ return $paginator;
+ }
+
+ public function updatedTablePage($page)
+ {
+ $this->dispatch('table-pagination-updated', ['tableId' => $this->id, 'page' => $page]);
+ }
+
+ protected function getTableQueryStringIdentifier(): ?string
+ {
+ return 'api-providers-table';
+ }
}
diff --git a/app/Filament/Resources/ImageResource.php b/app/Filament/Resources/ImageResource.php
index 25d8a31..ecc5766 100644
--- a/app/Filament/Resources/ImageResource.php
+++ b/app/Filament/Resources/ImageResource.php
@@ -31,6 +31,9 @@ class ImageResource extends Resource
->required()
->image()
->directory('uploads'),
+ Forms\Components\Toggle::make('is_public')
+ ->label(__('Publicly Visible'))
+ ->default(false),
]);
}
diff --git a/app/Filament/Resources/ImageResource/Pages/ListImages.php b/app/Filament/Resources/ImageResource/Pages/ListImages.php
index 77159c5..b6d5432 100644
--- a/app/Filament/Resources/ImageResource/Pages/ListImages.php
+++ b/app/Filament/Resources/ImageResource/Pages/ListImages.php
@@ -5,6 +5,8 @@ namespace App\Filament\Resources\ImageResource\Pages;
use App\Filament\Resources\ImageResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
+use Illuminate\Contracts\Pagination\Paginator;
+use Illuminate\Database\Eloquent\Builder;
class ListImages extends ListRecords
{
@@ -16,4 +18,35 @@ class ListImages extends ListRecords
Actions\CreateAction::make(),
];
}
+
+ protected function shouldPersistTableFiltersInSession(): bool
+ {
+ return true;
+ }
+
+ protected function shouldPersistTableSortInSession(): bool
+ {
+ return true;
+ }
+
+ protected function shouldPersistTableSearchInSession(): bool
+ {
+ return true;
+ }
+
+ protected function paginateTableQuery(Builder $query): Paginator
+ {
+ $paginator = parent::paginateTableQuery($query);
+ return $paginator;
+ }
+
+ public function updatedTablePage($page)
+ {
+ $this->dispatch('table-pagination-updated', ['tableId' => $this->id, 'page' => $page]);
+ }
+
+ protected function getTableQueryStringIdentifier(): ?string
+ {
+ return 'images-table';
+ }
}
diff --git a/app/Filament/Resources/SettingResource.php b/app/Filament/Resources/SettingResource.php
index a517712..688b3cb 100644
--- a/app/Filament/Resources/SettingResource.php
+++ b/app/Filament/Resources/SettingResource.php
@@ -12,18 +12,31 @@ use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
+use Filament\Forms\Components\TextInput;
+use Filament\Forms\Components\Fieldset;
class SettingResource extends Resource
{
protected static ?string $model = Setting::class;
- protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
+ protected static ?string $navigationIcon = 'heroicon-o-cog';
public static function form(Form $form): Form
{
return $form
->schema([
- //
+ Forms\Components\TextInput::make('key')
+ ->label(__('Key'))
+ ->required()
+ ->maxLength(255)
+ ->hiddenOn('edit'),
+ Forms\Components\Fieldset::make()
+ ->label(fn (?Setting $record) => $record ? $record->key : __('New Setting'))
+ ->schema([
+ TextInput::make('value')
+ ->label(__('Value'))
+ ->disableLabel()
+ ])
]);
}
@@ -31,7 +44,8 @@ class SettingResource extends Resource
{
return $table
->columns([
- //
+ Tables\Columns\TextColumn::make('key')->label(__('Key'))->searchable()->sortable(),
+ Tables\Columns\TextColumn::make('value')->label(__('Value'))->searchable()->sortable(),
])
->filters([
//
@@ -39,6 +53,7 @@ class SettingResource extends Resource
->actions([
Tables\Actions\EditAction::make(),
])
+
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
diff --git a/app/Filament/Resources/SettingResource/Pages/Settings.php b/app/Filament/Resources/SettingResource/Pages/Settings.php
deleted file mode 100644
index c783388..0000000
--- a/app/Filament/Resources/SettingResource/Pages/Settings.php
+++ /dev/null
@@ -1,64 +0,0 @@
-form->fill(
- collect(Setting::all())
- ->mapWithKeys(fn (Setting $setting) => [$setting->key => $setting->value])
- ->all()
- );
- }
-
- public function form(Form $form): Form
- {
- return $form
- ->schema([
- TextInput::make('gallery_heading')
- ->label(__('settings.gallery_heading')),
- ])
- ->statePath('data')
- ->model(Setting::class);
- }
-
- public function submit(): void
- {
- foreach ($this->form->getState() as $key => $value) {
- Setting::updateOrCreate(['key' => $key], ['value' => $value]);
- }
-
- Notification::make()
- ->title(__('settings.saved_successfully'))
- ->success()
- ->send();
- }
-}
diff --git a/app/Filament/Resources/StyleResource.php b/app/Filament/Resources/StyleResource.php
index f1eb4a9..e3dc42c 100644
--- a/app/Filament/Resources/StyleResource.php
+++ b/app/Filament/Resources/StyleResource.php
@@ -110,9 +110,7 @@ class StyleResource extends Resource
])
->emptyStateActions([
Tables\Actions\CreateAction::make(),
- ])
- ->persistFiltersInSession()
- ->persistSortInSession();
+ ]);
}
public static function getRelations(): array
diff --git a/app/Filament/Resources/StyleResource/Pages/ListStyles.php b/app/Filament/Resources/StyleResource/Pages/ListStyles.php
index 47ed5a2..4413bb2 100644
--- a/app/Filament/Resources/StyleResource/Pages/ListStyles.php
+++ b/app/Filament/Resources/StyleResource/Pages/ListStyles.php
@@ -5,6 +5,8 @@ namespace App\Filament\Resources\StyleResource\Pages;
use App\Filament\Resources\StyleResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
+use Illuminate\Contracts\Pagination\Paginator;
+use Illuminate\Database\Eloquent\Builder;
class ListStyles extends ListRecords
{
@@ -16,4 +18,35 @@ class ListStyles extends ListRecords
Actions\CreateAction::make(),
];
}
+
+ protected function shouldPersistTableFiltersInSession(): bool
+ {
+ return true;
+ }
+
+ protected function shouldPersistTableSortInSession(): bool
+ {
+ return true;
+ }
+
+ protected function shouldPersistTableSearchInSession(): bool
+ {
+ return true;
+ }
+
+ protected function paginateTableQuery(Builder $query): Paginator
+ {
+ $paginator = parent::paginateTableQuery($query);
+ return $paginator;
+ }
+
+ public function updatedTablePage($page)
+ {
+ $this->dispatch('table-pagination-updated', ['tableId' => $this->id, 'page' => $page]);
+ }
+
+ protected function getTableQueryStringIdentifier(): ?string
+ {
+ return 'styles-table';
+ }
}
diff --git a/app/Filament/Resources/UserResource.php b/app/Filament/Resources/UserResource.php
index ed6c2d7..a0a2b76 100644
--- a/app/Filament/Resources/UserResource.php
+++ b/app/Filament/Resources/UserResource.php
@@ -27,24 +27,50 @@ class UserResource extends Resource
{
return $form
->schema([
- TextInput::make('name')
- ->label(__('filament.resource.user.form.name'))
- ->required()
- ->maxLength(255),
- TextInput::make('email')
- ->label(__('filament.resource.user.form.email'))
- ->email()
- ->required()
- ->maxLength(255),
- TextInput::make('password')
- ->label(__('filament.resource.user.form.password'))
- ->password()
- ->required()
- ->maxLength(255),
- Select::make('role_id')
- ->relationship('role', 'name')
- ->label(__('filament.resource.user.form.role'))
- ->required(),
+ Forms\Components\Section::make('User Details')
+ ->schema([
+ TextInput::make('name')
+ ->label(__('filament.resource.user.form.name'))
+ ->required()
+ ->maxLength(255),
+ TextInput::make('email')
+ ->label(__('filament.resource.user.form.email'))
+ ->email()
+ ->required()
+ ->maxLength(255),
+ TextInput::make('password')
+ ->label(__('filament.resource.user.form.password'))
+ ->password()
+ ->dehydrateStateUsing(fn (string $state): string => bcrypt($state))
+ ->dehydrated(fn (?string $state): bool => filled($state))
+ ->required(fn (string $operation): bool => $operation === 'create')
+ ->maxLength(255),
+ Select::make('role_id')
+ ->relationship('role', 'name')
+ ->label(__('filament.resource.user.form.role'))
+ ->required(),
+ ])->columns(2),
+
+ Forms\Components\Section::make('Preferences')
+ ->schema([
+ Forms\Components\Toggle::make('email_notifications_enabled')
+ ->label(__('Email Notifications'))
+ ->default(true),
+ Select::make('theme_preference')
+ ->label(__('Theme'))
+ ->options([
+ 'light' => 'Light',
+ 'dark' => 'Dark',
+ ])
+ ->default('light'),
+ Select::make('locale')
+ ->label(__('Language'))
+ ->options([
+ 'en' => 'English',
+ 'de' => 'Deutsch',
+ ])
+ ->default('en'),
+ ])->columns(2),
]);
}
diff --git a/app/Filament/Resources/UserResource/Pages/ListUsers.php b/app/Filament/Resources/UserResource/Pages/ListUsers.php
index 0766ffe..2613a13 100644
--- a/app/Filament/Resources/UserResource/Pages/ListUsers.php
+++ b/app/Filament/Resources/UserResource/Pages/ListUsers.php
@@ -5,6 +5,8 @@ namespace App\Filament\Resources\UserResource\Pages;
use App\Filament\Resources\UserResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
+use Illuminate\Contracts\Pagination\Paginator;
+use Illuminate\Database\Eloquent\Builder;
class ListUsers extends ListRecords
{
@@ -16,4 +18,35 @@ class ListUsers extends ListRecords
Actions\CreateAction::make(),
];
}
+
+ protected function shouldPersistTableFiltersInSession(): bool
+ {
+ return true;
+ }
+
+ protected function shouldPersistTableSortInSession(): bool
+ {
+ return true;
+ }
+
+ protected function shouldPersistTableSearchInSession(): bool
+ {
+ return true;
+ }
+
+ protected function paginateTableQuery(Builder $query): Paginator
+ {
+ $paginator = parent::paginateTableQuery($query);
+ return $paginator;
+ }
+
+ public function updatedTablePage($page)
+ {
+ $this->dispatch('table-pagination-updated', ['tableId' => $this->id, 'page' => $page]);
+ }
+
+ protected function getTableQueryStringIdentifier(): ?string
+ {
+ return 'users-table';
+ }
}
diff --git a/app/Http/Controllers/Api/ImageController.php b/app/Http/Controllers/Api/ImageController.php
index 5b525dd..072d5d5 100644
--- a/app/Http/Controllers/Api/ImageController.php
+++ b/app/Http/Controllers/Api/ImageController.php
@@ -9,10 +9,12 @@ use App\Models\ApiProvider;
use App\Models\Style;
use App\Models\Image;
use Illuminate\Support\Facades\File;
+use Carbon\Carbon;
+use App\Models\Setting;
class ImageController extends Controller
{
- public function index()
+ public function index(Request $request)
{
$publicUploadsPath = public_path('storage/uploads');
@@ -34,15 +36,28 @@ class ImageController extends Controller
// Add images from disk that are not in the database
$imagesToAdd = array_diff($diskImagePaths, $dbImagePaths);
foreach ($imagesToAdd as $path) {
- Image::create(['path' => $path]);
+ Image::create(['path' => $path, 'is_public' => true]);
}
// Remove images from database that are not on disk
$imagesToRemove = array_diff($dbImagePaths, $diskImagePaths);
Image::whereIn('path', $imagesToRemove)->delete();
- // Fetch all images from the database after synchronization
- $images = Image::orderBy('updated_at', 'desc')->get();
+ // Fetch images from the database after synchronization
+ $query = Image::orderBy('updated_at', 'desc');
+
+ // If user is not authenticated, filter by is_public
+ if (!auth()->check()) {
+ $query->where('is_public', true);
+ }
+
+ $newImageTimespanMinutes = Setting::where('key', 'new_image_timespan_minutes')->first()->value ?? 60; // Default to 60 minutes
+
+ $images = $query->get()->map(function ($image) use ($newImageTimespanMinutes) {
+ $image->is_new = Carbon::parse($image->created_at)->diffInMinutes(Carbon::now()) <= $newImageTimespanMinutes;
+ return $image;
+ });
+
$formattedImages = [];
foreach ($images as $image) {
$formattedImages[] = [
@@ -50,6 +65,8 @@ class ImageController extends Controller
'path' => asset('storage/' . $image->path),
'name' => basename($image->path),
'is_temp' => (bool) $image->is_temp,
+ 'is_public' => (bool) $image->is_public,
+ 'is_new' => (bool) $image->is_new,
];
}
return response()->json($formattedImages);
@@ -75,6 +92,7 @@ class ImageController extends Controller
$image = Image::create([
'path' => $relativePath,
+ 'is_public' => true,
]);
return response()->json([
@@ -96,15 +114,29 @@ class ImageController extends Controller
$request->validate([
'image_id' => 'required|exists:images,id',
- 'style_id' => 'required|exists:styles,id',
+ 'style_id' => 'nullable|exists:styles,id',
]);
$image = Image::find($request->image_id);
- $style = Style::with(['aiModel' => function ($query) {
- $query->where('enabled', true)->with(['apiProviders' => function ($query) {
- $query->where('enabled', true);
- }]);
- }])->find($request->style_id);
+ $style = null;
+
+ if ($request->style_id) {
+ $style = Style::with(['aiModel' => function ($query) {
+ $query->where('enabled', true)->with(['apiProviders' => function ($query) {
+ $query->where('enabled', true);
+ }]);
+ }])->find($request->style_id);
+ } else {
+ // Attempt to get default style from settings
+ $defaultStyleSetting = \App\Models\Setting::where('key', 'default_style_id')->first();
+ if ($defaultStyleSetting && $defaultStyleSetting->value) {
+ $style = Style::with(['aiModel' => function ($query) {
+ $query->where('enabled', true)->with(['apiProviders' => function ($query) {
+ $query->where('enabled', true);
+ }]);
+ }])->find($defaultStyleSetting->value);
+ }
+ }
if (!$style || !$style->aiModel || $style->aiModel->apiProviders->isEmpty()) {
return response()->json(['error' => __('api.style_or_provider_not_found')], 404);
diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php
index 9a425ac..c7055ad 100644
--- a/app/Http/Controllers/HomeController.php
+++ b/app/Http/Controllers/HomeController.php
@@ -4,20 +4,33 @@ namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Setting;
+use App\Models\Image;
use Inertia\Inertia;
use Illuminate\Support\Facades\Lang;
+use Carbon\Carbon;
class HomeController extends Controller
{
public function index()
{
$locale = app()->getLocale();
- $translations = Lang::get('messages', [], $locale);
+ $translations = array_merge(
+ Lang::get('api', [], $locale),
+ Lang::get('settings', [], $locale)
+ );
$galleryHeading = Setting::where('key', 'gallery_heading')->first()->value ?? 'Style Gallery';
+ $newImageTimespanMinutes = Setting::where('key', 'new_image_timespan_minutes')->first()->value ?? 60; // Default to 60 minutes
+
+ $images = Image::all()->map(function ($image) use ($newImageTimespanMinutes) {
+ $image->is_new = Carbon::parse($image->created_at)->diffInMinutes(Carbon::now()) <= $newImageTimespanMinutes;
+ $image->path = 'storage/' . $image->path;
+ return $image;
+ });
return Inertia::render('Home', [
'translations' => $translations,
'galleryHeading' => $galleryHeading,
+ 'images' => $images,
]);
}
}
\ No newline at end of file
diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php
index e3fcd92..944de36 100644
--- a/app/Http/Middleware/HandleInertiaRequests.php
+++ b/app/Http/Middleware/HandleInertiaRequests.php
@@ -35,14 +35,21 @@ class HandleInertiaRequests extends Middleware
'user' => $request->user(),
],
'locale' => app()->getLocale(),
- 'lang' => function () {
+ 'translations' => function () use ($request) {
+ $currentLocale = app()->getLocale(); // Store current locale
+ $requestedLocale = $request->input('locale', $currentLocale);
+ app()->setLocale($requestedLocale); // Set locale based on request or current
+
$lang = [
'filament' => trans('filament'),
'api' => trans('api'),
'settings' => trans('settings'),
- 'messages' => trans('messages'),
// Add other translation files as needed
];
+
+ dd($lang); // <-- ADDED FOR DEBUGGING
+
+ app()->setLocale($currentLocale); // Revert to original locale
return $lang;
},
];
diff --git a/app/Http/Middleware/SetLocale.php b/app/Http/Middleware/SetLocale.php
index 651adcb..927f0ff 100644
--- a/app/Http/Middleware/SetLocale.php
+++ b/app/Http/Middleware/SetLocale.php
@@ -15,12 +15,16 @@ class SetLocale
*/
public function handle(Request $request, Closure $next): Response
{
- $locale = substr($request->server('HTTP_ACCEPT_LANGUAGE'), 0, 2);
-
- if (in_array($locale, ['de'])) {
- app()->setLocale($locale);
+ if (auth()->check() && auth()->user()->locale) {
+ app()->setLocale(auth()->user()->locale);
} else {
- app()->setLocale('en');
+ $locale = substr($request->server('HTTP_ACCEPT_LANGUAGE'), 0, 2);
+
+ if (in_array($locale, ['de'])) {
+ app()->setLocale($locale);
+ } else {
+ app()->setLocale('en');
+ }
}
return $next($request);
diff --git a/app/Models/Image.php b/app/Models/Image.php
index 6669d51..fbebaee 100644
--- a/app/Models/Image.php
+++ b/app/Models/Image.php
@@ -16,5 +16,6 @@ class Image extends Model
'original_image_id',
'style_id',
'is_temp',
+ 'is_public',
];
}
diff --git a/app/Models/User.php b/app/Models/User.php
index 23e432a..c9bfe78 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -22,6 +22,9 @@ class User extends Authenticatable
'email',
'password',
'role_id',
+ 'email_notifications_enabled',
+ 'theme_preference',
+ 'locale',
];
public function role()
diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
index cd52120..2ac0167 100644
--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -2,6 +2,7 @@
namespace App\Providers;
+use Filament\Support\Facades\FilamentAsset;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
@@ -19,6 +20,10 @@ class AppServiceProvider extends ServiceProvider
*/
public function boot(): void
{
+ FilamentAsset::register([
+ \Filament\Support\Assets\Js::make('custom-navigation-state', __DIR__ . '/../../resources/js/custom/navigation-state.js'),
+ ]);
+
$locale = substr(request()->server('HTTP_ACCEPT_LANGUAGE'), 0, 2);
if (in_array($locale, ['de'])) {
diff --git a/app/Providers/Filament/AdminPanelProvider.php b/app/Providers/Filament/AdminPanelProvider.php
index aaa3ec5..f22776a 100644
--- a/app/Providers/Filament/AdminPanelProvider.php
+++ b/app/Providers/Filament/AdminPanelProvider.php
@@ -19,12 +19,13 @@ use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
use App\Filament\Resources\StyleResource;
use App\Filament\Resources\SettingResource\Pages\Settings;
+use Illuminate\Support\Facades\Auth;
class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
- return $panel
+ $panel = $panel
->default()
->id('admin')
->path('admin')
@@ -37,7 +38,7 @@ class AdminPanelProvider extends PanelProvider
->pages([
Pages\Dashboard::class,
\App\Filament\Pages\InstallPluginPage::class,
- Settings::class,
+
])
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
->widgets([
@@ -60,6 +61,19 @@ class AdminPanelProvider extends PanelProvider
])
->plugins([
- ]);
+ ])
+ ->profile();
+
+ if (Auth::check()) {
+ $user = Auth::user();
+ if ($user->theme_preference === 'dark') {
+ $panel->darkMode();
+ } else {
+ $panel->lightMode();
+ }
+ }
+
+ return $panel;
}
}
+
diff --git a/composer.json b/composer.json
index 8cd6b45..65e841b 100644
--- a/composer.json
+++ b/composer.json
@@ -6,7 +6,6 @@
"license": "MIT",
"require": {
"php": "^8.1",
-
"filament/filament": "3.0",
"guzzlehttp/guzzle": "^7.2",
"inertiajs/inertia-laravel": "^0.6.8",
diff --git a/composer.lock b/composer.lock
index cbe6b62..f0b9f75 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4918,16 +4918,16 @@
},
{
"name": "symfony/console",
- "version": "v6.4.23",
+ "version": "v6.4.24",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "9056771b8eca08d026cd3280deeec3cfd99c4d93"
+ "reference": "59266a5bf6a596e3e0844fd95e6ad7ea3c1d3350"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/9056771b8eca08d026cd3280deeec3cfd99c4d93",
- "reference": "9056771b8eca08d026cd3280deeec3cfd99c4d93",
+ "url": "https://api.github.com/repos/symfony/console/zipball/59266a5bf6a596e3e0844fd95e6ad7ea3c1d3350",
+ "reference": "59266a5bf6a596e3e0844fd95e6ad7ea3c1d3350",
"shasum": ""
},
"require": {
@@ -4992,7 +4992,7 @@
"terminal"
],
"support": {
- "source": "https://github.com/symfony/console/tree/v6.4.23"
+ "source": "https://github.com/symfony/console/tree/v6.4.24"
},
"funding": [
{
@@ -5003,12 +5003,16 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-06-27T19:37:22+00:00"
+ "time": "2025-07-30T10:38:54+00:00"
},
{
"name": "symfony/css-selector",
@@ -5144,16 +5148,16 @@
},
{
"name": "symfony/error-handler",
- "version": "v6.4.23",
+ "version": "v6.4.24",
"source": {
"type": "git",
"url": "https://github.com/symfony/error-handler.git",
- "reference": "b088e0b175c30b4e06d8085200fa465b586f44fa"
+ "reference": "30fd0b3cf0e972e82636038ce4db0e4fe777112c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/error-handler/zipball/b088e0b175c30b4e06d8085200fa465b586f44fa",
- "reference": "b088e0b175c30b4e06d8085200fa465b586f44fa",
+ "url": "https://api.github.com/repos/symfony/error-handler/zipball/30fd0b3cf0e972e82636038ce4db0e4fe777112c",
+ "reference": "30fd0b3cf0e972e82636038ce4db0e4fe777112c",
"shasum": ""
},
"require": {
@@ -5199,7 +5203,7 @@
"description": "Provides tools to manage errors and ease debugging PHP code",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/error-handler/tree/v6.4.23"
+ "source": "https://github.com/symfony/error-handler/tree/v6.4.24"
},
"funding": [
{
@@ -5210,12 +5214,16 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-06-13T07:39:48+00:00"
+ "time": "2025-07-24T08:25:04+00:00"
},
{
"name": "symfony/event-dispatcher",
@@ -5375,16 +5383,16 @@
},
{
"name": "symfony/finder",
- "version": "v6.4.17",
+ "version": "v6.4.24",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
- "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7"
+ "reference": "73089124388c8510efb8d2d1689285d285937b08"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/finder/zipball/1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7",
- "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/73089124388c8510efb8d2d1689285d285937b08",
+ "reference": "73089124388c8510efb8d2d1689285d285937b08",
"shasum": ""
},
"require": {
@@ -5419,7 +5427,7 @@
"description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/finder/tree/v6.4.17"
+ "source": "https://github.com/symfony/finder/tree/v6.4.24"
},
"funding": [
{
@@ -5430,25 +5438,29 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2024-12-29T13:51:37+00:00"
+ "time": "2025-07-15T12:02:45+00:00"
},
{
"name": "symfony/html-sanitizer",
- "version": "v6.4.21",
+ "version": "v6.4.24",
"source": {
"type": "git",
"url": "https://github.com/symfony/html-sanitizer.git",
- "reference": "f66d6585c6ece946239317c339f8b2860dfdf2db"
+ "reference": "8e9bb309986809af4cd9e049f9362d736387f083"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/html-sanitizer/zipball/f66d6585c6ece946239317c339f8b2860dfdf2db",
- "reference": "f66d6585c6ece946239317c339f8b2860dfdf2db",
+ "url": "https://api.github.com/repos/symfony/html-sanitizer/zipball/8e9bb309986809af4cd9e049f9362d736387f083",
+ "reference": "8e9bb309986809af4cd9e049f9362d736387f083",
"shasum": ""
},
"require": {
@@ -5488,7 +5500,7 @@
"sanitizer"
],
"support": {
- "source": "https://github.com/symfony/html-sanitizer/tree/v6.4.21"
+ "source": "https://github.com/symfony/html-sanitizer/tree/v6.4.24"
},
"funding": [
{
@@ -5499,25 +5511,29 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-03-31T07:29:45+00:00"
+ "time": "2025-07-10T08:14:14+00:00"
},
{
"name": "symfony/http-foundation",
- "version": "v6.4.23",
+ "version": "v6.4.24",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
- "reference": "452d19f945ee41345fd8a50c18b60783546b7bd3"
+ "reference": "0341e41d8d8830c31a1dff5cbc5bdb3ec872a073"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-foundation/zipball/452d19f945ee41345fd8a50c18b60783546b7bd3",
- "reference": "452d19f945ee41345fd8a50c18b60783546b7bd3",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/0341e41d8d8830c31a1dff5cbc5bdb3ec872a073",
+ "reference": "0341e41d8d8830c31a1dff5cbc5bdb3ec872a073",
"shasum": ""
},
"require": {
@@ -5565,7 +5581,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/http-foundation/tree/v6.4.23"
+ "source": "https://github.com/symfony/http-foundation/tree/v6.4.24"
},
"funding": [
{
@@ -5576,25 +5592,29 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-05-26T09:17:58+00:00"
+ "time": "2025-07-10T08:14:14+00:00"
},
{
"name": "symfony/http-kernel",
- "version": "v6.4.23",
+ "version": "v6.4.24",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
- "reference": "2bb2cba685aabd859f22cf6946554e8e7f3c329a"
+ "reference": "b81dcdbe34b8e8f7b3fc7b2a47fa065d5bf30726"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-kernel/zipball/2bb2cba685aabd859f22cf6946554e8e7f3c329a",
- "reference": "2bb2cba685aabd859f22cf6946554e8e7f3c329a",
+ "url": "https://api.github.com/repos/symfony/http-kernel/zipball/b81dcdbe34b8e8f7b3fc7b2a47fa065d5bf30726",
+ "reference": "b81dcdbe34b8e8f7b3fc7b2a47fa065d5bf30726",
"shasum": ""
},
"require": {
@@ -5679,7 +5699,7 @@
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/http-kernel/tree/v6.4.23"
+ "source": "https://github.com/symfony/http-kernel/tree/v6.4.24"
},
"funding": [
{
@@ -5690,25 +5710,29 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-06-28T08:14:51+00:00"
+ "time": "2025-07-31T09:23:30+00:00"
},
{
"name": "symfony/mailer",
- "version": "v6.4.23",
+ "version": "v6.4.24",
"source": {
"type": "git",
"url": "https://github.com/symfony/mailer.git",
- "reference": "a480322ddf8e54de262c9bca31fdcbe26b553de5"
+ "reference": "b4d7fa2c69641109979ed06e98a588d245362062"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/mailer/zipball/a480322ddf8e54de262c9bca31fdcbe26b553de5",
- "reference": "a480322ddf8e54de262c9bca31fdcbe26b553de5",
+ "url": "https://api.github.com/repos/symfony/mailer/zipball/b4d7fa2c69641109979ed06e98a588d245362062",
+ "reference": "b4d7fa2c69641109979ed06e98a588d245362062",
"shasum": ""
},
"require": {
@@ -5759,7 +5783,7 @@
"description": "Helps sending emails",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/mailer/tree/v6.4.23"
+ "source": "https://github.com/symfony/mailer/tree/v6.4.24"
},
"funding": [
{
@@ -5770,25 +5794,29 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-06-26T21:24:02+00:00"
+ "time": "2025-07-24T08:25:04+00:00"
},
{
"name": "symfony/mime",
- "version": "v6.4.21",
+ "version": "v6.4.24",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
- "reference": "fec8aa5231f3904754955fad33c2db50594d22d1"
+ "reference": "664d5e844a2de5e11c8255d0aef6bc15a9660ac7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/mime/zipball/fec8aa5231f3904754955fad33c2db50594d22d1",
- "reference": "fec8aa5231f3904754955fad33c2db50594d22d1",
+ "url": "https://api.github.com/repos/symfony/mime/zipball/664d5e844a2de5e11c8255d0aef6bc15a9660ac7",
+ "reference": "664d5e844a2de5e11c8255d0aef6bc15a9660ac7",
"shasum": ""
},
"require": {
@@ -5844,7 +5872,7 @@
"mime-type"
],
"support": {
- "source": "https://github.com/symfony/mime/tree/v6.4.21"
+ "source": "https://github.com/symfony/mime/tree/v6.4.24"
},
"funding": [
{
@@ -5855,12 +5883,16 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-04-27T13:27:38+00:00"
+ "time": "2025-07-15T12:02:45+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -6501,16 +6533,16 @@
},
{
"name": "symfony/process",
- "version": "v6.4.20",
+ "version": "v6.4.24",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
- "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20"
+ "reference": "8eb6dc555bfb49b2703438d5de65cc9f138ff50b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/process/zipball/e2a61c16af36c9a07e5c9906498b73e091949a20",
- "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20",
+ "url": "https://api.github.com/repos/symfony/process/zipball/8eb6dc555bfb49b2703438d5de65cc9f138ff50b",
+ "reference": "8eb6dc555bfb49b2703438d5de65cc9f138ff50b",
"shasum": ""
},
"require": {
@@ -6542,7 +6574,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/process/tree/v6.4.20"
+ "source": "https://github.com/symfony/process/tree/v6.4.24"
},
"funding": [
{
@@ -6553,25 +6585,29 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-03-10T17:11:00+00:00"
+ "time": "2025-07-10T08:14:14+00:00"
},
{
"name": "symfony/routing",
- "version": "v6.4.22",
+ "version": "v6.4.24",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
- "reference": "1f5234e8457164a3a0038a4c0a4ba27876a9c670"
+ "reference": "e4f94e625c8e6f910aa004a0042f7b2d398278f5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/routing/zipball/1f5234e8457164a3a0038a4c0a4ba27876a9c670",
- "reference": "1f5234e8457164a3a0038a4c0a4ba27876a9c670",
+ "url": "https://api.github.com/repos/symfony/routing/zipball/e4f94e625c8e6f910aa004a0042f7b2d398278f5",
+ "reference": "e4f94e625c8e6f910aa004a0042f7b2d398278f5",
"shasum": ""
},
"require": {
@@ -6625,7 +6661,7 @@
"url"
],
"support": {
- "source": "https://github.com/symfony/routing/tree/v6.4.22"
+ "source": "https://github.com/symfony/routing/tree/v6.4.24"
},
"funding": [
{
@@ -6636,12 +6672,16 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-04-27T16:08:38+00:00"
+ "time": "2025-07-15T08:46:37+00:00"
},
{
"name": "symfony/service-contracts",
@@ -6728,16 +6768,16 @@
},
{
"name": "symfony/string",
- "version": "v7.3.0",
+ "version": "v7.3.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
- "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125"
+ "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/string/zipball/f3570b8c61ca887a9e2938e85cb6458515d2b125",
- "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125",
+ "url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca",
+ "reference": "42f505aff654e62ac7ac2ce21033818297ca89ca",
"shasum": ""
},
"require": {
@@ -6795,7 +6835,7 @@
"utf8"
],
"support": {
- "source": "https://github.com/symfony/string/tree/v7.3.0"
+ "source": "https://github.com/symfony/string/tree/v7.3.2"
},
"funding": [
{
@@ -6806,25 +6846,29 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-04-20T20:19:01+00:00"
+ "time": "2025-07-10T08:47:49+00:00"
},
{
"name": "symfony/translation",
- "version": "v6.4.23",
+ "version": "v6.4.24",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
- "reference": "de8afa521e04a5220e9e58a1dc99971ab7cac643"
+ "reference": "300b72643e89de0734d99a9e3f8494a3ef6936e1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/translation/zipball/de8afa521e04a5220e9e58a1dc99971ab7cac643",
- "reference": "de8afa521e04a5220e9e58a1dc99971ab7cac643",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/300b72643e89de0734d99a9e3f8494a3ef6936e1",
+ "reference": "300b72643e89de0734d99a9e3f8494a3ef6936e1",
"shasum": ""
},
"require": {
@@ -6890,7 +6934,7 @@
"description": "Provides tools to internationalize your application",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/translation/tree/v6.4.23"
+ "source": "https://github.com/symfony/translation/tree/v6.4.24"
},
"funding": [
{
@@ -6901,12 +6945,16 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-06-26T21:24:02+00:00"
+ "time": "2025-07-30T17:30:48+00:00"
},
{
"name": "symfony/translation-contracts",
@@ -6988,16 +7036,16 @@
},
{
"name": "symfony/uid",
- "version": "v6.4.23",
+ "version": "v6.4.24",
"source": {
"type": "git",
"url": "https://github.com/symfony/uid.git",
- "reference": "9c8592da78d7ee6af52011eef593350d87e814c0"
+ "reference": "17da16a750541a42cf2183935e0f6008316c23f7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/uid/zipball/9c8592da78d7ee6af52011eef593350d87e814c0",
- "reference": "9c8592da78d7ee6af52011eef593350d87e814c0",
+ "url": "https://api.github.com/repos/symfony/uid/zipball/17da16a750541a42cf2183935e0f6008316c23f7",
+ "reference": "17da16a750541a42cf2183935e0f6008316c23f7",
"shasum": ""
},
"require": {
@@ -7042,7 +7090,7 @@
"uuid"
],
"support": {
- "source": "https://github.com/symfony/uid/tree/v6.4.23"
+ "source": "https://github.com/symfony/uid/tree/v6.4.24"
},
"funding": [
{
@@ -7053,25 +7101,29 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-06-26T08:06:12+00:00"
+ "time": "2025-07-10T08:14:14+00:00"
},
{
"name": "symfony/var-dumper",
- "version": "v6.4.23",
+ "version": "v6.4.24",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
- "reference": "d55b1834cdbfcc31bc2cd7e095ba5ed9a88f6600"
+ "reference": "aa29484ce0544bd69fa9f0df902e5ed7b7fe5034"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-dumper/zipball/d55b1834cdbfcc31bc2cd7e095ba5ed9a88f6600",
- "reference": "d55b1834cdbfcc31bc2cd7e095ba5ed9a88f6600",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/aa29484ce0544bd69fa9f0df902e5ed7b7fe5034",
+ "reference": "aa29484ce0544bd69fa9f0df902e5ed7b7fe5034",
"shasum": ""
},
"require": {
@@ -7083,7 +7135,6 @@
"symfony/console": "<5.4"
},
"require-dev": {
- "ext-iconv": "*",
"symfony/console": "^5.4|^6.0|^7.0",
"symfony/error-handler": "^6.3|^7.0",
"symfony/http-kernel": "^5.4|^6.0|^7.0",
@@ -7127,7 +7178,7 @@
"dump"
],
"support": {
- "source": "https://github.com/symfony/var-dumper/tree/v6.4.23"
+ "source": "https://github.com/symfony/var-dumper/tree/v6.4.24"
},
"funding": [
{
@@ -7138,12 +7189,16 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-06-27T15:05:27+00:00"
+ "time": "2025-07-29T18:40:01+00:00"
},
{
"name": "tightenco/ziggy",
@@ -7890,16 +7945,16 @@
},
{
"name": "myclabs/deep-copy",
- "version": "1.13.3",
+ "version": "1.13.4",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
- "reference": "faed855a7b5f4d4637717c2b3863e277116beb36"
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/faed855a7b5f4d4637717c2b3863e277116beb36",
- "reference": "faed855a7b5f4d4637717c2b3863e277116beb36",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
+ "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
"shasum": ""
},
"require": {
@@ -7938,7 +7993,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
- "source": "https://github.com/myclabs/DeepCopy/tree/1.13.3"
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
},
"funding": [
{
@@ -7946,7 +8001,7 @@
"type": "tidelift"
}
],
- "time": "2025-07-05T12:25:42+00:00"
+ "time": "2025-08-01T08:46:24+00:00"
},
{
"name": "nunomaduro/collision",
@@ -9890,16 +9945,16 @@
},
{
"name": "symfony/yaml",
- "version": "v7.3.1",
+ "version": "v7.3.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
- "reference": "0c3555045a46ab3cd4cc5a69d161225195230edb"
+ "reference": "b8d7d868da9eb0919e99c8830431ea087d6aae30"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/yaml/zipball/0c3555045a46ab3cd4cc5a69d161225195230edb",
- "reference": "0c3555045a46ab3cd4cc5a69d161225195230edb",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/b8d7d868da9eb0919e99c8830431ea087d6aae30",
+ "reference": "b8d7d868da9eb0919e99c8830431ea087d6aae30",
"shasum": ""
},
"require": {
@@ -9942,7 +9997,7 @@
"description": "Loads and dumps YAML files",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/yaml/tree/v7.3.1"
+ "source": "https://github.com/symfony/yaml/tree/v7.3.2"
},
"funding": [
{
@@ -9953,12 +10008,16 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2025-06-03T06:57:57+00:00"
+ "time": "2025-07-10T08:47:49+00:00"
},
{
"name": "theseer/tokenizer",
diff --git a/database/migrations/2025_07_30_143255_create_settings_table.php b/database/migrations/2025_07_30_143255_create_settings_table.php
index ce3b578..3ff1773 100644
--- a/database/migrations/2025_07_30_143255_create_settings_table.php
+++ b/database/migrations/2025_07_30_143255_create_settings_table.php
@@ -12,8 +12,7 @@ return new class extends Migration
public function up(): void
{
Schema::create('settings', function (Blueprint $table) {
- $table->id();
- $table->string('key')->unique();
+ $table->string('key')->primary();
$table->text('value')->nullable();
$table->timestamps();
});
diff --git a/database/migrations/2025_08_01_055332_add_settings_to_users_table.php b/database/migrations/2025_08_01_055332_add_settings_to_users_table.php
new file mode 100644
index 0000000..ac5e29b
--- /dev/null
+++ b/database/migrations/2025_08_01_055332_add_settings_to_users_table.php
@@ -0,0 +1,30 @@
+boolean('email_notifications_enabled')->default(true);
+ $table->string('theme_preference')->default('light');
+ $table->string('locale')->default('en');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('users', function (Blueprint $table) {
+ $table->dropColumn(['email_notifications_enabled', 'theme_preference', 'locale']);
+ });
+ }
+};
diff --git a/database/migrations/2025_08_01_055356_add_is_public_to_images_table.php b/database/migrations/2025_08_01_055356_add_is_public_to_images_table.php
new file mode 100644
index 0000000..1a95bf8
--- /dev/null
+++ b/database/migrations/2025_08_01_055356_add_is_public_to_images_table.php
@@ -0,0 +1,28 @@
+boolean('is_public')->default(false);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('images', function (Blueprint $table) {
+ $table->dropColumn('is_public');
+ });
+ }
+};
diff --git a/database/migrations/2025_08_01_091436_add_two_factor_columns_to_users_table.php b/database/migrations/2025_08_01_091436_add_two_factor_columns_to_users_table.php
new file mode 100644
index 0000000..7fa1049
--- /dev/null
+++ b/database/migrations/2025_08_01_091436_add_two_factor_columns_to_users_table.php
@@ -0,0 +1,39 @@
+text('two_factor_secret')
+ ->nullable();
+
+ $table->text('two_factor_recovery_codes')
+ ->nullable();
+
+ $table->timestamp('two_factor_confirmed_at')
+ ->nullable();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('users', function (Blueprint $table) {
+ $table->dropColumn([
+ 'two_factor_secret',
+ 'two_factor_recovery_codes',
+ 'two_factor_confirmed_at',
+ ]);
+ });
+ }
+};
diff --git a/package-lock.json b/package-lock.json
index 247313f..08179be 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4,6 +4,11 @@
"requires": true,
"packages": {
"": {
+ "dependencies": {
+ "@fortawesome/fontawesome-svg-core": "^7.0.0",
+ "@fortawesome/free-solid-svg-icons": "^7.0.0",
+ "@fortawesome/vue-fontawesome": "^3.1.1"
+ },
"devDependencies": {
"@inertiajs/vue3": "^1.0.0",
"@tailwindcss/forms": "^0.5.3",
@@ -34,7 +39,6 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -44,7 +48,6 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -54,7 +57,6 @@
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
"integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.28.0"
@@ -70,7 +72,6 @@
"version": "7.28.2",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz",
"integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
@@ -471,6 +472,49 @@
"node": ">=12"
}
},
+ "node_modules/@fortawesome/fontawesome-common-types": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.0.0.tgz",
+ "integrity": "sha512-PGMrIYXLGA5K8RWy8zwBkd4vFi4z7ubxtet6Yn13Plf6krRTwPbdlCwlcfmoX0R7B4Z643QvrtHmdQ5fNtfFCg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/fontawesome-svg-core": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-7.0.0.tgz",
+ "integrity": "sha512-obBEF+zd98r/KtKVW6A+8UGWeaOoyMpl6Q9P3FzHsOnsg742aXsl8v+H/zp09qSSu/a/Hxe9LNKzbBaQq1CEbA==",
+ "license": "MIT",
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "7.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/free-solid-svg-icons": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-7.0.0.tgz",
+ "integrity": "sha512-njSLAllkOddYDCXgTFboXn54Oe5FcvpkWq+FoetOHR64PbN0608kM02Lze0xtISGpXgP+i26VyXRQA0Irh3Obw==",
+ "license": "(CC-BY-4.0 AND MIT)",
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "7.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/vue-fontawesome": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.1.1.tgz",
+ "integrity": "sha512-U5azn4mcUVpjHe4JO0Wbe7Ih8e3VbN83EH7OTBtA5/QGw9qcPGffqcmwsLyZYgEkpVkYbq/6dX1Iyl5KUGMp6Q==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@fortawesome/fontawesome-svg-core": "~1 || ~6 || ~7",
+ "vue": ">= 3.0.0 < 4"
+ }
+ },
"node_modules/@inertiajs/core": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@inertiajs/core/-/core-1.3.0.tgz",
@@ -542,7 +586,6 @@
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz",
"integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==",
- "dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
@@ -923,7 +966,6 @@
"version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.18.tgz",
"integrity": "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.0",
@@ -937,7 +979,6 @@
"version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz",
"integrity": "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-core": "3.5.18",
@@ -948,7 +989,6 @@
"version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz",
"integrity": "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.28.0",
@@ -966,7 +1006,6 @@
"version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz",
"integrity": "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.5.18",
@@ -977,7 +1016,6 @@
"version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.18.tgz",
"integrity": "sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@vue/shared": "3.5.18"
@@ -987,7 +1025,6 @@
"version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.18.tgz",
"integrity": "sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.5.18",
@@ -998,7 +1035,6 @@
"version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.18.tgz",
"integrity": "sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.5.18",
@@ -1011,7 +1047,6 @@
"version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.18.tgz",
"integrity": "sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-ssr": "3.5.18",
@@ -1025,7 +1060,6 @@
"version": "3.5.18",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.18.tgz",
"integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==",
- "dev": true,
"license": "MIT"
},
"node_modules/ansi-regex": {
@@ -1390,7 +1424,6 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "dev": true,
"license": "MIT"
},
"node_modules/deepmerge": {
@@ -1467,7 +1500,6 @@
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
- "dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
@@ -1578,7 +1610,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
- "dev": true,
"license": "MIT"
},
"node_modules/fast-glob": {
@@ -2027,7 +2058,6 @@
"version": "0.30.17",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
"integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0"
@@ -2142,7 +2172,6 @@
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
- "dev": true,
"funding": [
{
"type": "github",
@@ -2269,7 +2298,6 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
@@ -2309,7 +2337,6 @@
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
- "dev": true,
"funding": [
{
"type": "opencollective",
@@ -2734,7 +2761,6 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
- "dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@@ -3074,7 +3100,6 @@
"version": "3.5.18",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.18.tgz",
"integrity": "sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.5.18",
diff --git a/package.json b/package.json
index da24885..1bb1cb9 100644
--- a/package.json
+++ b/package.json
@@ -15,5 +15,10 @@
"tailwindcss": "^3.2.1",
"vite": "^5.0.0",
"vue": "^3.4.0"
+ },
+ "dependencies": {
+ "@fortawesome/fontawesome-svg-core": "^7.0.0",
+ "@fortawesome/free-solid-svg-icons": "^7.0.0",
+ "@fortawesome/vue-fontawesome": "^3.1.1"
}
}
diff --git a/public/js/app/custom-navigation-state.js b/public/js/app/custom-navigation-state.js
new file mode 100644
index 0000000..8abdc88
--- /dev/null
+++ b/public/js/app/custom-navigation-state.js
@@ -0,0 +1,83 @@
+document.addEventListener('DOMContentLoaded', () => {
+ const navigation = document.querySelector('.fi-main-nav');
+ if (!navigation) return;
+
+ const KEY = 'navigation_state';
+ const TABLE_PAGES_KEY = 'table_pages_state';
+
+ // Function to get the state from session via a custom endpoint
+ const getState = () => {
+ return JSON.parse(sessionStorage.getItem(KEY)) || {};
+ };
+
+ // Function to save the state to session via a custom endpoint
+ const saveState = (state) => {
+ sessionStorage.setItem(KEY, JSON.stringify(state));
+ const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
+ fetch('/api/admin/navigation-state', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-CSRF-TOKEN': csrfToken,
+ },
+ body: JSON.stringify({ groups: state }),
+ });
+ };
+
+ // Function to get table page state
+ const getTablePageState = () => {
+ return JSON.parse(sessionStorage.getItem(TABLE_PAGES_KEY)) || {};
+ };
+
+ // Function to save table page state
+ const saveTablePageState = (tableId, page) => {
+ let tablePages = getTablePageState();
+ tablePages[tableId] = page;
+ sessionStorage.setItem(TABLE_PAGES_KEY, JSON.stringify(tablePages));
+ };
+
+ let currentState = getState();
+ let currentTablePages = getTablePageState();
+
+ // Apply the saved navigation state on page load
+ const groups = navigation.querySelectorAll('.fi-nav-group');
+ groups.forEach(group => {
+ const label = group.querySelector('.fi-nav-group-label').textContent.trim();
+ if (currentState[label] === 'collapsed') {
+ group.classList.add('collapsed');
+ }
+ });
+
+ // Add event listeners to save navigation state on click
+ navigation.addEventListener('click', (e) => {
+ const labelElement = e.target.closest('.fi-nav-group-label');
+ if (!labelElement) return;
+
+ const group = labelElement.closest('.fi-nav-group');
+ const label = labelElement.textContent.trim();
+
+ if (group.classList.contains('collapsed')) {
+ currentState[label] = 'expanded';
+ group.classList.remove('collapsed');
+ } else {
+ currentState[label] = 'collapsed';
+ group.classList.add('collapsed');
+ }
+
+ saveState(currentState);
+ });
+
+ // Livewire hook to save table page when pagination changes
+ Livewire.hook('component.initialized', (component) => {
+ if (component.name.includes('table')) {
+ const tableId = component.el.id;
+ if (currentTablePages[tableId]) {
+ component.set('tablePage', currentTablePages[tableId], false);
+ }
+ }
+ });
+
+ Livewire.on('table-pagination-updated', (tableId, page) => {
+ saveTablePageState(tableId, page);
+ });
+});
diff --git a/resources/css/app.css b/resources/css/app.css
index b5c61c9..d1cf579 100644
--- a/resources/css/app.css
+++ b/resources/css/app.css
@@ -1,3 +1,23 @@
+@import '@fortawesome/fontawesome-svg-core/styles.css';
+
@tailwind base;
@tailwind components;
@tailwind utilities;
+
+/* Theme variables */
+:root {
+ --color-background: #ffffff;
+ --color-text: #000000;
+}
+
+html.dark {
+ --color-background: #333333; /* Dark grey */
+ --color-text: #ffffff;
+}
+
+/* Apply theme to body */
+body {
+ background-color: var(--color-background);
+ color: var(--color-text);
+ transition: background-color 0.3s ease, color 0.3s ease;
+}
\ No newline at end of file
diff --git a/resources/js/Components/GalleryGrid.vue b/resources/js/Components/GalleryGrid.vue
index 7efdd1b..a4acc60 100644
--- a/resources/js/Components/GalleryGrid.vue
+++ b/resources/js/Components/GalleryGrid.vue
@@ -8,20 +8,31 @@
@click="$emit('imageTapped', image, $event)"
>
+