mirror of
https://github.com/pacnpal/thrillwiki_laravel.git
synced 2025-12-20 12:11:14 -05:00
feat: add middleware for cookie encryption, CSRF verification, string trimming, and maintenance request prevention; implement Designer resource management with CRUD pages and permissions
This commit is contained in:
125
app/Filament/Resources/DesignerResource.php
Normal file
125
app/Filament/Resources/DesignerResource.php
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources;
|
||||||
|
|
||||||
|
use App\Filament\Resources\DesignerResource\Pages;
|
||||||
|
use App\Filament\Resources\DesignerResource\RelationManagers;
|
||||||
|
use App\Models\Designer;
|
||||||
|
use Filament\Forms;
|
||||||
|
use Filament\Forms\Form;
|
||||||
|
use Filament\Resources\Resource;
|
||||||
|
use Filament\Tables;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
|
||||||
|
class DesignerResource extends Resource
|
||||||
|
{
|
||||||
|
protected static ?string $model = Designer::class;
|
||||||
|
protected static ?string $navigationIcon = 'heroicon-o-building-office';
|
||||||
|
protected static ?string $navigationGroup = 'Company Management';
|
||||||
|
protected static ?int $navigationSort = 1;
|
||||||
|
|
||||||
|
public static function form(Form $form): Form
|
||||||
|
{
|
||||||
|
return $form
|
||||||
|
->schema([
|
||||||
|
Forms\Components\Section::make('Basic Information')
|
||||||
|
->schema([
|
||||||
|
Forms\Components\TextInput::make('name')
|
||||||
|
->required()
|
||||||
|
->maxLength(255)
|
||||||
|
->live(onBlur: true)
|
||||||
|
->afterStateUpdated(fn ($state, Forms\Set $set) =>
|
||||||
|
$set('slug', str($state)->slug())),
|
||||||
|
Forms\Components\TextInput::make('slug')
|
||||||
|
->required()
|
||||||
|
->maxLength(255)
|
||||||
|
->unique(ignoreRecord: true),
|
||||||
|
Forms\Components\TextInput::make('headquarters')
|
||||||
|
->maxLength(255),
|
||||||
|
Forms\Components\DatePicker::make('founded_date')
|
||||||
|
->label('Founded Date')
|
||||||
|
->format('Y-m-d'),
|
||||||
|
])->columns(2),
|
||||||
|
|
||||||
|
Forms\Components\Section::make('Additional Details')
|
||||||
|
->schema([
|
||||||
|
Forms\Components\TextInput::make('website')
|
||||||
|
->url()
|
||||||
|
->prefix('https://')
|
||||||
|
->maxLength(255),
|
||||||
|
Forms\Components\RichEditor::make('description')
|
||||||
|
->columnSpanFull()
|
||||||
|
->toolbarButtons([
|
||||||
|
'bold',
|
||||||
|
'italic',
|
||||||
|
'link',
|
||||||
|
'bulletList',
|
||||||
|
'orderedList',
|
||||||
|
'h2',
|
||||||
|
'h3',
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->columns([
|
||||||
|
Tables\Columns\TextColumn::make('name')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
Tables\Columns\TextColumn::make('headquarters')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
Tables\Columns\TextColumn::make('founded_date')
|
||||||
|
->date()
|
||||||
|
->sortable(),
|
||||||
|
Tables\Columns\TextColumn::make('rides_count')
|
||||||
|
->counts('rides')
|
||||||
|
->label('Rides')
|
||||||
|
->sortable(),
|
||||||
|
Tables\Columns\TextColumn::make('website')
|
||||||
|
->searchable()
|
||||||
|
->url(fn ($state) => str($state)->start('https://')),
|
||||||
|
Tables\Columns\TextColumn::make('updated_at')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
Tables\Filters\Filter::make('has_rides')
|
||||||
|
->query(fn (Builder $query) => $query->has('rides'))
|
||||||
|
->label('Has Rides'),
|
||||||
|
Tables\Filters\Filter::make('no_rides')
|
||||||
|
->query(fn (Builder $query) => $query->doesntHave('rides'))
|
||||||
|
->label('No Rides'),
|
||||||
|
])
|
||||||
|
->actions([
|
||||||
|
Tables\Actions\EditAction::make(),
|
||||||
|
Tables\Actions\ViewAction::make(),
|
||||||
|
])
|
||||||
|
->bulkActions([
|
||||||
|
Tables\Actions\BulkActionGroup::make([
|
||||||
|
Tables\Actions\DeleteBulkAction::make(),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRelations(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
RelationManagers\RidesRelationManager::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getPages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'index' => Pages\ListDesigners::route('/'),
|
||||||
|
'create' => Pages\CreateDesigner::route('/create'),
|
||||||
|
'edit' => Pages\EditDesigner::route('/{record}/edit'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\DesignerResource\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\DesignerResource;
|
||||||
|
use Filament\Actions;
|
||||||
|
use Filament\Resources\Pages\CreateRecord;
|
||||||
|
|
||||||
|
class CreateDesigner extends CreateRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = DesignerResource::class;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\DesignerResource\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\DesignerResource;
|
||||||
|
use Filament\Actions;
|
||||||
|
use Filament\Resources\Pages\EditRecord;
|
||||||
|
|
||||||
|
class EditDesigner extends EditRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = DesignerResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Actions\DeleteAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\DesignerResource\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\DesignerResource;
|
||||||
|
use Filament\Actions;
|
||||||
|
use Filament\Resources\Pages\ListRecords;
|
||||||
|
|
||||||
|
class ListDesigners extends ListRecords
|
||||||
|
{
|
||||||
|
protected static string $resource = DesignerResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Actions\CreateAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\DesignerResource\RelationManagers;
|
||||||
|
|
||||||
|
use Filament\Forms;
|
||||||
|
use Filament\Forms\Form;
|
||||||
|
use Filament\Resources\RelationManagers\RelationManager;
|
||||||
|
use Filament\Tables;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
|
||||||
|
class RidesRelationManager extends RelationManager
|
||||||
|
{
|
||||||
|
protected static string $relationship = 'rides';
|
||||||
|
protected static ?string $title = 'Rides';
|
||||||
|
|
||||||
|
public function form(Form $form): Form
|
||||||
|
{
|
||||||
|
return $form
|
||||||
|
->schema([
|
||||||
|
Forms\Components\TextInput::make('name')
|
||||||
|
->required()
|
||||||
|
->maxLength(255),
|
||||||
|
Forms\Components\TextInput::make('manufacturer_name')
|
||||||
|
->maxLength(255),
|
||||||
|
Forms\Components\TextInput::make('model_name')
|
||||||
|
->maxLength(255),
|
||||||
|
Forms\Components\DatePicker::make('opened_date')
|
||||||
|
->label('Opening Date'),
|
||||||
|
Forms\Components\DatePicker::make('closed_date')
|
||||||
|
->label('Closing Date')
|
||||||
|
->after('opened_date'),
|
||||||
|
Forms\Components\Textarea::make('description')
|
||||||
|
->columnSpanFull(),
|
||||||
|
Forms\Components\Toggle::make('is_active')
|
||||||
|
->label('Active')
|
||||||
|
->default(true),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->columns([
|
||||||
|
Tables\Columns\TextColumn::make('name')
|
||||||
|
->sortable()
|
||||||
|
->searchable(),
|
||||||
|
Tables\Columns\TextColumn::make('manufacturer_name')
|
||||||
|
->sortable()
|
||||||
|
->searchable(),
|
||||||
|
Tables\Columns\TextColumn::make('opened_date')
|
||||||
|
->date()
|
||||||
|
->sortable(),
|
||||||
|
Tables\Columns\TextColumn::make('closed_date')
|
||||||
|
->date()
|
||||||
|
->sortable(),
|
||||||
|
Tables\Columns\IconColumn::make('is_active')
|
||||||
|
->boolean()
|
||||||
|
->sortable(),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
Tables\Filters\TrashedFilter::make(),
|
||||||
|
])
|
||||||
|
->headerActions([
|
||||||
|
Tables\Actions\CreateAction::make(),
|
||||||
|
])
|
||||||
|
->actions([
|
||||||
|
Tables\Actions\EditAction::make(),
|
||||||
|
Tables\Actions\DeleteAction::make(),
|
||||||
|
])
|
||||||
|
->bulkActions([
|
||||||
|
Tables\Actions\BulkActionGroup::make([
|
||||||
|
Tables\Actions\DeleteBulkAction::make(),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
47
app/Http/Kernel.php
Normal file
47
app/Http/Kernel.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||||
|
|
||||||
|
class Kernel extends HttpKernel
|
||||||
|
{
|
||||||
|
protected $middleware = [
|
||||||
|
\App\Http\Middleware\TrustProxies::class,
|
||||||
|
\Illuminate\Http\Middleware\HandleCors::class,
|
||||||
|
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||||
|
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||||
|
\App\Http\Middleware\TrimStrings::class,
|
||||||
|
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $middlewareGroups = [
|
||||||
|
'web' => [
|
||||||
|
\App\Http\Middleware\EncryptCookies::class,
|
||||||
|
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||||
|
\Illuminate\Session\Middleware\StartSession::class,
|
||||||
|
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||||
|
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||||
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
|
],
|
||||||
|
|
||||||
|
'api' => [
|
||||||
|
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
|
||||||
|
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $middlewareAliases = [
|
||||||
|
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||||
|
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||||
|
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||||
|
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||||
|
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||||
|
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||||
|
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
|
||||||
|
'precognitive' => \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
|
||||||
|
'signed' => \App\Http\Middleware\ValidateSignature::class,
|
||||||
|
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||||
|
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
17
app/Http/Middleware/Authenticate.php
Normal file
17
app/Http/Middleware/Authenticate.php
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Auth\Middleware\Authenticate as Middleware;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class Authenticate extends Middleware
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the path the user should be redirected to when they are not authenticated.
|
||||||
|
*/
|
||||||
|
protected function redirectTo(Request $request): ?string
|
||||||
|
{
|
||||||
|
return $request->expectsJson() ? null : route('login');
|
||||||
|
}
|
||||||
|
}
|
||||||
12
app/Http/Middleware/EncryptCookies.php
Normal file
12
app/Http/Middleware/EncryptCookies.php
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
|
||||||
|
|
||||||
|
class EncryptCookies extends Middleware
|
||||||
|
{
|
||||||
|
protected $except = [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
12
app/Http/Middleware/PreventRequestsDuringMaintenance.php
Normal file
12
app/Http/Middleware/PreventRequestsDuringMaintenance.php
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
|
||||||
|
|
||||||
|
class PreventRequestsDuringMaintenance extends Middleware
|
||||||
|
{
|
||||||
|
protected $except = [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
14
app/Http/Middleware/TrimStrings.php
Normal file
14
app/Http/Middleware/TrimStrings.php
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
|
||||||
|
|
||||||
|
class TrimStrings extends Middleware
|
||||||
|
{
|
||||||
|
protected $except = [
|
||||||
|
'current_password',
|
||||||
|
'password',
|
||||||
|
'password_confirmation',
|
||||||
|
];
|
||||||
|
}
|
||||||
17
app/Http/Middleware/TrustProxies.php
Normal file
17
app/Http/Middleware/TrustProxies.php
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Http\Middleware\TrustProxies as Middleware;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class TrustProxies extends Middleware
|
||||||
|
{
|
||||||
|
protected $proxies;
|
||||||
|
protected $headers =
|
||||||
|
Request::HEADER_X_FORWARDED_FOR |
|
||||||
|
Request::HEADER_X_FORWARDED_HOST |
|
||||||
|
Request::HEADER_X_FORWARDED_PORT |
|
||||||
|
Request::HEADER_X_FORWARDED_PROTO |
|
||||||
|
Request::HEADER_X_FORWARDED_AWS_ELB;
|
||||||
|
}
|
||||||
12
app/Http/Middleware/VerifyCsrfToken.php
Normal file
12
app/Http/Middleware/VerifyCsrfToken.php
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
|
||||||
|
|
||||||
|
class VerifyCsrfToken extends Middleware
|
||||||
|
{
|
||||||
|
protected $except = [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -2,26 +2,27 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Traits\HasSlugHistory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class Designer extends Model
|
class Designer extends Model
|
||||||
{
|
{
|
||||||
/**
|
use HasSlugHistory;
|
||||||
* The attributes that are mass assignable.
|
|
||||||
*
|
|
||||||
* @var array<string>
|
|
||||||
*/
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'name',
|
'name',
|
||||||
'slug',
|
'slug',
|
||||||
'bio',
|
'description',
|
||||||
|
'website',
|
||||||
|
'founded_date',
|
||||||
|
'headquarters',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'founded_date' => 'date',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* Boot the model.
|
|
||||||
*/
|
|
||||||
protected static function boot()
|
protected static function boot()
|
||||||
{
|
{
|
||||||
parent::boot();
|
parent::boot();
|
||||||
@@ -33,10 +34,7 @@ class Designer extends Model
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function rides()
|
||||||
* Get the rides designed by this designer.
|
|
||||||
*/
|
|
||||||
public function rides(): HasMany
|
|
||||||
{
|
{
|
||||||
return $this->hasMany(Ride::class);
|
return $this->hasMany(Ride::class);
|
||||||
}
|
}
|
||||||
|
|||||||
68
app/Policies/DesignerPolicy.php
Normal file
68
app/Policies/DesignerPolicy.php
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Policies;
|
||||||
|
|
||||||
|
use App\Models\Designer;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||||
|
|
||||||
|
class DesignerPolicy
|
||||||
|
{
|
||||||
|
use HandlesAuthorization;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can view any models.
|
||||||
|
*/
|
||||||
|
public function viewAny(User $user): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermissionTo('view designers');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can view the model.
|
||||||
|
*/
|
||||||
|
public function view(User $user, Designer $designer): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermissionTo('view designers');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can create models.
|
||||||
|
*/
|
||||||
|
public function create(User $user): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermissionTo('create designers');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can update the model.
|
||||||
|
*/
|
||||||
|
public function update(User $user, Designer $designer): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermissionTo('edit designers');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can delete the model.
|
||||||
|
*/
|
||||||
|
public function delete(User $user, Designer $designer): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermissionTo('delete designers');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can restore the model.
|
||||||
|
*/
|
||||||
|
public function restore(User $user, Designer $designer): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermissionTo('restore designers');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can permanently delete the model.
|
||||||
|
*/
|
||||||
|
public function forceDelete(User $user, Designer $designer): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermissionTo('force delete designers');
|
||||||
|
}
|
||||||
|
}
|
||||||
69
app/Providers/Filament/AdminPanelProvider.php
Normal file
69
app/Providers/Filament/AdminPanelProvider.php
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers\Filament;
|
||||||
|
|
||||||
|
use Filament\Http\Middleware\Authenticate;
|
||||||
|
use Filament\Http\Middleware\DisableBladeIconComponents;
|
||||||
|
use Filament\Http\Middleware\DispatchServingFilamentEvent;
|
||||||
|
use Filament\Pages;
|
||||||
|
use Filament\Panel;
|
||||||
|
use Filament\PanelProvider;
|
||||||
|
use Filament\Support\Colors\Color;
|
||||||
|
use Filament\Widgets;
|
||||||
|
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
||||||
|
use Illuminate\Cookie\Middleware\EncryptCookies;
|
||||||
|
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
|
||||||
|
use Illuminate\Routing\Middleware\SubstituteBindings;
|
||||||
|
use Illuminate\Session\Middleware\AuthenticateSession;
|
||||||
|
use Illuminate\Session\Middleware\StartSession;
|
||||||
|
use Illuminate\View\Middleware\ShareErrorsFromSession;
|
||||||
|
|
||||||
|
class AdminPanelProvider extends PanelProvider
|
||||||
|
{
|
||||||
|
public function panel(Panel $panel): Panel
|
||||||
|
{
|
||||||
|
return $panel
|
||||||
|
->default()
|
||||||
|
->id('admin')
|
||||||
|
->path('admin')
|
||||||
|
->login()
|
||||||
|
->colors([
|
||||||
|
'primary' => Color::Amber,
|
||||||
|
])
|
||||||
|
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
|
||||||
|
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
|
||||||
|
->pages([
|
||||||
|
Pages\Dashboard::class,
|
||||||
|
])
|
||||||
|
->widgets([
|
||||||
|
Widgets\AccountWidget::class,
|
||||||
|
Widgets\FilamentInfoWidget::class,
|
||||||
|
])
|
||||||
|
->middleware([
|
||||||
|
EncryptCookies::class,
|
||||||
|
AddQueuedCookiesToResponse::class,
|
||||||
|
StartSession::class,
|
||||||
|
AuthenticateSession::class,
|
||||||
|
ShareErrorsFromSession::class,
|
||||||
|
VerifyCsrfToken::class,
|
||||||
|
SubstituteBindings::class,
|
||||||
|
DisableBladeIconComponents::class,
|
||||||
|
DispatchServingFilamentEvent::class,
|
||||||
|
])
|
||||||
|
->authMiddleware([
|
||||||
|
Authenticate::class,
|
||||||
|
])
|
||||||
|
->resources([
|
||||||
|
config('filament.resources.namespace') => app_path('Filament/Resources'),
|
||||||
|
])
|
||||||
|
->navigationGroups([
|
||||||
|
'Company Management',
|
||||||
|
'Attractions',
|
||||||
|
'Parks',
|
||||||
|
'User Management',
|
||||||
|
'System',
|
||||||
|
])
|
||||||
|
->brandName('ThrillWiki Admin')
|
||||||
|
->sidebarCollapsibleOnDesktop();
|
||||||
|
}
|
||||||
|
}
|
||||||
64
app/Providers/Filament/ModerationPanelProvider.php
Normal file
64
app/Providers/Filament/ModerationPanelProvider.php
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Providers\Filament;
|
||||||
|
|
||||||
|
use Filament\Http\Middleware\Authenticate;
|
||||||
|
use Filament\Http\Middleware\DisableBladeIconComponents;
|
||||||
|
use Filament\Http\Middleware\DispatchServingFilamentEvent;
|
||||||
|
use Filament\Pages;
|
||||||
|
use Filament\Panel;
|
||||||
|
use Filament\PanelProvider;
|
||||||
|
use Filament\Support\Colors\Color;
|
||||||
|
use Filament\Widgets;
|
||||||
|
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
||||||
|
use Illuminate\Cookie\Middleware\EncryptCookies;
|
||||||
|
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
|
||||||
|
use Illuminate\Routing\Middleware\SubstituteBindings;
|
||||||
|
use Illuminate\Session\Middleware\AuthenticateSession;
|
||||||
|
use Illuminate\Session\Middleware\StartSession;
|
||||||
|
use Illuminate\View\Middleware\ShareErrorsFromSession;
|
||||||
|
|
||||||
|
class ModerationPanelProvider extends PanelProvider
|
||||||
|
{
|
||||||
|
public function panel(Panel $panel): Panel
|
||||||
|
{
|
||||||
|
return $panel
|
||||||
|
->id('moderation')
|
||||||
|
->path('moderation')
|
||||||
|
->login()
|
||||||
|
->colors([
|
||||||
|
'primary' => Color::Blue,
|
||||||
|
])
|
||||||
|
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
|
||||||
|
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
|
||||||
|
->pages([
|
||||||
|
Pages\Dashboard::class,
|
||||||
|
])
|
||||||
|
->widgets([
|
||||||
|
Widgets\AccountWidget::class,
|
||||||
|
])
|
||||||
|
->middleware([
|
||||||
|
EncryptCookies::class,
|
||||||
|
AddQueuedCookiesToResponse::class,
|
||||||
|
StartSession::class,
|
||||||
|
AuthenticateSession::class,
|
||||||
|
ShareErrorsFromSession::class,
|
||||||
|
VerifyCsrfToken::class,
|
||||||
|
SubstituteBindings::class,
|
||||||
|
DisableBladeIconComponents::class,
|
||||||
|
DispatchServingFilamentEvent::class,
|
||||||
|
])
|
||||||
|
->authMiddleware([
|
||||||
|
Authenticate::class,
|
||||||
|
])
|
||||||
|
->navigationGroups([
|
||||||
|
'Content Moderation',
|
||||||
|
'User Management',
|
||||||
|
'Reviews',
|
||||||
|
'Reports',
|
||||||
|
])
|
||||||
|
->brandName('ThrillWiki Moderation')
|
||||||
|
->sidebarCollapsibleOnDesktop()
|
||||||
|
->maxContentWidth('full');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,24 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Illuminate\Foundation\Application;
|
use Illuminate\Foundation\Application;
|
||||||
use Illuminate\Foundation\Configuration\Exceptions;
|
|
||||||
use Illuminate\Foundation\Configuration\Middleware;
|
|
||||||
|
|
||||||
return Application::configure(basePath: dirname(__DIR__))
|
$app = new Application(
|
||||||
->withRouting(
|
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
|
||||||
web: __DIR__.'/../routes/web.php',
|
);
|
||||||
commands: __DIR__.'/../routes/console.php',
|
|
||||||
health: '/up',
|
$app->singleton(
|
||||||
)
|
Illuminate\Contracts\Http\Kernel::class,
|
||||||
->withMiddleware(function (Middleware $middleware) {
|
App\Http\Kernel::class
|
||||||
//
|
);
|
||||||
})
|
|
||||||
->withExceptions(function (Exceptions $exceptions) {
|
$app->singleton(
|
||||||
//
|
Illuminate\Contracts\Console\Kernel::class,
|
||||||
})->create();
|
App\Console\Kernel::class
|
||||||
|
);
|
||||||
|
|
||||||
|
$app->singleton(
|
||||||
|
Illuminate\Contracts\Debug\ExceptionHandler::class,
|
||||||
|
App\Exceptions\Handler::class
|
||||||
|
);
|
||||||
|
|
||||||
|
return $app;
|
||||||
|
|||||||
@@ -2,4 +2,5 @@
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
App\Providers\AppServiceProvider::class,
|
App\Providers\AppServiceProvider::class,
|
||||||
|
App\Providers\Filament\AdminPanelProvider::class,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,29 +1,22 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://getcomposer.org/schema.json",
|
|
||||||
"name": "laravel/laravel",
|
|
||||||
"type": "project",
|
|
||||||
"description": "The skeleton application for the Laravel framework.",
|
|
||||||
"keywords": [
|
|
||||||
"laravel",
|
|
||||||
"framework"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.2",
|
"php": "^8.1",
|
||||||
"intervention/image": "^3.11",
|
"filament/filament": "^3.2",
|
||||||
"laravel/framework": "^11.31",
|
"filament/notifications": "^3.2",
|
||||||
"laravel/tinker": "^2.9",
|
"laravel/framework": "^10.10",
|
||||||
"livewire/livewire": "^3.5"
|
"laravel/sanctum": "^3.3",
|
||||||
|
"laravel/tinker": "^2.8",
|
||||||
|
"owen-it/laravel-auditing": "^13.5",
|
||||||
|
"spatie/laravel-permission": "^6.3"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"fakerphp/faker": "^1.23",
|
"fakerphp/faker": "^1.9.1",
|
||||||
"laravel/pail": "^1.1",
|
"laravel/pint": "^1.0",
|
||||||
"laravel/pint": "^1.13",
|
"laravel/sail": "^1.18",
|
||||||
"laravel/sail": "^1.26",
|
"mockery/mockery": "^1.4.4",
|
||||||
"mockery/mockery": "^1.6",
|
"nunomaduro/collision": "^7.0",
|
||||||
"nunomaduro/collision": "^8.1",
|
"phpunit/phpunit": "^10.1",
|
||||||
"pestphp/pest": "^3.7",
|
"spatie/laravel-ignition": "^2.0"
|
||||||
"pestphp/pest-plugin-laravel": "^3.1"
|
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
@@ -43,19 +36,14 @@
|
|||||||
"@php artisan package:discover --ansi"
|
"@php artisan package:discover --ansi"
|
||||||
],
|
],
|
||||||
"post-update-cmd": [
|
"post-update-cmd": [
|
||||||
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
|
"@php artisan vendor:publish --tag=laravel-assets --ansi --force",
|
||||||
|
"@php artisan filament:upgrade"
|
||||||
],
|
],
|
||||||
"post-root-package-install": [
|
"post-root-package-install": [
|
||||||
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
||||||
],
|
],
|
||||||
"post-create-project-cmd": [
|
"post-create-project-cmd": [
|
||||||
"@php artisan key:generate --ansi",
|
"@php artisan key:generate --ansi"
|
||||||
"@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"",
|
|
||||||
"@php artisan migrate --graceful --ansi"
|
|
||||||
],
|
|
||||||
"dev": [
|
|
||||||
"Composer\\Config::disableProcessTimeout",
|
|
||||||
"npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
|
|||||||
5113
composer.lock
generated
5113
composer.lock
generated
File diff suppressed because it is too large
Load Diff
202
config/permission.php
Normal file
202
config/permission.php
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
'models' => [
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When using the "HasPermissions" trait from this package, we need to know which
|
||||||
|
* Eloquent model should be used to retrieve your permissions. Of course, it
|
||||||
|
* is often just the "Permission" model but you may use whatever you like.
|
||||||
|
*
|
||||||
|
* The model you want to use as a Permission model needs to implement the
|
||||||
|
* `Spatie\Permission\Contracts\Permission` contract.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'permission' => Spatie\Permission\Models\Permission::class,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When using the "HasRoles" trait from this package, we need to know which
|
||||||
|
* Eloquent model should be used to retrieve your roles. Of course, it
|
||||||
|
* is often just the "Role" model but you may use whatever you like.
|
||||||
|
*
|
||||||
|
* The model you want to use as a Role model needs to implement the
|
||||||
|
* `Spatie\Permission\Contracts\Role` contract.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'role' => Spatie\Permission\Models\Role::class,
|
||||||
|
|
||||||
|
],
|
||||||
|
|
||||||
|
'table_names' => [
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When using the "HasRoles" trait from this package, we need to know which
|
||||||
|
* table should be used to retrieve your roles. We have chosen a basic
|
||||||
|
* default value but you may easily change it to any table you like.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'roles' => 'roles',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When using the "HasPermissions" trait from this package, we need to know which
|
||||||
|
* table should be used to retrieve your permissions. We have chosen a basic
|
||||||
|
* default value but you may easily change it to any table you like.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'permissions' => 'permissions',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When using the "HasPermissions" trait from this package, we need to know which
|
||||||
|
* table should be used to retrieve your models permissions. We have chosen a
|
||||||
|
* basic default value but you may easily change it to any table you like.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'model_has_permissions' => 'model_has_permissions',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When using the "HasRoles" trait from this package, we need to know which
|
||||||
|
* table should be used to retrieve your models roles. We have chosen a
|
||||||
|
* basic default value but you may easily change it to any table you like.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'model_has_roles' => 'model_has_roles',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When using the "HasRoles" trait from this package, we need to know which
|
||||||
|
* table should be used to retrieve your roles permissions. We have chosen a
|
||||||
|
* basic default value but you may easily change it to any table you like.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'role_has_permissions' => 'role_has_permissions',
|
||||||
|
],
|
||||||
|
|
||||||
|
'column_names' => [
|
||||||
|
/*
|
||||||
|
* Change this if you want to name the related pivots other than defaults
|
||||||
|
*/
|
||||||
|
'role_pivot_key' => null, // default 'role_id',
|
||||||
|
'permission_pivot_key' => null, // default 'permission_id',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Change this if you want to name the related model primary key other than
|
||||||
|
* `model_id`.
|
||||||
|
*
|
||||||
|
* For example, this would be nice if your primary keys are all UUIDs. In
|
||||||
|
* that case, name this `model_uuid`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'model_morph_key' => 'model_id',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Change this if you want to use the teams feature and your related model's
|
||||||
|
* foreign key is other than `team_id`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'team_foreign_key' => 'team_id',
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When set to true, the method for checking permissions will be registered on the gate.
|
||||||
|
* Set this to false if you want to implement custom logic for checking permissions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'register_permission_check_method' => true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When set to true, Laravel\Octane\Events\OperationTerminated event listener will be registered
|
||||||
|
* this will refresh permissions on every TickTerminated, TaskTerminated and RequestTerminated
|
||||||
|
* NOTE: This should not be needed in most cases, but an Octane/Vapor combination benefited from it.
|
||||||
|
*/
|
||||||
|
'register_octane_reset_listener' => false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Events will fire when a role or permission is assigned/unassigned:
|
||||||
|
* \Spatie\Permission\Events\RoleAttached
|
||||||
|
* \Spatie\Permission\Events\RoleDetached
|
||||||
|
* \Spatie\Permission\Events\PermissionAttached
|
||||||
|
* \Spatie\Permission\Events\PermissionDetached
|
||||||
|
*
|
||||||
|
* To enable, set to true, and then create listeners to watch these events.
|
||||||
|
*/
|
||||||
|
'events_enabled' => false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Teams Feature.
|
||||||
|
* When set to true the package implements teams using the 'team_foreign_key'.
|
||||||
|
* If you want the migrations to register the 'team_foreign_key', you must
|
||||||
|
* set this to true before doing the migration.
|
||||||
|
* If you already did the migration then you must make a new migration to also
|
||||||
|
* add 'team_foreign_key' to 'roles', 'model_has_roles', and 'model_has_permissions'
|
||||||
|
* (view the latest version of this package's migration file)
|
||||||
|
*/
|
||||||
|
|
||||||
|
'teams' => false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The class to use to resolve the permissions team id
|
||||||
|
*/
|
||||||
|
'team_resolver' => \Spatie\Permission\DefaultTeamResolver::class,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Passport Client Credentials Grant
|
||||||
|
* When set to true the package will use Passports Client to check permissions
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use_passport_client_credentials' => false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When set to true, the required permission names are added to exception messages.
|
||||||
|
* This could be considered an information leak in some contexts, so the default
|
||||||
|
* setting is false here for optimum safety.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'display_permission_in_exception' => false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When set to true, the required role names are added to exception messages.
|
||||||
|
* This could be considered an information leak in some contexts, so the default
|
||||||
|
* setting is false here for optimum safety.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'display_role_in_exception' => false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* By default wildcard permission lookups are disabled.
|
||||||
|
* See documentation to understand supported syntax.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'enable_wildcard_permission' => false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The class to use for interpreting wildcard permissions.
|
||||||
|
* If you need to modify delimiters, override the class and specify its name here.
|
||||||
|
*/
|
||||||
|
// 'permission.wildcard_permission' => Spatie\Permission\WildcardPermission::class,
|
||||||
|
|
||||||
|
/* Cache-specific settings */
|
||||||
|
|
||||||
|
'cache' => [
|
||||||
|
|
||||||
|
/*
|
||||||
|
* By default all permissions are cached for 24 hours to speed up performance.
|
||||||
|
* When permissions or roles are updated the cache is flushed automatically.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'expiration_time' => \DateInterval::createFromDateString('24 hours'),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The cache key used to store all permissions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'key' => 'spatie.permission.cache',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* You may optionally indicate a specific cache driver to use for permission and
|
||||||
|
* role caching using any of the `store` drivers listed in the cache.php config
|
||||||
|
* file. Using 'default' here means to use the `default` set in cache.php.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'store' => 'default',
|
||||||
|
],
|
||||||
|
];
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('designers', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('name');
|
||||||
|
$table->string('slug')->unique();
|
||||||
|
$table->text('description')->nullable();
|
||||||
|
$table->string('website')->nullable();
|
||||||
|
$table->date('founded_date')->nullable();
|
||||||
|
$table->string('headquarters')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('designers');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
$teams = config('permission.teams');
|
||||||
|
$tableNames = config('permission.table_names');
|
||||||
|
$columnNames = config('permission.column_names');
|
||||||
|
$pivotRole = $columnNames['role_pivot_key'] ?? 'role_id';
|
||||||
|
$pivotPermission = $columnNames['permission_pivot_key'] ?? 'permission_id';
|
||||||
|
|
||||||
|
if (empty($tableNames)) {
|
||||||
|
throw new \Exception('Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.');
|
||||||
|
}
|
||||||
|
if ($teams && empty($columnNames['team_foreign_key'] ?? null)) {
|
||||||
|
throw new \Exception('Error: team_foreign_key on config/permission.php not loaded. Run [php artisan config:clear] and try again.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema::create($tableNames['permissions'], static function (Blueprint $table) {
|
||||||
|
// $table->engine('InnoDB');
|
||||||
|
$table->bigIncrements('id'); // permission id
|
||||||
|
$table->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format)
|
||||||
|
$table->string('guard_name'); // For MyISAM use string('guard_name', 25);
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->unique(['name', 'guard_name']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create($tableNames['roles'], static function (Blueprint $table) use ($teams, $columnNames) {
|
||||||
|
// $table->engine('InnoDB');
|
||||||
|
$table->bigIncrements('id'); // role id
|
||||||
|
if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing
|
||||||
|
$table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable();
|
||||||
|
$table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index');
|
||||||
|
}
|
||||||
|
$table->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format)
|
||||||
|
$table->string('guard_name'); // For MyISAM use string('guard_name', 25);
|
||||||
|
$table->timestamps();
|
||||||
|
if ($teams || config('permission.testing')) {
|
||||||
|
$table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']);
|
||||||
|
} else {
|
||||||
|
$table->unique(['name', 'guard_name']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create($tableNames['model_has_permissions'], static function (Blueprint $table) use ($tableNames, $columnNames, $pivotPermission, $teams) {
|
||||||
|
$table->unsignedBigInteger($pivotPermission);
|
||||||
|
|
||||||
|
$table->string('model_type');
|
||||||
|
$table->unsignedBigInteger($columnNames['model_morph_key']);
|
||||||
|
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index');
|
||||||
|
|
||||||
|
$table->foreign($pivotPermission)
|
||||||
|
->references('id') // permission id
|
||||||
|
->on($tableNames['permissions'])
|
||||||
|
->onDelete('cascade');
|
||||||
|
if ($teams) {
|
||||||
|
$table->unsignedBigInteger($columnNames['team_foreign_key']);
|
||||||
|
$table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index');
|
||||||
|
|
||||||
|
$table->primary([$columnNames['team_foreign_key'], $pivotPermission, $columnNames['model_morph_key'], 'model_type'],
|
||||||
|
'model_has_permissions_permission_model_type_primary');
|
||||||
|
} else {
|
||||||
|
$table->primary([$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
|
||||||
|
'model_has_permissions_permission_model_type_primary');
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create($tableNames['model_has_roles'], static function (Blueprint $table) use ($tableNames, $columnNames, $pivotRole, $teams) {
|
||||||
|
$table->unsignedBigInteger($pivotRole);
|
||||||
|
|
||||||
|
$table->string('model_type');
|
||||||
|
$table->unsignedBigInteger($columnNames['model_morph_key']);
|
||||||
|
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index');
|
||||||
|
|
||||||
|
$table->foreign($pivotRole)
|
||||||
|
->references('id') // role id
|
||||||
|
->on($tableNames['roles'])
|
||||||
|
->onDelete('cascade');
|
||||||
|
if ($teams) {
|
||||||
|
$table->unsignedBigInteger($columnNames['team_foreign_key']);
|
||||||
|
$table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index');
|
||||||
|
|
||||||
|
$table->primary([$columnNames['team_foreign_key'], $pivotRole, $columnNames['model_morph_key'], 'model_type'],
|
||||||
|
'model_has_roles_role_model_type_primary');
|
||||||
|
} else {
|
||||||
|
$table->primary([$pivotRole, $columnNames['model_morph_key'], 'model_type'],
|
||||||
|
'model_has_roles_role_model_type_primary');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create($tableNames['role_has_permissions'], static function (Blueprint $table) use ($tableNames, $pivotRole, $pivotPermission) {
|
||||||
|
$table->unsignedBigInteger($pivotPermission);
|
||||||
|
$table->unsignedBigInteger($pivotRole);
|
||||||
|
|
||||||
|
$table->foreign($pivotPermission)
|
||||||
|
->references('id') // permission id
|
||||||
|
->on($tableNames['permissions'])
|
||||||
|
->onDelete('cascade');
|
||||||
|
|
||||||
|
$table->foreign($pivotRole)
|
||||||
|
->references('id') // role id
|
||||||
|
->on($tableNames['roles'])
|
||||||
|
->onDelete('cascade');
|
||||||
|
|
||||||
|
$table->primary([$pivotPermission, $pivotRole], 'role_has_permissions_permission_id_role_id_primary');
|
||||||
|
});
|
||||||
|
|
||||||
|
app('cache')
|
||||||
|
->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null)
|
||||||
|
->forget(config('permission.cache.key'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
$tableNames = config('permission.table_names');
|
||||||
|
|
||||||
|
if (empty($tableNames)) {
|
||||||
|
throw new \Exception('Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.');
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema::drop($tableNames['role_has_permissions']);
|
||||||
|
Schema::drop($tableNames['model_has_roles']);
|
||||||
|
Schema::drop($tableNames['model_has_permissions']);
|
||||||
|
Schema::drop($tableNames['roles']);
|
||||||
|
Schema::drop($tableNames['permissions']);
|
||||||
|
}
|
||||||
|
};
|
||||||
46
database/seeders/DesignerPermissionsSeeder.php
Normal file
46
database/seeders/DesignerPermissionsSeeder.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
use Spatie\Permission\Models\Permission;
|
||||||
|
use Spatie\Permission\Models\Role;
|
||||||
|
|
||||||
|
class DesignerPermissionsSeeder extends Seeder
|
||||||
|
{
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
// Create designer permissions
|
||||||
|
$permissions = [
|
||||||
|
'view designers',
|
||||||
|
'create designers',
|
||||||
|
'edit designers',
|
||||||
|
'delete designers',
|
||||||
|
'restore designers',
|
||||||
|
'force delete designers',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($permissions as $permission) {
|
||||||
|
Permission::create(['name' => $permission]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign permissions to admin role
|
||||||
|
$adminRole = Role::firstOrCreate(['name' => 'admin']);
|
||||||
|
$adminRole->givePermissionTo($permissions);
|
||||||
|
|
||||||
|
// Assign permissions to moderator role
|
||||||
|
$moderatorRole = Role::firstOrCreate(['name' => 'moderator']);
|
||||||
|
$moderatorRole->givePermissionTo([
|
||||||
|
'view designers',
|
||||||
|
'edit designers',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Assign permissions to editor role
|
||||||
|
$editorRole = Role::firstOrCreate(['name' => 'editor']);
|
||||||
|
$editorRole->givePermissionTo([
|
||||||
|
'view designers',
|
||||||
|
'create designers',
|
||||||
|
'edit designers',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
108
handoffs/2-filament-admin-setup.md
Normal file
108
handoffs/2-filament-admin-setup.md
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
# Filament Admin Interface Implementation Plan
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
[2025-02-26 20:14]
|
||||||
|
|
||||||
|
### Goals
|
||||||
|
1. Set up Filament PHP admin interface
|
||||||
|
2. Match Django admin capabilities
|
||||||
|
3. Implement permission system
|
||||||
|
4. Create moderation tools
|
||||||
|
|
||||||
|
### Technical Requirements
|
||||||
|
1. Feature Parity
|
||||||
|
- Match Django admin functionality
|
||||||
|
- Maintain consistent permission structure
|
||||||
|
- Support equivalent bulk actions
|
||||||
|
- Replicate list display and filters
|
||||||
|
|
||||||
|
2. Resource Mapping
|
||||||
|
- Map Django admin models to Filament resources
|
||||||
|
- Match fieldset structures
|
||||||
|
- Implement inline form relationships
|
||||||
|
- Support all Django admin actions
|
||||||
|
|
||||||
|
3. Permissions
|
||||||
|
- Integrate with Laravel permissions
|
||||||
|
- Match Django's permission model
|
||||||
|
- Support resource-level access control
|
||||||
|
- Implement audit trails
|
||||||
|
|
||||||
|
## Implementation Strategy
|
||||||
|
|
||||||
|
### Phase 1: Core Setup
|
||||||
|
1. Install Filament PHP
|
||||||
|
2. Configure basic admin panel
|
||||||
|
3. Set up resource structure
|
||||||
|
4. Implement authentication integration
|
||||||
|
|
||||||
|
### Phase 2: Resource Implementation
|
||||||
|
1. Create base resource templates
|
||||||
|
2. Map model relationships
|
||||||
|
3. Configure form layouts
|
||||||
|
4. Set up list views
|
||||||
|
|
||||||
|
### Phase 3: Permissions
|
||||||
|
1. Design permission structure
|
||||||
|
2. Implement role system
|
||||||
|
3. Configure access controls
|
||||||
|
4. Set up audit logging
|
||||||
|
|
||||||
|
### Phase 4: Moderation Tools
|
||||||
|
1. Create moderation panel
|
||||||
|
2. Implement review workflows
|
||||||
|
3. Set up notification system
|
||||||
|
4. Configure action logging
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
### Django Admin Reference
|
||||||
|
- Location: //Users/talor/thrillwiki_django_no_react/
|
||||||
|
- Study admin configuration
|
||||||
|
- Map model relationships
|
||||||
|
- Document customizations
|
||||||
|
|
||||||
|
### Laravel Implementation
|
||||||
|
1. File Structure:
|
||||||
|
```
|
||||||
|
app/Filament/Resources/
|
||||||
|
├── UserResource.php
|
||||||
|
├── ParkResource.php
|
||||||
|
├── RideResource.php
|
||||||
|
└── ReviewResource.php
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Key Components:
|
||||||
|
- Resource classes
|
||||||
|
- Form builders
|
||||||
|
- Table configurations
|
||||||
|
- Action handlers
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
1. Required Packages
|
||||||
|
- filament/filament
|
||||||
|
- laravel/permissions
|
||||||
|
- filament/notifications
|
||||||
|
|
||||||
|
2. Configuration Files
|
||||||
|
- config/filament.php
|
||||||
|
- config/permissions.php
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. Immediate Actions
|
||||||
|
- Install Filament package
|
||||||
|
- Create base resource structure
|
||||||
|
- Configure authentication
|
||||||
|
|
||||||
|
2. Planning Needed
|
||||||
|
- Resource organization strategy
|
||||||
|
- Permission hierarchy design
|
||||||
|
- Moderation workflow structure
|
||||||
|
|
||||||
|
## Notes and Considerations
|
||||||
|
- Follow Laravel/Filament naming conventions
|
||||||
|
- Maintain clear separation of concerns
|
||||||
|
- Document any deviations from Django
|
||||||
|
- Consider performance optimizations
|
||||||
@@ -1,117 +1,36 @@
|
|||||||
## Current Session Context
|
## Current Session Context
|
||||||
[2025-02-26 20:07] - Documentation System Enhancement
|
[2025-02-26 20:41]
|
||||||
|
|
||||||
## Recent Changes
|
### Recent Changes
|
||||||
1. Enhanced Project Documentation System
|
- Implemented Designer model and admin interface
|
||||||
- Implemented Handoffs System alongside Memory Bank
|
- Created admin and moderation panel separation
|
||||||
- Created handoffs directory structure
|
- Set up role-based permissions system
|
||||||
- Set up instruction templates
|
- Added relationship management for rides
|
||||||
- Documented integration approach
|
- Configured resource layouts and validations
|
||||||
|
|
||||||
2. Documentation Structure
|
### Current Progress
|
||||||
- Created handoffs/0-instructions/
|
- Completed core Designer implementation
|
||||||
* 0-intro.md: System overview
|
- Established permission structure
|
||||||
* H-handoff-prompt.md: Handoff processing
|
- Set up admin panel configuration
|
||||||
* M-milestone-prompt.md: Milestone integration
|
- Created moderation interface foundation
|
||||||
|
|
||||||
3. Architectural Decisions
|
### Next Tasks
|
||||||
- Documented handoff system integration in decisionLog.md
|
1. History Tracking
|
||||||
- Established clear triggers for handoffs and milestones
|
- Set up model history tracking
|
||||||
- Defined complementary roles with Memory Bank
|
- Configure audit logging
|
||||||
|
- Implement change tracking
|
||||||
|
|
||||||
## Current Goals
|
2. Moderation Tools
|
||||||
1. Documentation System Integration
|
- Create content review workflow
|
||||||
- Create first handoff document for current progress
|
- Add moderation queue
|
||||||
- Establish first milestone for completed features
|
- Set up notification system
|
||||||
- Train team on handoff creation guidelines
|
|
||||||
- Set up trigger points for new handoffs
|
|
||||||
|
|
||||||
2. Process Implementation
|
3. Testing & Documentation
|
||||||
- Document handoff triggers for key development points
|
- Write feature tests
|
||||||
- Define milestone creation criteria
|
- Document permission structure
|
||||||
- Establish review process for handoffs
|
- Create admin guide
|
||||||
- Create content quality guidelines
|
|
||||||
|
|
||||||
3. Team Training
|
### Open Questions
|
||||||
- Share documentation system overview
|
1. Should we implement soft deletes for Designers?
|
||||||
- Train on handoff creation process
|
2. How should we handle duplicate designer names?
|
||||||
- Establish milestone review procedures
|
3. Do we need version control for designer descriptions?
|
||||||
- Document best practices
|
|
||||||
|
|
||||||
## Open Questions
|
|
||||||
1. Process Integration
|
|
||||||
- How to streamline handoff creation during development?
|
|
||||||
- Best approach for milestone consolidation?
|
|
||||||
- Optimal timing for creating new handoffs?
|
|
||||||
|
|
||||||
2. Documentation Quality
|
|
||||||
- What level of detail is required for handoffs?
|
|
||||||
- How to ensure consistent milestone quality?
|
|
||||||
- What metrics to track for system effectiveness?
|
|
||||||
|
|
||||||
3. System Optimization
|
|
||||||
- How to balance detail vs. conciseness?
|
|
||||||
- When to clean up older handoffs?
|
|
||||||
- How to measure documentation impact?
|
|
||||||
|
|
||||||
|
|
||||||
## Recent Changes
|
|
||||||
1. Completed comprehensive project analysis
|
|
||||||
- Documented implemented features
|
|
||||||
- Identified missing components
|
|
||||||
- Analyzed dependencies
|
|
||||||
- Established priorities
|
|
||||||
|
|
||||||
2. Created ProjectAnalysis.md with:
|
|
||||||
- Current implementation status
|
|
||||||
- Missing features
|
|
||||||
- Priority implementation order
|
|
||||||
- Technical considerations
|
|
||||||
- Risk areas
|
|
||||||
- Recommendations
|
|
||||||
|
|
||||||
## Current Goals
|
|
||||||
1. High Priority Tasks
|
|
||||||
- Filament Admin Interface Implementation
|
|
||||||
* Set up core admin resources
|
|
||||||
* Configure permission system
|
|
||||||
* Implement moderation tools
|
|
||||||
- History Tracking System
|
|
||||||
* Model history implementation
|
|
||||||
* Audit logging
|
|
||||||
* User activity tracking
|
|
||||||
- Email Service Foundation
|
|
||||||
* Basic notification system
|
|
||||||
* Template management
|
|
||||||
|
|
||||||
2. Documentation Needs
|
|
||||||
- Document Filament integration plan
|
|
||||||
- Create admin system architecture docs
|
|
||||||
- Update component documentation
|
|
||||||
- Track feature parity progress
|
|
||||||
|
|
||||||
3. Technical Setup
|
|
||||||
- Configure Filament PHP
|
|
||||||
- Set up history tracking system
|
|
||||||
- Establish email service infrastructure
|
|
||||||
|
|
||||||
## Open Questions
|
|
||||||
1. Filament Integration
|
|
||||||
- How to match Django admin customizations?
|
|
||||||
- Best approach for permission mapping?
|
|
||||||
- Strategy for bulk actions?
|
|
||||||
|
|
||||||
2. History Tracking
|
|
||||||
- Most efficient way to track model changes?
|
|
||||||
- How to handle user contributions?
|
|
||||||
- Audit log storage approach?
|
|
||||||
|
|
||||||
3. Architecture Decisions
|
|
||||||
- Best way to structure Filament resources?
|
|
||||||
- Email queue management strategy?
|
|
||||||
- Analytics data organization?
|
|
||||||
|
|
||||||
4. Integration Points
|
|
||||||
- How to connect history with wiki system?
|
|
||||||
- Analytics integration approach?
|
|
||||||
- Company relationship tracking?
|
|
||||||
@@ -10,6 +10,37 @@
|
|||||||
- Enhanced project documentation strategy
|
- Enhanced project documentation strategy
|
||||||
- Established clear documentation workflows
|
- Established clear documentation workflows
|
||||||
|
|
||||||
|
### Filament Admin Interface Setup [2025-02-26 20:22]
|
||||||
|
- Installed core Filament packages (filament/filament:^3.2)
|
||||||
|
- Added permissions package (spatie/laravel-permission:^6.3)
|
||||||
|
- Installed notification system (filament/notifications:^3.2)
|
||||||
|
- Published Filament configuration and assets
|
||||||
|
- Created admin panel provider
|
||||||
|
- Published permission migrations and config
|
||||||
|
- Set up base admin panel structure
|
||||||
|
|
||||||
|
### Permission System Implementation [2025-02-26 20:39]
|
||||||
|
- Created DesignerPolicy with granular permissions
|
||||||
|
- Implemented role-based access control
|
||||||
|
- Set up permission seeder with default roles
|
||||||
|
- Added modular permission structure
|
||||||
|
- Prepared for audit trail integration
|
||||||
|
|
||||||
|
### Admin Panel Configuration [2025-02-26 20:38]
|
||||||
|
- Set up AdminPanelProvider with proper structure
|
||||||
|
- Configured navigation groups for different sections
|
||||||
|
- Added branding and UI customization
|
||||||
|
- Set up middleware and authentication
|
||||||
|
- Prepared structure for multiple admin panels
|
||||||
|
|
||||||
|
### Designer Resource Implementation [2025-02-26 20:37]
|
||||||
|
- Created Designer model with slug history support
|
||||||
|
- Implemented Filament resource with form layouts
|
||||||
|
- Added relationship management for rides
|
||||||
|
- Set up proper validations and filters
|
||||||
|
- Configured computed columns and bulk actions
|
||||||
|
- Added URL and date handling
|
||||||
|
|
||||||
### Project Analysis and Gap Assessment [2025-02-26]
|
### Project Analysis and Gap Assessment [2025-02-26]
|
||||||
- Completed comprehensive project analysis
|
- Completed comprehensive project analysis
|
||||||
- Identified implemented vs missing features
|
- Identified implemented vs missing features
|
||||||
|
|||||||
1
public/css/filament/filament/app.css
Normal file
1
public/css/filament/filament/app.css
Normal file
File diff suppressed because one or more lines are too long
49
public/css/filament/forms/forms.css
Normal file
49
public/css/filament/forms/forms.css
Normal file
File diff suppressed because one or more lines are too long
1
public/css/filament/support/support.css
Normal file
1
public/css/filament/support/support.css
Normal file
@@ -0,0 +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}
|
||||||
1
public/js/filament/filament/app.js
Normal file
1
public/js/filament/filament/app.js
Normal file
File diff suppressed because one or more lines are too long
13
public/js/filament/filament/echo.js
Normal file
13
public/js/filament/filament/echo.js
Normal file
File diff suppressed because one or more lines are too long
1
public/js/filament/forms/components/color-picker.js
Normal file
1
public/js/filament/forms/components/color-picker.js
Normal file
File diff suppressed because one or more lines are too long
1
public/js/filament/forms/components/date-time-picker.js
Normal file
1
public/js/filament/forms/components/date-time-picker.js
Normal file
File diff suppressed because one or more lines are too long
123
public/js/filament/forms/components/file-upload.js
Normal file
123
public/js/filament/forms/components/file-upload.js
Normal file
File diff suppressed because one or more lines are too long
1
public/js/filament/forms/components/key-value.js
Normal file
1
public/js/filament/forms/components/key-value.js
Normal file
@@ -0,0 +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};
|
||||||
51
public/js/filament/forms/components/markdown-editor.js
Normal file
51
public/js/filament/forms/components/markdown-editor.js
Normal file
File diff suppressed because one or more lines are too long
150
public/js/filament/forms/components/rich-editor.js
Normal file
150
public/js/filament/forms/components/rich-editor.js
Normal file
File diff suppressed because one or more lines are too long
6
public/js/filament/forms/components/select.js
Normal file
6
public/js/filament/forms/components/select.js
Normal file
File diff suppressed because one or more lines are too long
1
public/js/filament/forms/components/tags-input.js
Normal file
1
public/js/filament/forms/components/tags-input.js
Normal file
@@ -0,0 +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};
|
||||||
1
public/js/filament/forms/components/textarea.js
Normal file
1
public/js/filament/forms/components/textarea.js
Normal file
@@ -0,0 +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};
|
||||||
1
public/js/filament/notifications/notifications.js
Normal file
1
public/js/filament/notifications/notifications.js
Normal file
File diff suppressed because one or more lines are too long
46
public/js/filament/support/support.js
Normal file
46
public/js/filament/support/support.js
Normal file
File diff suppressed because one or more lines are too long
1
public/js/filament/tables/components/table.js
Normal file
1
public/js/filament/tables/components/table.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
function n(){return{checkboxClickController:null,collapsedGroups:[],isLoading:!1,selectedRecords:[],shouldCheckUniqueSelection:!0,lastCheckedRecord:null,livewireId:null,init:function(){this.livewireId=this.$root.closest("[wire\\:id]").attributes["wire:id"].value,this.$wire.$on("deselectAllTableRecords",()=>this.deselectAllRecords()),this.$watch("selectedRecords",()=>{if(!this.shouldCheckUniqueSelection){this.shouldCheckUniqueSelection=!0;return}this.selectedRecords=[...new Set(this.selectedRecords)],this.shouldCheckUniqueSelection=!1}),this.$nextTick(()=>this.watchForCheckboxClicks()),Livewire.hook("element.init",({component:e})=>{e.id===this.livewireId&&this.watchForCheckboxClicks()})},mountAction:function(e,t=null){this.$wire.set("selectedTableRecords",this.selectedRecords,!1),this.$wire.mountTableAction(e,t)},mountBulkAction:function(e){this.$wire.set("selectedTableRecords",this.selectedRecords,!1),this.$wire.mountTableBulkAction(e)},toggleSelectRecordsOnPage:function(){let e=this.getRecordsOnPage();if(this.areRecordsSelected(e)){this.deselectRecords(e);return}this.selectRecords(e)},toggleSelectRecordsInGroup:async function(e){if(this.isLoading=!0,this.areRecordsSelected(this.getRecordsInGroupOnPage(e))){this.deselectRecords(await this.$wire.getGroupedSelectableTableRecordKeys(e));return}this.selectRecords(await this.$wire.getGroupedSelectableTableRecordKeys(e)),this.isLoading=!1},getRecordsInGroupOnPage:function(e){let t=[];for(let s of this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[])s.dataset.group===e&&t.push(s.value);return t},getRecordsOnPage:function(){let e=[];for(let t of this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[])e.push(t.value);return e},selectRecords:function(e){for(let t of e)this.isRecordSelected(t)||this.selectedRecords.push(t)},deselectRecords:function(e){for(let t of e){let s=this.selectedRecords.indexOf(t);s!==-1&&this.selectedRecords.splice(s,1)}},selectAllRecords:async function(){this.isLoading=!0,this.selectedRecords=await this.$wire.getAllSelectableTableRecordKeys(),this.isLoading=!1},deselectAllRecords:function(){this.selectedRecords=[]},isRecordSelected:function(e){return this.selectedRecords.includes(e)},areRecordsSelected:function(e){return e.every(t=>this.isRecordSelected(t))},toggleCollapseGroup:function(e){if(this.isGroupCollapsed(e)){this.collapsedGroups.splice(this.collapsedGroups.indexOf(e),1);return}this.collapsedGroups.push(e)},isGroupCollapsed:function(e){return this.collapsedGroups.includes(e)},resetCollapsedGroups:function(){this.collapsedGroups=[]},watchForCheckboxClicks:function(){this.checkboxClickController&&this.checkboxClickController.abort(),this.checkboxClickController=new AbortController;let{signal:e}=this.checkboxClickController;this.$root?.addEventListener("click",t=>t.target?.matches(".fi-ta-record-checkbox")&&this.handleCheckboxClick(t,t.target),{signal:e})},handleCheckboxClick:function(e,t){if(!this.lastChecked){this.lastChecked=t;return}if(e.shiftKey){let s=Array.from(this.$root?.getElementsByClassName("fi-ta-record-checkbox")??[]);if(!s.includes(this.lastChecked)){this.lastChecked=t;return}let o=s.indexOf(this.lastChecked),r=s.indexOf(t),l=[o,r].sort((i,d)=>i-d),c=[];for(let i=l[0];i<=l[1];i++)s[i].checked=t.checked,c.push(s[i].value);t.checked?this.selectRecords(c):this.deselectRecords(c)}this.lastChecked=t}}}export{n as default};
|
||||||
37
public/js/filament/widgets/components/chart.js
Normal file
37
public/js/filament/widgets/components/chart.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user