From c631f1f2c05e4c5aad879f6eceff886f3488688b Mon Sep 17 00:00:00 2001 From: dhanabalan Date: Mon, 24 Mar 2025 17:23:01 +0530 Subject: [PATCH] Masters and Transaction changes --- app/Filament/Exports/ItemExporter.php | 38 ++ app/Filament/Imports/ItemImporter.php | 56 +++ .../Imports/ProductionQuantityImporter.php | 91 +++++ app/Filament/Pages/Dashboard.php | 26 ++ app/Filament/Resources/BlockResource.php | 102 +++++ .../BlockResource/Pages/CreateBlock.php | 12 + .../BlockResource/Pages/EditBlock.php | 22 + .../BlockResource/Pages/ListBlocks.php | 19 + .../BlockResource/Pages/ViewBlock.php | 19 + .../BlocksRelationManager.php | 50 +++ app/Filament/Resources/CompanyResource.php | 12 +- app/Filament/Resources/ItemResource.php | 120 ++++++ .../ItemResource/Pages/CreateItem.php | 12 + .../Resources/ItemResource/Pages/EditItem.php | 22 + .../ItemResource/Pages/ListItems.php | 19 + .../Resources/ItemResource/Pages/ViewItem.php | 19 + app/Filament/Resources/LineResource.php | 117 ++++++ .../LineResource/Pages/CreateLine.php | 12 + .../Resources/LineResource/Pages/EditLine.php | 22 + .../LineResource/Pages/ListLines.php | 19 + .../Resources/LineResource/Pages/ViewLine.php | 19 + app/Filament/Resources/LineStopResource.php | 117 ++++++ .../LineStopResource/Pages/CreateLineStop.php | 12 + .../LineStopResource/Pages/EditLineStop.php | 22 + .../LineStopResource/Pages/ListLineStops.php | 19 + .../LineStopResource/Pages/ViewLineStop.php | 19 + app/Filament/Resources/PlantResource.php | 13 +- .../Resources/ProductionLineStopResource.php | 144 +++++++ .../Pages/CreateProductionLineStop.php | 12 + .../Pages/EditProductionLineStop.php | 22 + .../Pages/ListProductionLineStops.php | 19 + .../Pages/ViewProductionLineStop.php | 19 + .../Resources/ProductionPlanResource.php | 122 ++++++ .../Pages/CreateProductionPlan.php | 12 + .../Pages/EditProductionPlan.php | 22 + .../Pages/ListProductionPlans.php | 19 + .../Pages/ViewProductionPlan.php | 19 + .../Resources/ProductionQuantityResource.php | 142 +++++++ .../Pages/CreateProductionQuantity.php | 12 + .../Pages/EditProductionQuantity.php | 22 + .../Pages/ListProductionQuantities.php | 19 + .../Pages/ViewProductionQuantity.php | 19 + app/Filament/Resources/ShiftResource.php | 120 ++++++ .../ShiftResource/Pages/CreateShift.php | 12 + .../ShiftResource/Pages/EditShift.php | 22 + .../ShiftResource/Pages/ListShifts.php | 19 + .../ShiftResource/Pages/ViewShift.php | 19 + app/Filament/Widgets/ItemOverview.php | 105 +++++ app/Models/Block.php | 33 ++ app/Models/Item.php | 17 + app/Models/Line.php | 42 ++ app/Models/LineStop.php | 36 ++ app/Models/Plant.php | 6 + app/Models/ProductionLineStop.php | 50 +++ app/Models/ProductionPlan.php | 35 ++ app/Models/ProductionQuantity.php | 43 ++ app/Models/Shift.php | 36 ++ app/Models/User.php | 3 +- app/Policies/BlockPolicy.php | 65 +++ app/Providers/AppServiceProvider.php | 5 +- app/Providers/Filament/AdminPanelProvider.php | 17 +- composer.json | 1 + composer.lock | 148 ++++++- config/filament-spatie-roles-permissions.php | 218 ++++++++++ config/filament.php | 89 ++++ config/permission.php | 202 ++++++++++ ...25_03_19_161954_create_companies_table.php | 2 +- .../2025_03_19_171555_create_plants_table.php | 7 +- .../2025_03_20_115918_create_blocks_table.php | 39 ++ .../2025_03_20_125247_create_shifts_table.php | 45 +++ .../2025_03_21_090348_create_items_table.php | 47 +++ ...3453_add_status_column_to_shifts_table.php | 29 ++ ...3_21_115008_create_notifications_table.php | 31 ++ ...2025_03_21_115025_create_imports_table.php | 35 ++ ...2025_03_21_115026_create_exports_table.php | 35 ++ ...115027_create_failed_import_rows_table.php | 30 ++ ..._03_21_124542_create_permission_tables.php | 140 +++++++ ...5_03_22_184847_create_line_stops_table.php | 46 +++ .../2025_03_22_190920_create_lines_table.php | 45 +++ ...3_113936_create_production_plans_table.php | 44 ++ ...259_create_production_line_stops_table.php | 52 +++ ...155_create_production_quantities_table.php | 48 +++ database/seeders/DatabaseSeeder.php | 9 +- database/seeders/RoleSeeder.php | 20 + database/seeders/UserSeeder.php | 33 ++ public/assets | 1 + .../views/vendor/filament/assets.blade.php | 17 + .../filament/components/actions.blade.php | 64 +++ .../filament/components/avatar.blade.php | 21 + .../filament/components/badge.blade.php | 238 +++++++++++ .../filament/components/breadcrumbs.blade.php | 50 +++ .../components/button/group.blade.php | 9 + .../components/button/index.blade.php | 332 +++++++++++++++ .../vendor/filament/components/card.blade.php | 3 + .../components/dropdown/header.blade.php | 71 ++++ .../components/dropdown/index.blade.php | 87 ++++ .../components/dropdown/list/index.blade.php | 3 + .../components/dropdown/list/item.blade.php | 275 +++++++++++++ .../filament/components/fieldset.blade.php | 25 ++ .../filament/components/grid/column.blade.php | 62 +++ .../filament/components/grid/index.blade.php | 53 +++ .../filament/components/icon-button.blade.php | 242 +++++++++++ .../vendor/filament/components/icon.blade.php | 29 ++ .../components/input/checkbox.blade.php | 29 ++ .../filament/components/input/index.blade.php | 22 + .../filament/components/input/radio.blade.php | 15 + .../components/input/select.blade.php | 16 + .../components/input/wrapper.blade.php | 212 ++++++++++ .../vendor/filament/components/link.blade.php | 304 ++++++++++++++ .../components/loading-indicator.blade.php | 18 + .../components/loading-section.blade.php | 33 ++ .../components/modal/description.blade.php | 5 + .../components/modal/heading.blade.php | 5 + .../filament/components/modal/index.blade.php | 379 ++++++++++++++++++ .../components/pagination/index.blade.php | 190 +++++++++ .../components/pagination/item.blade.php | 50 +++ .../components/section/description.blade.php | 5 + .../components/section/heading.blade.php | 5 + .../components/section/index.blade.php | 213 ++++++++++ .../filament/components/tabs/index.blade.php | 21 + .../filament/components/tabs/item.blade.php | 123 ++++++ resources/views/welcome.blade.php | 4 +- storage/app/public/.gitignore | 2 - 123 files changed, 6935 insertions(+), 32 deletions(-) create mode 100644 app/Filament/Exports/ItemExporter.php create mode 100644 app/Filament/Imports/ItemImporter.php create mode 100644 app/Filament/Imports/ProductionQuantityImporter.php create mode 100644 app/Filament/Pages/Dashboard.php create mode 100644 app/Filament/Resources/BlockResource.php create mode 100644 app/Filament/Resources/BlockResource/Pages/CreateBlock.php create mode 100644 app/Filament/Resources/BlockResource/Pages/EditBlock.php create mode 100644 app/Filament/Resources/BlockResource/Pages/ListBlocks.php create mode 100644 app/Filament/Resources/BlockResource/Pages/ViewBlock.php create mode 100644 app/Filament/Resources/BlockResource/RelationManagers/BlocksRelationManager.php create mode 100644 app/Filament/Resources/ItemResource.php create mode 100644 app/Filament/Resources/ItemResource/Pages/CreateItem.php create mode 100644 app/Filament/Resources/ItemResource/Pages/EditItem.php create mode 100644 app/Filament/Resources/ItemResource/Pages/ListItems.php create mode 100644 app/Filament/Resources/ItemResource/Pages/ViewItem.php create mode 100644 app/Filament/Resources/LineResource.php create mode 100644 app/Filament/Resources/LineResource/Pages/CreateLine.php create mode 100644 app/Filament/Resources/LineResource/Pages/EditLine.php create mode 100644 app/Filament/Resources/LineResource/Pages/ListLines.php create mode 100644 app/Filament/Resources/LineResource/Pages/ViewLine.php create mode 100644 app/Filament/Resources/LineStopResource.php create mode 100644 app/Filament/Resources/LineStopResource/Pages/CreateLineStop.php create mode 100644 app/Filament/Resources/LineStopResource/Pages/EditLineStop.php create mode 100644 app/Filament/Resources/LineStopResource/Pages/ListLineStops.php create mode 100644 app/Filament/Resources/LineStopResource/Pages/ViewLineStop.php create mode 100644 app/Filament/Resources/ProductionLineStopResource.php create mode 100644 app/Filament/Resources/ProductionLineStopResource/Pages/CreateProductionLineStop.php create mode 100644 app/Filament/Resources/ProductionLineStopResource/Pages/EditProductionLineStop.php create mode 100644 app/Filament/Resources/ProductionLineStopResource/Pages/ListProductionLineStops.php create mode 100644 app/Filament/Resources/ProductionLineStopResource/Pages/ViewProductionLineStop.php create mode 100644 app/Filament/Resources/ProductionPlanResource.php create mode 100644 app/Filament/Resources/ProductionPlanResource/Pages/CreateProductionPlan.php create mode 100644 app/Filament/Resources/ProductionPlanResource/Pages/EditProductionPlan.php create mode 100644 app/Filament/Resources/ProductionPlanResource/Pages/ListProductionPlans.php create mode 100644 app/Filament/Resources/ProductionPlanResource/Pages/ViewProductionPlan.php create mode 100644 app/Filament/Resources/ProductionQuantityResource.php create mode 100644 app/Filament/Resources/ProductionQuantityResource/Pages/CreateProductionQuantity.php create mode 100644 app/Filament/Resources/ProductionQuantityResource/Pages/EditProductionQuantity.php create mode 100644 app/Filament/Resources/ProductionQuantityResource/Pages/ListProductionQuantities.php create mode 100644 app/Filament/Resources/ProductionQuantityResource/Pages/ViewProductionQuantity.php create mode 100644 app/Filament/Resources/ShiftResource.php create mode 100644 app/Filament/Resources/ShiftResource/Pages/CreateShift.php create mode 100644 app/Filament/Resources/ShiftResource/Pages/EditShift.php create mode 100644 app/Filament/Resources/ShiftResource/Pages/ListShifts.php create mode 100644 app/Filament/Resources/ShiftResource/Pages/ViewShift.php create mode 100644 app/Filament/Widgets/ItemOverview.php create mode 100644 app/Models/Block.php create mode 100644 app/Models/Item.php create mode 100644 app/Models/Line.php create mode 100644 app/Models/LineStop.php create mode 100644 app/Models/ProductionLineStop.php create mode 100644 app/Models/ProductionPlan.php create mode 100644 app/Models/ProductionQuantity.php create mode 100644 app/Models/Shift.php create mode 100644 app/Policies/BlockPolicy.php create mode 100644 config/filament-spatie-roles-permissions.php create mode 100644 config/filament.php create mode 100644 config/permission.php create mode 100644 database/migrations/2025_03_20_115918_create_blocks_table.php create mode 100644 database/migrations/2025_03_20_125247_create_shifts_table.php create mode 100644 database/migrations/2025_03_21_090348_create_items_table.php create mode 100644 database/migrations/2025_03_21_113453_add_status_column_to_shifts_table.php create mode 100644 database/migrations/2025_03_21_115008_create_notifications_table.php create mode 100644 database/migrations/2025_03_21_115025_create_imports_table.php create mode 100644 database/migrations/2025_03_21_115026_create_exports_table.php create mode 100644 database/migrations/2025_03_21_115027_create_failed_import_rows_table.php create mode 100644 database/migrations/2025_03_21_124542_create_permission_tables.php create mode 100644 database/migrations/2025_03_22_184847_create_line_stops_table.php create mode 100644 database/migrations/2025_03_22_190920_create_lines_table.php create mode 100644 database/migrations/2025_03_23_113936_create_production_plans_table.php create mode 100644 database/migrations/2025_03_23_163259_create_production_line_stops_table.php create mode 100644 database/migrations/2025_03_23_170155_create_production_quantities_table.php create mode 100644 database/seeders/RoleSeeder.php create mode 100644 database/seeders/UserSeeder.php create mode 120000 public/assets create mode 100644 resources/views/vendor/filament/assets.blade.php create mode 100644 resources/views/vendor/filament/components/actions.blade.php create mode 100644 resources/views/vendor/filament/components/avatar.blade.php create mode 100644 resources/views/vendor/filament/components/badge.blade.php create mode 100644 resources/views/vendor/filament/components/breadcrumbs.blade.php create mode 100644 resources/views/vendor/filament/components/button/group.blade.php create mode 100644 resources/views/vendor/filament/components/button/index.blade.php create mode 100644 resources/views/vendor/filament/components/card.blade.php create mode 100644 resources/views/vendor/filament/components/dropdown/header.blade.php create mode 100644 resources/views/vendor/filament/components/dropdown/index.blade.php create mode 100644 resources/views/vendor/filament/components/dropdown/list/index.blade.php create mode 100644 resources/views/vendor/filament/components/dropdown/list/item.blade.php create mode 100644 resources/views/vendor/filament/components/fieldset.blade.php create mode 100644 resources/views/vendor/filament/components/grid/column.blade.php create mode 100644 resources/views/vendor/filament/components/grid/index.blade.php create mode 100644 resources/views/vendor/filament/components/icon-button.blade.php create mode 100644 resources/views/vendor/filament/components/icon.blade.php create mode 100644 resources/views/vendor/filament/components/input/checkbox.blade.php create mode 100644 resources/views/vendor/filament/components/input/index.blade.php create mode 100644 resources/views/vendor/filament/components/input/radio.blade.php create mode 100644 resources/views/vendor/filament/components/input/select.blade.php create mode 100644 resources/views/vendor/filament/components/input/wrapper.blade.php create mode 100644 resources/views/vendor/filament/components/link.blade.php create mode 100644 resources/views/vendor/filament/components/loading-indicator.blade.php create mode 100644 resources/views/vendor/filament/components/loading-section.blade.php create mode 100644 resources/views/vendor/filament/components/modal/description.blade.php create mode 100644 resources/views/vendor/filament/components/modal/heading.blade.php create mode 100644 resources/views/vendor/filament/components/modal/index.blade.php create mode 100644 resources/views/vendor/filament/components/pagination/index.blade.php create mode 100644 resources/views/vendor/filament/components/pagination/item.blade.php create mode 100644 resources/views/vendor/filament/components/section/description.blade.php create mode 100644 resources/views/vendor/filament/components/section/heading.blade.php create mode 100644 resources/views/vendor/filament/components/section/index.blade.php create mode 100644 resources/views/vendor/filament/components/tabs/index.blade.php create mode 100644 resources/views/vendor/filament/components/tabs/item.blade.php delete mode 100644 storage/app/public/.gitignore diff --git a/app/Filament/Exports/ItemExporter.php b/app/Filament/Exports/ItemExporter.php new file mode 100644 index 0000000..e08260b --- /dev/null +++ b/app/Filament/Exports/ItemExporter.php @@ -0,0 +1,38 @@ +label('ID'), + ExportColumn::make('code'), + ExportColumn::make('description'), + ExportColumn::make('hourly_quantity'), + ExportColumn::make('created_at'), + ExportColumn::make('updated_at'), + ExportColumn::make('deleted_at'), + ]; + } + + public static function getCompletedNotificationBody(Export $export): string + { + $body = 'Your item export has completed and '.number_format($export->successful_rows).' '.str('row')->plural($export->successful_rows).' exported.'; + + if ($failedRowsCount = $export->getFailedRowsCount()) { + $body .= ' '.number_format($failedRowsCount).' '.str('row')->plural($failedRowsCount).' failed to export.'; + } + + return $body; + } +} diff --git a/app/Filament/Imports/ItemImporter.php b/app/Filament/Imports/ItemImporter.php new file mode 100644 index 0000000..849ee56 --- /dev/null +++ b/app/Filament/Imports/ItemImporter.php @@ -0,0 +1,56 @@ +requiredMapping() + ->exampleHeader('Item Code') + ->label('Item Code') + ->rules(['required']), + ImportColumn::make('description') + ->requiredMapping() + ->exampleHeader('Description') + ->label('Description') + ->rules(['required']), + ImportColumn::make('hourly_quantity') + ->requiredMapping() + ->exampleHeader('Hourly Quantity') + ->label('Hourly Quantity') + ->numeric() + ->rules(['required', 'integer']), + ]; + } + + public function resolveRecord(): ?Item + { + // return Item::firstOrNew([ + // // Update existing records, matching them by `$this->data['column_name']` + // 'email' => $this->data['email'], + // ]); + + return new Item; + } + + public static function getCompletedNotificationBody(Import $import): string + { + $body = 'Your item import has completed and '.number_format($import->successful_rows).' '.str('row')->plural($import->successful_rows).' imported.'; + + if ($failedRowsCount = $import->getFailedRowsCount()) { + $body .= ' '.number_format($failedRowsCount).' '.str('row')->plural($failedRowsCount).' failed to import.'; + } + + return $body; + } +} diff --git a/app/Filament/Imports/ProductionQuantityImporter.php b/app/Filament/Imports/ProductionQuantityImporter.php new file mode 100644 index 0000000..c2828db --- /dev/null +++ b/app/Filament/Imports/ProductionQuantityImporter.php @@ -0,0 +1,91 @@ +requiredMapping() + ->exampleHeader('Created DateTime') + ->label('Created DateTime') + ->rules(['required']), + ImportColumn::make('plan_quantity') + ->requiredMapping() + ->exampleHeader('Plan Quantity') + ->label('Plan Quantity') + ->numeric() + ->rules(['required', 'integer']), + ImportColumn::make('hourly_quantity') + ->requiredMapping() + ->exampleHeader('Hourly Quantity') + ->label('Hourly Quantity') + ->numeric() + ->rules(['required', 'integer']), + ImportColumn::make('item_code') + ->requiredMapping() + ->exampleHeader('Item Code') + ->label('Item Code') + ->rules(['required']), + ImportColumn::make('serial_number') + ->requiredMapping() + ->exampleHeader('Serial Number') + ->label('Serial Number') + ->rules(['required']), + ImportColumn::make('line') + ->requiredMapping() + ->exampleHeader('Line Name') + ->label('Line Name') + ->relationship(resolveUsing:'name') + ->rules(['required']), + ImportColumn::make('shift') + ->requiredMapping() + ->exampleHeader('Shift Name') + ->label('Shift Name') + ->relationship(resolveUsing:'name') + ->rules(['required']), + ImportColumn::make('block') + ->requiredMapping() + ->exampleHeader('Block Name') + ->label('Block Name') + ->relationship(resolveUsing:'name') + ->rules(['required']), + ImportColumn::make('plant') + ->requiredMapping() + ->exampleHeader('Plant Name') + ->label('Plant Name') + ->relationship(resolveUsing:'name') + ->rules(['required']), + ]; + } + + public function resolveRecord(): ?ProductionQuantity + { + // return ProductionQuantity::firstOrNew([ + // // Update existing records, matching them by `$this->data['column_name']` + // 'email' => $this->data['email'], + // ]); + + return new ProductionQuantity(); + } + + public static function getCompletedNotificationBody(Import $import): string + { + $body = 'Your production quantity import has completed and ' . number_format($import->successful_rows) . ' ' . str('row')->plural($import->successful_rows) . ' imported.'; + + if ($failedRowsCount = $import->getFailedRowsCount()) { + $body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to import.'; + } + + return $body; + } +} diff --git a/app/Filament/Pages/Dashboard.php b/app/Filament/Pages/Dashboard.php new file mode 100644 index 0000000..20604ae --- /dev/null +++ b/app/Filament/Pages/Dashboard.php @@ -0,0 +1,26 @@ +schema([ + Select::make('Plant') + ->options(\DB::table('plants')->pluck('name', 'name')) // Fetch plant names + ->label('Select Plant'), + + Select::make('Line') + ->options(\DB::table('lines')->pluck('name', 'name')) // Fetch line names + ->label('Select Line'), + ]); + } +} diff --git a/app/Filament/Resources/BlockResource.php b/app/Filament/Resources/BlockResource.php new file mode 100644 index 0000000..b37030f --- /dev/null +++ b/app/Filament/Resources/BlockResource.php @@ -0,0 +1,102 @@ +schema([ + Forms\Components\TextInput::make('name') + ->required() + ->unique(ignoreRecord: true), + // ->columnSpanFull(), + Forms\Components\Select::make('plant_id') + ->relationship('plant', 'name') + ->required(), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('id') + ->label('ID') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('name') + ->sortable(), + Tables\Columns\TextColumn::make('plant.name') + ->sortable(), + Tables\Columns\TextColumn::make('created_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('updated_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('deleted_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + Tables\Filters\TrashedFilter::make(), + ]) + ->actions([ + Tables\Actions\ViewAction::make(), + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + Tables\Actions\ForceDeleteBulkAction::make(), + Tables\Actions\RestoreBulkAction::make(), + ]), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListBlocks::route('/'), + 'create' => Pages\CreateBlock::route('/create'), + 'view' => Pages\ViewBlock::route('/{record}'), + 'edit' => Pages\EditBlock::route('/{record}/edit'), + ]; + } + + public static function getEloquentQuery(): Builder + { + return parent::getEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } +} diff --git a/app/Filament/Resources/BlockResource/Pages/CreateBlock.php b/app/Filament/Resources/BlockResource/Pages/CreateBlock.php new file mode 100644 index 0000000..052c495 --- /dev/null +++ b/app/Filament/Resources/BlockResource/Pages/CreateBlock.php @@ -0,0 +1,12 @@ +schema([ + Forms\Components\TextInput::make('name') + ->required() + ->maxLength(255), + ]); + } + + public function table(Table $table): Table + { + return $table + ->recordTitleAttribute('name') + ->columns([ + Tables\Columns\TextColumn::make('name'), + // Tables\Columns\TextColumn::make('plants.name'), + ]) + ->filters([ + // + ]) + ->headerActions([ + Tables\Actions\CreateAction::make(), + ]) + ->actions([ + Tables\Actions\ViewAction::make(), + Tables\Actions\EditAction::make(), + Tables\Actions\DeleteAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + ]), + ]); + } +} diff --git a/app/Filament/Resources/CompanyResource.php b/app/Filament/Resources/CompanyResource.php index 0f9faaa..7830c2d 100644 --- a/app/Filament/Resources/CompanyResource.php +++ b/app/Filament/Resources/CompanyResource.php @@ -17,7 +17,9 @@ class CompanyResource extends Resource { protected static ?string $model = Company::class; - protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack'; + protected static ?string $navigationIcon = 'heroicon-s-home-modern'; + + protected static ?string $navigationGroup = 'Master Entries'; public static function form(Form $form): Form { @@ -25,6 +27,7 @@ class CompanyResource extends Resource ->schema([ Forms\Components\TextInput::make('name') ->required() + ->unique() ->columnSpanFull(), ]); } @@ -37,7 +40,6 @@ class CompanyResource extends Resource ->label('ID') ->numeric() ->sortable(), - Tables\Columns\TextColumn::make('name') ->sortable(), Tables\Columns\TextColumn::make('created_at') @@ -45,10 +47,12 @@ class CompanyResource extends Resource ->sortable(), Tables\Columns\TextColumn::make('updated_at') ->dateTime() - ->sortable(), + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), Tables\Columns\TextColumn::make('deleted_at') ->dateTime() - ->sortable(), + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), ]) ->filters([ Tables\Filters\TrashedFilter::make(), diff --git a/app/Filament/Resources/ItemResource.php b/app/Filament/Resources/ItemResource.php new file mode 100644 index 0000000..d55e80f --- /dev/null +++ b/app/Filament/Resources/ItemResource.php @@ -0,0 +1,120 @@ +schema([ + Forms\Components\TextInput::make('code') + ->required() + ->unique(ignoreRecord: true) + ->minLength(6) + ->autocapitalize('code'), + Forms\Components\TextInput::make('hourly_quantity') + ->required() + ->numeric() + ->minValue(1), + Forms\Components\Textarea::make('description') + ->required() + ->columnSpanFull(), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('id') + ->label('ID') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('code') + ->sortable(), + Tables\Columns\TextColumn::make('hourly_quantity') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('description') + ->sortable(), + Tables\Columns\TextColumn::make('created_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('updated_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('deleted_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + Tables\Filters\TrashedFilter::make(), + ]) + ->actions([ + Tables\Actions\ViewAction::make(), + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + Tables\Actions\ForceDeleteBulkAction::make(), + Tables\Actions\RestoreBulkAction::make(), + ]), + ]) + ->headerActions([ + ImportAction::make() + ->importer(ItemImporter::class), + ExportAction::make() + ->exporter(ItemExporter::class), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListItems::route('/'), + 'create' => Pages\CreateItem::route('/create'), + 'view' => Pages\ViewItem::route('/{record}'), + 'edit' => Pages\EditItem::route('/{record}/edit'), + ]; + } + + public static function getEloquentQuery(): Builder + { + return parent::getEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } +} diff --git a/app/Filament/Resources/ItemResource/Pages/CreateItem.php b/app/Filament/Resources/ItemResource/Pages/CreateItem.php new file mode 100644 index 0000000..40e94d7 --- /dev/null +++ b/app/Filament/Resources/ItemResource/Pages/CreateItem.php @@ -0,0 +1,12 @@ +schema([ + Forms\Components\Textarea::make('name') + ->required(), + // ->columnSpanFull(), + Forms\Components\Textarea::make('type') + ->required(), + //->columnSpanFull(), + Forms\Components\Select::make('plant_id') + ->relationship('plant', 'name') + ->required(), + Forms\Components\Select::make('block_id') + ->relationship('block', 'name') + ->required(), + Forms\Components\Select::make('shift_id') + ->relationship('shift', 'name') + ->required(), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('id') + ->label('ID') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('name') + ->sortable(), + Tables\Columns\TextColumn::make('type') + ->sortable(), + Tables\Columns\TextColumn::make('shift.name') + ->sortable(), + Tables\Columns\TextColumn::make('block.name') + ->sortable(), + Tables\Columns\TextColumn::make('plant.name') + ->sortable(), + Tables\Columns\TextColumn::make('created_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('updated_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('deleted_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + Tables\Filters\TrashedFilter::make(), + ]) + ->actions([ + Tables\Actions\ViewAction::make(), + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + Tables\Actions\ForceDeleteBulkAction::make(), + Tables\Actions\RestoreBulkAction::make(), + ]), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListLines::route('/'), + 'create' => Pages\CreateLine::route('/create'), + 'view' => Pages\ViewLine::route('/{record}'), + 'edit' => Pages\EditLine::route('/{record}/edit'), + ]; + } + + public static function getEloquentQuery(): Builder + { + return parent::getEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } +} diff --git a/app/Filament/Resources/LineResource/Pages/CreateLine.php b/app/Filament/Resources/LineResource/Pages/CreateLine.php new file mode 100644 index 0000000..8cb7a10 --- /dev/null +++ b/app/Filament/Resources/LineResource/Pages/CreateLine.php @@ -0,0 +1,12 @@ +schema([ + Forms\Components\TextInput::make('code') + ->required() + ->autocapitalize(autocapitalize: 'code'), + Forms\Components\Textarea::make('reason') + ->required(), + //->columnSpanFull(), + Forms\Components\Select::make('plant_id') + ->relationship('plant', 'name') + ->required(), + Forms\Components\Select::make('block_id') + ->relationship('block', 'name') + ->required(), + Forms\Components\Select::make('shift_id') + ->relationship('shift', 'name') + ->required(), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('id') + ->label('ID') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('code') + ->sortable(), + Tables\Columns\TextColumn::make('reason') + ->sortable(), + Tables\Columns\TextColumn::make('shift.name') + ->sortable(), + Tables\Columns\TextColumn::make('block.name') + ->sortable(), + Tables\Columns\TextColumn::make('plant.name') + ->sortable(), + Tables\Columns\TextColumn::make('created_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('updated_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('deleted_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + Tables\Filters\TrashedFilter::make(), + ]) + ->actions([ + Tables\Actions\ViewAction::make(), + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + Tables\Actions\ForceDeleteBulkAction::make(), + Tables\Actions\RestoreBulkAction::make(), + ]), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListLineStops::route('/'), + 'create' => Pages\CreateLineStop::route('/create'), + 'view' => Pages\ViewLineStop::route('/{record}'), + 'edit' => Pages\EditLineStop::route('/{record}/edit'), + ]; + } + + public static function getEloquentQuery(): Builder + { + return parent::getEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } +} diff --git a/app/Filament/Resources/LineStopResource/Pages/CreateLineStop.php b/app/Filament/Resources/LineStopResource/Pages/CreateLineStop.php new file mode 100644 index 0000000..3e77035 --- /dev/null +++ b/app/Filament/Resources/LineStopResource/Pages/CreateLineStop.php @@ -0,0 +1,12 @@ +schema([ Forms\Components\TextInput::make('code') ->required() - ->unique() - ->minValue(1000) - ->numeric(), + ->unique(ignoreRecord: true) + ->numeric() + ->minValue(1000), Forms\Components\Select::make('company_id') ->relationship('company', 'name') ->required(), Forms\Components\TextInput::make('name') ->required() + ->unique(ignoreRecord: true) ->columnSpanFull(), Forms\Components\Textarea::make('address') ->required() + ->unique(ignoreRecord: true) ->columnSpanFull(), ]) ->columns(2), @@ -55,8 +59,9 @@ class PlantResource extends Resource Tables\Columns\TextColumn::make('code') ->numeric() ->sortable(), + Tables\Columns\TextColumn::make('name') + ->sortable(), Tables\Columns\TextColumn::make('company.name') - ->numeric() ->sortable(), Tables\Columns\TextColumn::make('created_at') ->dateTime() diff --git a/app/Filament/Resources/ProductionLineStopResource.php b/app/Filament/Resources/ProductionLineStopResource.php new file mode 100644 index 0000000..09316b6 --- /dev/null +++ b/app/Filament/Resources/ProductionLineStopResource.php @@ -0,0 +1,144 @@ +schema([ + Forms\Components\Select::make('code_id') + ->relationship('linestop', 'code') + ->required(), + Forms\Components\Textarea::make('reason') + ->required(), + //->columnSpanFull(), + Forms\Components\DateTimePicker::make('from_datetime') + ->required(), + Forms\Components\DateTimePicker::make('to_datetime') + ->required(), + Forms\Components\TextInput::make('stop_hour') + ->required() + ->numeric(), + Forms\Components\TextInput::make('stop_min') + ->required() + ->numeric(), + Forms\Components\Select::make('plant_id') + ->relationship('plant', 'name') + ->required(), + Forms\Components\Select::make('block_id') + ->relationship('block', 'name') + ->required(), + Forms\Components\Select::make('shift_id') + ->relationship('shift', 'name') + ->required(), + Forms\Components\Select::make('line_id') + ->relationship('line', 'name') + ->required(), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('id') + ->label('ID') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('linestop.code') + ->sortable(), + Tables\Columns\TextColumn::make('reason') + ->sortable(), + Tables\Columns\TextColumn::make('from_datetime') + ->dateTime() + ->sortable(), + Tables\Columns\TextColumn::make('to_datetime') + ->dateTime() + ->sortable(), + Tables\Columns\TextColumn::make('stop_hour') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('stop_min') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('line.name') + ->sortable(), + Tables\Columns\TextColumn::make('shift.name') + ->sortable(), + Tables\Columns\TextColumn::make('block.name') + ->sortable(), + Tables\Columns\TextColumn::make('plant.name') + ->sortable(), + Tables\Columns\TextColumn::make('created_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('updated_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('deleted_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + Tables\Filters\TrashedFilter::make(), + ]) + ->actions([ + Tables\Actions\ViewAction::make(), + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + Tables\Actions\ForceDeleteBulkAction::make(), + Tables\Actions\RestoreBulkAction::make(), + ]), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListProductionLineStops::route('/'), + 'create' => Pages\CreateProductionLineStop::route('/create'), + 'view' => Pages\ViewProductionLineStop::route('/{record}'), + 'edit' => Pages\EditProductionLineStop::route('/{record}/edit'), + ]; + } + + public static function getEloquentQuery(): Builder + { + return parent::getEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } +} diff --git a/app/Filament/Resources/ProductionLineStopResource/Pages/CreateProductionLineStop.php b/app/Filament/Resources/ProductionLineStopResource/Pages/CreateProductionLineStop.php new file mode 100644 index 0000000..37ca32d --- /dev/null +++ b/app/Filament/Resources/ProductionLineStopResource/Pages/CreateProductionLineStop.php @@ -0,0 +1,12 @@ +schema([ + Forms\Components\TextInput::make('plan_quantity') + ->required() + ->numeric() + ->minValue(1), + Forms\Components\TextInput::make('production_quantity') + ->required() + ->numeric() + ->readOnly() + ->default(0), + Forms\Components\Select::make('plant_id') + ->relationship('plant', 'name') + ->required(), + Forms\Components\Select::make('shift_id') + ->relationship('shift', 'name') + ->required(), + Forms\Components\Select::make('line_id') + ->relationship('line', 'name') + ->required(), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('id') + ->label('ID') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('plan_quantity') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('production_quantity') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('line.name') + ->sortable(), + Tables\Columns\TextColumn::make('shift.name') + ->sortable(), + Tables\Columns\TextColumn::make('plant.name') + ->sortable(), + Tables\Columns\TextColumn::make('created_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('updated_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('deleted_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + Tables\Filters\TrashedFilter::make(), + ]) + ->actions([ + Tables\Actions\ViewAction::make(), + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + Tables\Actions\ForceDeleteBulkAction::make(), + Tables\Actions\RestoreBulkAction::make(), + ]), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListProductionPlans::route('/'), + 'create' => Pages\CreateProductionPlan::route('/create'), + 'view' => Pages\ViewProductionPlan::route('/{record}'), + 'edit' => Pages\EditProductionPlan::route('/{record}/edit'), + ]; + } + + public static function getEloquentQuery(): Builder + { + return parent::getEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } +} diff --git a/app/Filament/Resources/ProductionPlanResource/Pages/CreateProductionPlan.php b/app/Filament/Resources/ProductionPlanResource/Pages/CreateProductionPlan.php new file mode 100644 index 0000000..26a905d --- /dev/null +++ b/app/Filament/Resources/ProductionPlanResource/Pages/CreateProductionPlan.php @@ -0,0 +1,12 @@ +schema([ + Forms\Components\TextInput::make('plan_quantity') + ->required() + ->numeric(), + Forms\Components\TextInput::make('hourly_quantity') + ->required() + ->numeric(), + Forms\Components\TextInput::make('item_code') + ->required() + ->autocapitalize('item_code'), + //->columnSpanFull(), + Forms\Components\TextInput::make('serial_number') + ->required() + ->autocapitalize('serial_number'), + //->columnSpanFull(), + Forms\Components\Select::make('plant_id') + ->relationship('plant', 'name') + ->required(), + Forms\Components\Select::make('block_id') + ->relationship('block', 'name') + ->required(), + Forms\Components\Select::make('shift_id') + ->relationship('shift', 'name') + ->required(), + Forms\Components\Select::make('line_id') + ->relationship('line', 'name') + ->required(), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('id') + ->label('ID') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('plan_quantity') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('hourly_quantity') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('item_code') + ->sortable(), + Tables\Columns\TextColumn::make('serial_number') + ->sortable(), + Tables\Columns\TextColumn::make('line.name') + ->sortable(), + Tables\Columns\TextColumn::make('shift.name') + ->sortable(), + Tables\Columns\TextColumn::make('block.name') + ->sortable(), + Tables\Columns\TextColumn::make('plant.name') + ->sortable(), + Tables\Columns\TextColumn::make('created_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('updated_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('deleted_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + Tables\Filters\TrashedFilter::make(), + ]) + ->actions([ + Tables\Actions\ViewAction::make(), + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + Tables\Actions\ForceDeleteBulkAction::make(), + Tables\Actions\RestoreBulkAction::make(), + ]), + ]) + ->headerActions([ + ImportAction::make() + ->importer(ProductionQuantityImporter::class), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListProductionQuantities::route('/'), + 'create' => Pages\CreateProductionQuantity::route('/create'), + 'view' => Pages\ViewProductionQuantity::route('/{record}'), + 'edit' => Pages\EditProductionQuantity::route('/{record}/edit'), + ]; + } + + public static function getEloquentQuery(): Builder + { + return parent::getEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } +} diff --git a/app/Filament/Resources/ProductionQuantityResource/Pages/CreateProductionQuantity.php b/app/Filament/Resources/ProductionQuantityResource/Pages/CreateProductionQuantity.php new file mode 100644 index 0000000..0ad7efb --- /dev/null +++ b/app/Filament/Resources/ProductionQuantityResource/Pages/CreateProductionQuantity.php @@ -0,0 +1,12 @@ +schema([ + Forms\Components\Select::make('plant_id') + ->relationship('plant', 'name') + ->required(), + Forms\Components\Select::make('block_id') + ->relationship('block', 'name') + ->required(), + Forms\Components\TextInput::make('name') + ->required(), + Forms\Components\TimePicker::make('start_time') + ->required(), + // ->native(false), + Forms\Components\TextInput::make('duration') + ->required() + ->inputMode('decimal') + ->minValue(1), + Forms\Components\TimePicker::make('end_time') + ->required(), + // ->native(false), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('id') + ->label('ID') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('name') + ->sortable(), + Tables\Columns\TextColumn::make('start_time') + ->sortable(), + Tables\Columns\TextColumn::make('duration') + ->sortable(), + Tables\Columns\TextColumn::make('end_time'), + Tables\Columns\TextColumn::make('block.name') + ->sortable(), + Tables\Columns\TextColumn::make('plant.name') + ->sortable(), + Tables\Columns\TextColumn::make('created_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('updated_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('deleted_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + Tables\Filters\TrashedFilter::make(), + ]) + ->actions([ + Tables\Actions\ViewAction::make(), + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + Tables\Actions\ForceDeleteBulkAction::make(), + Tables\Actions\RestoreBulkAction::make(), + ]), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListShifts::route('/'), + 'create' => Pages\CreateShift::route('/create'), + 'view' => Pages\ViewShift::route('/{record}'), + 'edit' => Pages\EditShift::route('/{record}/edit'), + ]; + } + + public static function getEloquentQuery(): Builder + { + return parent::getEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } +} diff --git a/app/Filament/Resources/ShiftResource/Pages/CreateShift.php b/app/Filament/Resources/ShiftResource/Pages/CreateShift.php new file mode 100644 index 0000000..8aa2109 --- /dev/null +++ b/app/Filament/Resources/ShiftResource/Pages/CreateShift.php @@ -0,0 +1,12 @@ +filter; + $query = \DB::table('production_quantities') + ->selectRaw('EXTRACT(HOUR FROM created_at) AS hour, SUM(hourly_quantity) AS total_quantity') + ->whereBetween('created_at', [now()->startOfDay(), now()->endOfDay()]) + ->groupByRaw('EXTRACT(HOUR FROM created_at)') + ->orderByRaw('EXTRACT(HOUR FROM created_at)') + ->pluck('total_quantity', 'hour') + ->toArray(); + // Initialize data for each hour interval (8 AM - 7 PM) + $data = array_fill(8, 12, 0); // 8 AM to 7 PM (indexes 8 to 19) + + // Populate actual values + // foreach ($query as $record) { + // $hour = (int) $record->hour; + // if ($hour >= 8 && $hour <= 19) { + // $data[$hour] = $record->total_quantity; // Assign only the hourly production + // } + // } + + // Convert data to chart format + return [ + 'datasets' => [ + [ + 'label' => 'Hourly Production', + 'data' => array_values($data), // Values only + 'borderColor' => 'rgba(75, 192, 192, 1)', + 'backgroundColor' => 'rgba(75, 192, 192, 0.2)', + 'fill' => false, // No area fill, just the line + 'tension' => 0.3, // Smooth curve + ], + ], + 'labels' => array_map(fn($h) => ($h <= 11 ? "$h AM" : ($h == 12 ? "12 PM" : ($h - 12) . " PM")), array_keys($data)), + ]; + + // // Initialize data for each hour interval (8 AM - 7 PM) + // $data = [ + // '8 AM - 9 AM' => 0, '9 AM - 10 AM' => 0, '10 AM - 11 AM' => 0, '11 AM - 12 PM' => 0, + // '12 PM - 1 PM' => 0, '1 PM - 2 PM' => 0, '2 PM - 3 PM' => 0, '3 PM - 4 PM' => 0, + // '4 PM - 5 PM' => 0, '5 PM - 6 PM' => 0, '6 PM - 7 PM' => 0, '7 PM - 8 PM' => 0, + // ]; + + + // // Populate actual values + // foreach ($query as $record) { + // $hour = (int) $record->hour; + // if ($hour >= 8 && $hour <= 19) { + // $index = ($hour - 8); // Match hour to array index + // $labels = array_keys($data); // Get labels + + // // Store only the production for that specific hour + // $data[$labels[$index]] = $record->total_quantity; + // } + // } + + + // // Debugging: Check if data exists + // if (empty($query) || array_sum($data) === 0) { + // \Log::info('No production data found for today.'); + // } + + // return [ + // 'datasets' => [ + // [ + // 'label' => 'Production Quantities', + // 'data' => array_values($data), + // 'borderColor' => 'rgba(75, 192, 192, 1)', + // 'backgroundColor' => 'rgba(75, 192, 192, 0.2)', + // 'fill' => true, + // ], + // ], + // 'labels' => array_keys($data), + // ]; + } + + protected function getType(): string + { + return 'line'; + } + + protected function getFilters(): ?array + { + return [ + 'today' => 'Today', + 'week' => 'Last week', + 'month' => 'Last month', + 'year' => 'This year', + ]; + } + +} diff --git a/app/Models/Block.php b/app/Models/Block.php new file mode 100644 index 0000000..72287b3 --- /dev/null +++ b/app/Models/Block.php @@ -0,0 +1,33 @@ +belongsTo(Plant::class); + } + + public function shifts(): HasMany + { + return $this->hasMany(Shift::class); + } + + public function lines(): HasMany + { + return $this->hasMany(Line::class); + } +} diff --git a/app/Models/Item.php b/app/Models/Item.php new file mode 100644 index 0000000..dba63d8 --- /dev/null +++ b/app/Models/Item.php @@ -0,0 +1,17 @@ +belongsTo(Plant::class); + } + + public function block(): BelongsTo + { + return $this->belongsTo(Block::class); + } + + public function shift(): BelongsTo + { + return $this->belongsTo(Shift::class); + } + + public function line_stops(): HasMany + { + return $this->hasMany(LineStop::class); + } +} diff --git a/app/Models/LineStop.php b/app/Models/LineStop.php new file mode 100644 index 0000000..41a5793 --- /dev/null +++ b/app/Models/LineStop.php @@ -0,0 +1,36 @@ +belongsTo(Plant::class); + } + + public function block(): BelongsTo + { + return $this->belongsTo(Block::class); + } + + public function shift(): BelongsTo + { + return $this->belongsTo(Shift::class); + } +} diff --git a/app/Models/Plant.php b/app/Models/Plant.php index 3f053b3..8d42542 100644 --- a/app/Models/Plant.php +++ b/app/Models/Plant.php @@ -4,6 +4,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; class Plant extends Model @@ -21,4 +22,9 @@ class Plant extends Model { return $this->belongsTo(Company::class); } + + public function blocks(): HasMany + { + return $this->hasMany(Block::class); + } } diff --git a/app/Models/ProductionLineStop.php b/app/Models/ProductionLineStop.php new file mode 100644 index 0000000..653bd85 --- /dev/null +++ b/app/Models/ProductionLineStop.php @@ -0,0 +1,50 @@ +belongsTo(Plant::class); + } + + public function block(): BelongsTo + { + return $this->belongsTo(Block::class); + } + + public function shift(): BelongsTo + { + return $this->belongsTo(Shift::class); + } + + public function line(): BelongsTo + { + return $this->belongsTo(Line::class); + } + + public function linestop(): BelongsTo + { + return $this->belongsTo(LineStop::class); + } +} diff --git a/app/Models/ProductionPlan.php b/app/Models/ProductionPlan.php new file mode 100644 index 0000000..4942dd4 --- /dev/null +++ b/app/Models/ProductionPlan.php @@ -0,0 +1,35 @@ +belongsTo(Plant::class); + } + + public function shift(): BelongsTo + { + return $this->belongsTo(Shift::class); + } + + public function line(): BelongsTo + { + return $this->belongsTo(Line::class); + } +} diff --git a/app/Models/ProductionQuantity.php b/app/Models/ProductionQuantity.php new file mode 100644 index 0000000..12e9c69 --- /dev/null +++ b/app/Models/ProductionQuantity.php @@ -0,0 +1,43 @@ +belongsTo(Plant::class); + } + + public function block(): BelongsTo + { + return $this->belongsTo(Block::class); + } + + public function shift(): BelongsTo + { + return $this->belongsTo(Shift::class); + } + + public function line(): BelongsTo + { + return $this->belongsTo(Line::class); + } +} diff --git a/app/Models/Shift.php b/app/Models/Shift.php new file mode 100644 index 0000000..5012e68 --- /dev/null +++ b/app/Models/Shift.php @@ -0,0 +1,36 @@ +belongsTo(Plant::class); + // } + + public function block(): BelongsTo + { + return $this->belongsTo(Block::class); + } + + public function plant(): BelongsTo + { + return $this->belongsTo(Plant::class); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 749c7b7..ff5888a 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -6,11 +6,12 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Spatie\Permission\Traits\HasRoles; class User extends Authenticatable { /** @use HasFactory<\Database\Factories\UserFactory> */ - use HasFactory, Notifiable; + use HasFactory, HasRoles, Notifiable; /** * The attributes that are mass assignable. diff --git a/app/Policies/BlockPolicy.php b/app/Policies/BlockPolicy.php new file mode 100644 index 0000000..332e2f9 --- /dev/null +++ b/app/Policies/BlockPolicy.php @@ -0,0 +1,65 @@ +hasPermissionTo('view-any Block'); + } + + /** + * Determine whether the user can view the model. + */ + public function view(User $user, Block $block): bool + { + return $user->hasPermissionTo('view Block'); + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + return $user->hasPermissionTo('create Block'); + } + + /** + * Determine whether the user can update the model. + */ + public function update(User $user, Block $block): bool + { + return false; + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(User $user, Block $block): bool + { + return false; + } + + /** + * Determine whether the user can restore the model. + */ + public function restore(User $user, Block $block): bool + { + return false; + } + + /** + * Determine whether the user can permanently delete the model. + */ + public function forceDelete(User $user, Block $block): bool + { + return false; + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 452e6b6..e5970ac 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,7 @@ namespace App\Providers; +use Illuminate\Support\Facades\Gate; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider @@ -19,6 +20,8 @@ class AppServiceProvider extends ServiceProvider */ public function boot(): void { - // + Gate::before(function ($user, $ability) { + return $user->hasRole('Super Admin') ? true : null; + }); } } diff --git a/app/Providers/Filament/AdminPanelProvider.php b/app/Providers/Filament/AdminPanelProvider.php index 6694332..a474fea 100644 --- a/app/Providers/Filament/AdminPanelProvider.php +++ b/app/Providers/Filament/AdminPanelProvider.php @@ -2,6 +2,7 @@ namespace App\Providers\Filament; +use Althinect\FilamentSpatieRolesPermissions\FilamentSpatieRolesPermissionsPlugin; use Filament\Http\Middleware\Authenticate; use Filament\Http\Middleware\AuthenticateSession; use Filament\Http\Middleware\DisableBladeIconComponents; @@ -24,22 +25,24 @@ class AdminPanelProvider extends PanelProvider { return $panel ->default() + ->id('admin') ->path('admin') ->login() + ->favicon(asset('/assets/crilogo1.png')) ->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, + ]) ->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets') - ->widgets([ - Widgets\AccountWidget::class, - Widgets\FilamentInfoWidget::class, - ]) + // ->widgets([ + // Widgets\AccountWidget::class, + // Widgets\FilamentInfoWidget::class, + // ]) ->middleware([ EncryptCookies::class, AddQueuedCookiesToResponse::class, @@ -53,6 +56,8 @@ class AdminPanelProvider extends PanelProvider ]) ->authMiddleware([ Authenticate::class, - ]); + ]) + ->databaseNotifications() + ->plugin(FilamentSpatieRolesPermissionsPlugin::make()); } } diff --git a/composer.json b/composer.json index 9f164e8..5832820 100644 --- a/composer.json +++ b/composer.json @@ -7,6 +7,7 @@ "license": "MIT", "require": { "php": "^8.2", + "althinect/filament-spatie-roles-permissions": "^2.3", "filament/filament": "^3.3", "laravel/framework": "^11.31", "laravel/tinker": "^2.9" diff --git a/composer.lock b/composer.lock index 5f2b19b..f11b24d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,71 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c2247a5e7b193701c7a41ae798179d8e", + "content-hash": "292ea7e3ed35e47bcbea3972cfa9bb78", "packages": [ + { + "name": "althinect/filament-spatie-roles-permissions", + "version": "v2.3", + "source": { + "type": "git", + "url": "https://github.com/Althinect/filament-spatie-roles-permissions.git", + "reference": "7278e336eabd95150843c8153f680aab3f2ba131" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Althinect/filament-spatie-roles-permissions/zipball/7278e336eabd95150843c8153f680aab3f2ba131", + "reference": "7278e336eabd95150843c8153f680aab3f2ba131", + "shasum": "" + }, + "require": { + "filament/filament": "^3.0", + "illuminate/support": "^9|^10|^11|^12.0", + "php": "^8.1|^8.2", + "spatie/laravel-permission": "^5.4|^6.0" + }, + "require-dev": { + "laravel/pint": "^1.10", + "orchestra/testbench": "^7.0|^9.0|^10.0", + "phpunit/phpunit": "^9.0|^10.5|^11.5.3" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "FilamentSpatieRolesPermissions": "Althinect\\FilamentSpatieRolesPermissions\\FilamentSpatieRolesPermissionsFacade" + }, + "providers": [ + "Althinect\\FilamentSpatieRolesPermissions\\FilamentSpatieRolesPermissionsServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Althinect\\FilamentSpatieRolesPermissions\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tharinda Rodrigo", + "email": "tharindarodrigo@gmail.com", + "role": "Developer" + } + ], + "homepage": "https://github.com/althinect/filament-spatie-roles-permissions", + "keywords": [ + "althinect", + "filament-spatie-roles-permissions" + ], + "support": { + "issues": "https://github.com/Althinect/filament-spatie-roles-permissions/issues", + "source": "https://github.com/Althinect/filament-spatie-roles-permissions/tree/v2.3" + }, + "time": "2025-03-01T21:25:50+00:00" + }, { "name": "anourvalar/eloquent-serialize", "version": "1.2.29", @@ -4885,6 +4948,89 @@ ], "time": "2025-02-06T14:58:20+00:00" }, + { + "name": "spatie/laravel-permission", + "version": "6.16.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-permission.git", + "reference": "4fa03c06509e037a4d42c131d0f181e3e4bbd483" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-permission/zipball/4fa03c06509e037a4d42c131d0f181e3e4bbd483", + "reference": "4fa03c06509e037a4d42c131d0f181e3e4bbd483", + "shasum": "" + }, + "require": { + "illuminate/auth": "^8.12|^9.0|^10.0|^11.0|^12.0", + "illuminate/container": "^8.12|^9.0|^10.0|^11.0|^12.0", + "illuminate/contracts": "^8.12|^9.0|^10.0|^11.0|^12.0", + "illuminate/database": "^8.12|^9.0|^10.0|^11.0|^12.0", + "php": "^8.0" + }, + "require-dev": { + "laravel/passport": "^11.0|^12.0", + "laravel/pint": "^1.0", + "orchestra/testbench": "^6.23|^7.0|^8.0|^9.0|^10.0", + "phpunit/phpunit": "^9.4|^10.1|^11.5" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\Permission\\PermissionServiceProvider" + ] + }, + "branch-alias": { + "dev-main": "6.x-dev", + "dev-master": "6.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Spatie\\Permission\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Permission handling for Laravel 8.0 and up", + "homepage": "https://github.com/spatie/laravel-permission", + "keywords": [ + "acl", + "laravel", + "permission", + "permissions", + "rbac", + "roles", + "security", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-permission/issues", + "source": "https://github.com/spatie/laravel-permission/tree/6.16.0" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2025-02-28T20:29:57+00:00" + }, { "name": "symfony/clock", "version": "v7.2.0", diff --git a/config/filament-spatie-roles-permissions.php b/config/filament-spatie-roles-permissions.php new file mode 100644 index 0000000..82bf9c6 --- /dev/null +++ b/config/filament-spatie-roles-permissions.php @@ -0,0 +1,218 @@ + [ + 'PermissionResource' => \Althinect\FilamentSpatieRolesPermissions\Resources\PermissionResource::class, + 'RoleResource' => \Althinect\FilamentSpatieRolesPermissions\Resources\RoleResource::class, + ], + + 'preload_roles' => true, + + 'preload_permissions' => true, + + 'navigation_section_group' => 'filament-spatie-roles-permissions::filament-spatie.section.roles_and_permissions', // Default uses language constant + + // 'team_model' => \App\Models\Team::class, + + 'scope_to_tenant' => true, + + 'super_admin_role_name' => 'Super Admin', + + /* + * Set as false to remove from navigation. + */ + 'should_register_on_navigation' => [ + 'permissions' => true, + 'roles' => true, + ], + + 'should_show_permissions_for_roles' => true, + + /* + * Set as true to use simple modal resource. + */ + 'should_use_simple_modal_resource' => [ + 'permissions' => false, + 'roles' => false, + ], + + /* + * Set as true to remove empty state actions. + */ + 'should_remove_empty_state_actions' => [ + 'permissions' => false, + 'roles' => false, + ], + + /** + * Set to true to redirect to the resource index instead of the view + */ + 'should_redirect_to_index' => [ + 'permissions' => [ + 'after_create' => false, + 'after_edit' => false, + ], + 'roles' => [ + 'after_create' => false, + 'after_edit' => false, + ], + ], + + /** + * Set to true to display relation managers in the resources + */ + 'should_display_relation_managers' => [ + 'permissions' => true, + 'users' => true, + 'roles' => true, + ], + + /* + * If you want to place the Resource in a Cluster, then set the required Cluster class. + * Eg. \App\Filament\Clusters\Cluster::class + */ + 'clusters' => [ + 'permissions' => null, + 'roles' => null, + ], + + 'guard_names' => [ + 'web' => 'web', + 'api' => 'api', + ], + + 'toggleable_guard_names' => [ + 'roles' => [ + 'isToggledHiddenByDefault' => true, + ], + 'permissions' => [ + 'isToggledHiddenByDefault' => true, + ], + ], + + 'default_guard_name' => null, + + // if false guard option will not be show on screen. You should set a default_guard_name in this case + 'should_show_guard' => true, + + 'model_filter_key' => 'return \'%\'.$value;', // Eg: 'return \'%\'.$key.'\%\';' + + 'user_name_column' => 'name', + + /* + * If user_name_column is an accessor from a model, then list columns to search. + * Default: null, will search by user_name_column + * + * Example: + * + * 'user_name_searchable_columns' => ['first_name', 'last_name'] + * + * and in your model: + * + * public function getFullNameAttribute() { + * return $this->first_name . ' ' . $this->last_name; + * } + * + */ + 'user_name_searchable_columns' => ['name'], + + /* + * Icons to use for navigation + */ + 'icons' => [ + 'role_navigation' => 'heroicon-o-lock-closed', + 'permission_navigation' => 'heroicon-o-lock-closed', + ], + + /* + * Navigation items order - int value, false restores the default position + */ + + 'sort' => [ + 'role_navigation' => false, + 'permission_navigation' => false, + ], + + 'generator' => [ + + 'guard_names' => [ + 'web', + 'api', + ], + + 'permission_affixes' => [ + + /* + * Permissions Aligned with Policies. + * DO NOT change the keys unless the genericPolicy.stub is published and altered accordingly + */ + 'viewAnyPermission' => 'view-any', + 'viewPermission' => 'view', + 'createPermission' => 'create', + 'updatePermission' => 'update', + 'deletePermission' => 'delete', + 'deleteAnyPermission' => 'delete-any', + 'replicatePermission' => 'replicate', + 'restorePermission' => 'restore', + 'restoreAnyPermission' => 'restore-any', + 'reorderPermission' => 'reorder', + 'forceDeletePermission' => 'force-delete', + 'forceDeleteAnyPermission' => 'force-delete-any', + ], + + /* + * returns the "name" for the permission. + * + * $permission which is an iteration of [permission_affixes] , + * $model The model to which the $permission will be concatenated + * + * Eg: 'permission_name' => 'return $permissionAffix . ' ' . Str::kebab($modelName), + * + * Note: If you are changing the "permission_name" , It's recommended to run with --clean to avoid duplications + */ + 'permission_name' => 'return $permissionAffix . \' \' . $modelName;', + + /* + * Permissions will be generated for the models associated with the respective Filament Resources + */ + 'discover_models_through_filament_resources' => false, + + /* + * Include directories which consists of models. + */ + 'model_directories' => [ + app_path('Models'), + // app_path('Domains/Forum') + ], + + /* + * Define custom_models + */ + 'custom_models' => [ + // + ], + + /* + * Define excluded_models + */ + 'excluded_models' => [ + // + ], + + 'excluded_policy_models' => [ + \App\Models\User::class, + ], + + /* + * Define any other permission that should be synced with the DB + */ + 'custom_permissions' => [ + // 'view-log' + ], + + 'user_model' => \App\Models\User::class, + + 'policies_namespace' => 'App\Policies', + ], +]; diff --git a/config/filament.php b/config/filament.php new file mode 100644 index 0000000..65b4bf0 --- /dev/null +++ b/config/filament.php @@ -0,0 +1,89 @@ + [ + + // 'echo' => [ + // 'broadcaster' => 'pusher', + // 'key' => env('VITE_PUSHER_APP_KEY'), + // 'cluster' => env('VITE_PUSHER_APP_CLUSTER'), + // 'wsHost' => env('VITE_PUSHER_HOST'), + // 'wsPort' => env('VITE_PUSHER_PORT'), + // 'wssPort' => env('VITE_PUSHER_PORT'), + // 'authEndpoint' => '/broadcasting/auth', + // 'disableStats' => true, + // 'encrypted' => true, + // 'forceTLS' => true, + // ], + + ], + + /* + |-------------------------------------------------------------------------- + | Default Filesystem Disk + |-------------------------------------------------------------------------- + | + | This is the storage disk Filament will use to store files. You may use + | any of the disks defined in the `config/filesystems.php`. + | + */ + + 'default_filesystem_disk' => env('FILAMENT_FILESYSTEM_DISK', 'public'), + + /* + |-------------------------------------------------------------------------- + | Assets Path + |-------------------------------------------------------------------------- + | + | This is the directory where Filament's assets will be published to. It + | is relative to the `public` directory of your Laravel application. + | + | After changing the path, you should run `php artisan filament:assets`. + | + */ + + 'assets_path' => null, + + /* + |-------------------------------------------------------------------------- + | Cache Path + |-------------------------------------------------------------------------- + | + | This is the directory that Filament will use to store cache files that + | are used to optimize the registration of components. + | + | After changing the path, you should run `php artisan filament:cache-components`. + | + */ + + 'cache_path' => base_path('bootstrap/cache/filament'), + + /* + |-------------------------------------------------------------------------- + | Livewire Loading Delay + |-------------------------------------------------------------------------- + | + | This sets the delay before loading indicators appear. + | + | Setting this to 'none' makes indicators appear immediately, which can be + | desirable for high-latency connections. Setting it to 'default' applies + | Livewire's standard 200ms delay. + | + */ + + 'livewire_loading_delay' => 'default', + +]; diff --git a/config/permission.php b/config/permission.php new file mode 100644 index 0000000..8e84e9d --- /dev/null +++ b/config/permission.php @@ -0,0 +1,202 @@ + [ + + /* + * 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', + ], +]; diff --git a/database/migrations/2025_03_19_161954_create_companies_table.php b/database/migrations/2025_03_19_161954_create_companies_table.php index d4a0e15..749561a 100644 --- a/database/migrations/2025_03_19_161954_create_companies_table.php +++ b/database/migrations/2025_03_19_161954_create_companies_table.php @@ -13,7 +13,7 @@ return new class extends Migration $sql = <<<'SQL' CREATE TABLE companies ( id BIGINT GENERATED always AS IDENTITY PRIMARY KEY, - name TEXT NOT NULL UNIQUE, + name CITEXT NOT NULL UNIQUE, created_at TIMESTAMP NOT NULL DEFAULT NOW(), updated_at TIMESTAMP NOT NULL DEFAULT NOW(), diff --git a/database/migrations/2025_03_19_171555_create_plants_table.php b/database/migrations/2025_03_19_171555_create_plants_table.php index bf03ddd..6916201 100644 --- a/database/migrations/2025_03_19_171555_create_plants_table.php +++ b/database/migrations/2025_03_19_171555_create_plants_table.php @@ -15,10 +15,10 @@ return new class extends Migration id BIGINT GENERATED always AS IDENTITY PRIMARY KEY, code INT NOT NULL CHECK (code >= 1000) UNIQUE, - company_id INT NOT NULL, + company_id BIGINT NOT NULL, - name TEXT NOT NULL UNIQUE, -- CITEXT use extension - address TEXT NOT NULL, + name CITEXT NOT NULL UNIQUE, -- CITEXT use extension + address CITEXT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT NOW(), updated_at TIMESTAMP NOT NULL DEFAULT NOW(), @@ -26,7 +26,6 @@ return new class extends Migration FOREIGN KEY (company_id) REFERENCES companies (id) ); - SQL; DB::statement($sql); diff --git a/database/migrations/2025_03_20_115918_create_blocks_table.php b/database/migrations/2025_03_20_115918_create_blocks_table.php new file mode 100644 index 0000000..f161609 --- /dev/null +++ b/database/migrations/2025_03_20_115918_create_blocks_table.php @@ -0,0 +1,39 @@ += 6), + description CITEXT NOT NULL, + + hourly_quantity INT NOT NULL CHECK (hourly_quantity > 0), + + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW(), + deleted_at TIMESTAMP, + + UNIQUE (code, line_id, block_id, plant_id), + FOREIGN KEY (line_id) REFERENCES lines (id), + FOREIGN KEY (block_id) REFERENCES blocks (id), + FOREIGN KEY (plant_id) REFERENCES plants (id) + ); + SQL; + + DB::statement($sql); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('items'); + } +}; diff --git a/database/migrations/2025_03_21_113453_add_status_column_to_shifts_table.php b/database/migrations/2025_03_21_113453_add_status_column_to_shifts_table.php new file mode 100644 index 0000000..5ef1715 --- /dev/null +++ b/database/migrations/2025_03_21_113453_add_status_column_to_shifts_table.php @@ -0,0 +1,29 @@ +uuid('id')->primary(); + $table->string('type'); + $table->morphs('notifiable'); + $table->json('data'); + $table->timestamp('read_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('notifications'); + } +}; diff --git a/database/migrations/2025_03_21_115025_create_imports_table.php b/database/migrations/2025_03_21_115025_create_imports_table.php new file mode 100644 index 0000000..100b7d9 --- /dev/null +++ b/database/migrations/2025_03_21_115025_create_imports_table.php @@ -0,0 +1,35 @@ +id(); + $table->timestamp('completed_at')->nullable(); + $table->string('file_name'); + $table->string('file_path'); + $table->string('importer'); + $table->unsignedInteger('processed_rows')->default(0); + $table->unsignedInteger('total_rows'); + $table->unsignedInteger('successful_rows')->default(0); + $table->foreignId('user_id')->constrained()->cascadeOnDelete(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('imports'); + } +}; diff --git a/database/migrations/2025_03_21_115026_create_exports_table.php b/database/migrations/2025_03_21_115026_create_exports_table.php new file mode 100644 index 0000000..a4aab11 --- /dev/null +++ b/database/migrations/2025_03_21_115026_create_exports_table.php @@ -0,0 +1,35 @@ +id(); + $table->timestamp('completed_at')->nullable(); + $table->string('file_disk'); + $table->string('file_name')->nullable(); + $table->string('exporter'); + $table->unsignedInteger('processed_rows')->default(0); + $table->unsignedInteger('total_rows'); + $table->unsignedInteger('successful_rows')->default(0); + $table->foreignId('user_id')->constrained()->cascadeOnDelete(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('exports'); + } +}; diff --git a/database/migrations/2025_03_21_115027_create_failed_import_rows_table.php b/database/migrations/2025_03_21_115027_create_failed_import_rows_table.php new file mode 100644 index 0000000..260507e --- /dev/null +++ b/database/migrations/2025_03_21_115027_create_failed_import_rows_table.php @@ -0,0 +1,30 @@ +id(); + $table->json('data'); + $table->foreignId('import_id')->constrained()->cascadeOnDelete(); + $table->text('validation_error')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('failed_import_rows'); + } +}; diff --git a/database/migrations/2025_03_21_124542_create_permission_tables.php b/database/migrations/2025_03_21_124542_create_permission_tables.php new file mode 100644 index 0000000..70a120f --- /dev/null +++ b/database/migrations/2025_03_21_124542_create_permission_tables.php @@ -0,0 +1,140 @@ +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']); + } +}; diff --git a/database/migrations/2025_03_22_184847_create_line_stops_table.php b/database/migrations/2025_03_22_184847_create_line_stops_table.php new file mode 100644 index 0000000..01e5348 --- /dev/null +++ b/database/migrations/2025_03_22_184847_create_line_stops_table.php @@ -0,0 +1,46 @@ + 0), + production_quantity INT NOT NULL DEFAULT (0), + + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW(), + deleted_at TIMESTAMP, + + FOREIGN KEY (line_id) REFERENCES lines (id), + FOREIGN KEY (shift_id) REFERENCES shifts (id), + FOREIGN KEY (plant_id) REFERENCES plants (id) + ); + SQL; + + DB::statement($sql); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('production_plans'); + } +}; diff --git a/database/migrations/2025_03_23_163259_create_production_line_stops_table.php b/database/migrations/2025_03_23_163259_create_production_line_stops_table.php new file mode 100644 index 0000000..efbba6e --- /dev/null +++ b/database/migrations/2025_03_23_163259_create_production_line_stops_table.php @@ -0,0 +1,52 @@ +create(); - - User::factory()->create([ - 'name' => 'Test User', - 'email' => 'test@example.com', - ]); + $this->call(RoleSeeder::class); + $this->call(UserSeeder::class); } } diff --git a/database/seeders/RoleSeeder.php b/database/seeders/RoleSeeder.php new file mode 100644 index 0000000..e50ea86 --- /dev/null +++ b/database/seeders/RoleSeeder.php @@ -0,0 +1,20 @@ + 'Super Admin', + 'guard_name' => 'web', + ]); + } +} diff --git a/database/seeders/UserSeeder.php b/database/seeders/UserSeeder.php new file mode 100644 index 0000000..c2fa2b4 --- /dev/null +++ b/database/seeders/UserSeeder.php @@ -0,0 +1,33 @@ + 'Admin', + 'email' => 'admin@cripumps.com', + 'password' => bcrypt('admin'), + ]); + + $user1->assignRole('Super Admin'); + + $user2 = User::create([ + 'name' => 'Admin', + 'email' => 'admin@cripumps.com', + 'password' => bcrypt('admin'), + ]); + + $user2->assignRole('Super Admin'); + + User::factory()->count(10)->create(); + } +} diff --git a/public/assets b/public/assets new file mode 120000 index 0000000..ec9d244 --- /dev/null +++ b/public/assets @@ -0,0 +1 @@ +/home/iot-dev/projects/pds/storage/app/public \ No newline at end of file diff --git a/resources/views/vendor/filament/assets.blade.php b/resources/views/vendor/filament/assets.blade.php new file mode 100644 index 0000000..1cf3bde --- /dev/null +++ b/resources/views/vendor/filament/assets.blade.php @@ -0,0 +1,17 @@ +@if (isset($data)) + +@endif + +@foreach ($assets as $asset) + @if (! $asset->isLoadedOnRequest()) + {{ $asset->getHtml() }} + @endif +@endforeach + + diff --git a/resources/views/vendor/filament/components/actions.blade.php b/resources/views/vendor/filament/components/actions.blade.php new file mode 100644 index 0000000..607d546 --- /dev/null +++ b/resources/views/vendor/filament/components/actions.blade.php @@ -0,0 +1,64 @@ +@php + use Filament\Support\Enums\Alignment; +@endphp + +@props([ + 'actions' => [], + 'alignment' => Alignment::Start, + 'fullWidth' => false, +]) + +@php + if (is_array($actions)) { + $actions = array_filter( + $actions, + fn ($action): bool => $action->isVisible(), + ); + } + + if (! $alignment instanceof Alignment) { + $alignment = filled($alignment) ? (Alignment::tryFrom($alignment) ?? $alignment) : null; + } + + $hasActions = false; + + $hasSlot = ! \Filament\Support\is_slot_empty($slot); + $actionsAreHtmlable = $actions instanceof \Illuminate\Contracts\Support\Htmlable; + + if ($hasSlot) { + $hasActions = true; + } elseif ($actionsAreHtmlable) { + $hasActions = ! \Filament\Support\is_slot_empty($actions); + } else { + $hasActions = filled($actions); + } +@endphp + +@if ($hasActions) +
class([ + 'fi-ac gap-3', + 'flex flex-wrap items-center' => ! $fullWidth, + match ($alignment) { + Alignment::Start, Alignment::Left => 'justify-start', + Alignment::Center => 'justify-center', + Alignment::End, Alignment::Right => 'flex-row-reverse', + Alignment::Between, Alignment::Justify => 'justify-between', + default => $alignment, + } => ! $fullWidth, + 'grid grid-cols-[repeat(auto-fit,minmax(0,1fr))]' => $fullWidth, + ]) + }} + > + @if ($hasSlot) + {{ $slot }} + @elseif ($actionsAreHtmlable) + {{ $actions }} + @else + @foreach ($actions as $action) + {{ $action }} + @endforeach + @endif +
+@endif diff --git a/resources/views/vendor/filament/components/avatar.blade.php b/resources/views/vendor/filament/components/avatar.blade.php new file mode 100644 index 0000000..aa81aae --- /dev/null +++ b/resources/views/vendor/filament/components/avatar.blade.php @@ -0,0 +1,21 @@ +@props([ + 'circular' => true, + 'size' => 'md', +]) + +class([ + 'fi-avatar object-cover object-center', + 'rounded-md' => ! $circular, + 'fi-circular rounded-full' => $circular, + match ($size) { + 'sm' => 'h-6 w-6', + 'md' => 'h-8 w-8', + 'lg' => 'h-10 w-10', + default => $size, + }, + ]) + }} +/> diff --git a/resources/views/vendor/filament/components/badge.blade.php b/resources/views/vendor/filament/components/badge.blade.php new file mode 100644 index 0000000..bc6bcc8 --- /dev/null +++ b/resources/views/vendor/filament/components/badge.blade.php @@ -0,0 +1,238 @@ +@php + use Filament\Support\Enums\ActionSize; + use Filament\Support\Enums\IconPosition; + use Filament\Support\Enums\IconSize; +@endphp + +@props([ + 'color' => 'primary', + 'deleteButton' => null, + 'disabled' => false, + 'form' => null, + 'formId' => null, + 'href' => null, + 'icon' => null, + 'iconAlias' => null, + 'iconPosition' => IconPosition::Before, + 'iconSize' => IconSize::Small, + 'keyBindings' => null, + 'loadingIndicator' => true, + 'size' => ActionSize::Medium, + 'spaMode' => null, + 'tag' => 'span', + 'target' => null, + 'tooltip' => null, + 'type' => 'button', +]) + +@php + if (! $iconPosition instanceof IconPosition) { + $iconPosition = filled($iconPosition) ? (IconPosition::tryFrom($iconPosition) ?? $iconPosition) : null; + } + + if (! $size instanceof ActionSize) { + $size = filled($size) ? (ActionSize::tryFrom($size) ?? $size) : null; + } + + if (! $iconSize instanceof IconSize) { + $iconSize = filled($iconSize) ? (IconSize::tryFrom($iconSize) ?? $iconSize) : null; + } + + $isDeletable = count($deleteButton?->attributes->getAttributes() ?? []) > 0; + + $iconClasses = \Illuminate\Support\Arr::toCssClasses([ + 'fi-badge-icon h-4 w-4', + match ($iconSize) { + IconSize::Small => 'h-4 w-4', + IconSize::Medium => 'h-5 w-5', + IconSize::Large => 'h-6 w-6', + default => $iconSize, + }, + match ($color) { + 'gray' => 'text-gray-400 dark:text-gray-500', + default => 'text-custom-500', + }, + ]); + + $iconStyles = \Illuminate\Support\Arr::toCssStyles([ + \Filament\Support\get_color_css_variables( + $color, + shades: [500], + alias: 'badge.icon', + ) => $color !== 'gray', + ]); + + $wireTarget = $loadingIndicator ? $attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first() : null; + + $hasLoadingIndicator = filled($wireTarget) || ($type === 'submit' && filled($form)); + + if ($hasLoadingIndicator) { + $loadingIndicatorTarget = html_entity_decode($wireTarget ?: $form, ENT_QUOTES); + } + + $hasTooltip = filled($tooltip); +@endphp + +<{{ $tag }} + @if ($tag === 'a') + {{ \Filament\Support\generate_href_html($href, $target === '_blank', $spaMode) }} + @endif + @if ($keyBindings || $hasTooltip) + x-data="{}" + @endif + @if ($keyBindings) + x-bind:id="$id('key-bindings')" + x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()" + @endif + @if ($hasTooltip) + x-tooltip="{ + content: @js($tooltip), + theme: $store.theme, + }" + @endif + {{ + $attributes + ->merge([ + 'disabled' => $disabled, + 'form' => $tag === 'button' ? $formId : null, + 'type' => $tag === 'button' ? $type : null, + 'wire:loading.attr' => $tag === 'button' ? 'disabled' : null, + 'wire:target' => ($hasLoadingIndicator && $loadingIndicatorTarget) ? $loadingIndicatorTarget : null, + ], escape: false) + ->class([ + 'fi-badge flex items-center justify-center gap-x-1 rounded-md text-xs font-medium ring-1 ring-inset', + 'pointer-events-none opacity-70' => $disabled, + match ($size) { + ActionSize::ExtraSmall => 'px-0.5 min-w-[theme(spacing.4)] tracking-tighter', + ActionSize::Small => 'px-1.5 min-w-[theme(spacing.5)] py-0.5 tracking-tight', + ActionSize::Medium, ActionSize::Large, ActionSize::ExtraLarge => 'px-2 min-w-[theme(spacing.6)] py-1', + default => $size, + }, + match ($color) { + 'gray' => 'bg-gray-50 text-gray-600 ring-gray-600/10 dark:bg-gray-400/10 dark:text-gray-400 dark:ring-gray-400/20', + default => 'fi-color-custom bg-custom-50 text-custom-600 ring-custom-600/10 dark:bg-custom-400/10 dark:text-custom-400 dark:ring-custom-400/30', + }, + is_string($color) ? "fi-color-{$color}" : null, + ]) + ->style([ + \Filament\Support\get_color_css_variables( + $color, + shades: [ + 50, + 400, + 600, + ], + alias: 'badge', + ) => $color !== 'gray', + ]) + }} +> + @if ($iconPosition === IconPosition::Before) + @if ($icon) + + @endif + + @if ($hasLoadingIndicator) + + @endif + @endif + + + + {{ $slot }} + + + + @if ($isDeletable) + + @elseif ($iconPosition === IconPosition::After) + @if ($icon) + + @endif + + @if ($hasLoadingIndicator) + + @endif + @endif + diff --git a/resources/views/vendor/filament/components/breadcrumbs.blade.php b/resources/views/vendor/filament/components/breadcrumbs.blade.php new file mode 100644 index 0000000..92a31d2 --- /dev/null +++ b/resources/views/vendor/filament/components/breadcrumbs.blade.php @@ -0,0 +1,50 @@ +@props([ + 'breadcrumbs' => [], +]) + +@php + $iconClasses = 'fi-breadcrumbs-item-separator flex h-5 w-5 text-gray-400 dark:text-gray-500'; + $itemLabelClasses = 'fi-breadcrumbs-item-label text-sm font-medium text-gray-500 dark:text-gray-400'; +@endphp + + diff --git a/resources/views/vendor/filament/components/button/group.blade.php b/resources/views/vendor/filament/components/button/group.blade.php new file mode 100644 index 0000000..d06423b --- /dev/null +++ b/resources/views/vendor/filament/components/button/group.blade.php @@ -0,0 +1,9 @@ +
class([ + 'fi-btn-group grid grid-flow-col rounded-lg shadow-sm ring-1 ring-gray-950/10 dark:ring-white/20', + ]) + }} +> + {{ $slot }} +
diff --git a/resources/views/vendor/filament/components/button/index.blade.php b/resources/views/vendor/filament/components/button/index.blade.php new file mode 100644 index 0000000..1928120 --- /dev/null +++ b/resources/views/vendor/filament/components/button/index.blade.php @@ -0,0 +1,332 @@ +@php + use Filament\Support\Enums\ActionSize; + use Filament\Support\Enums\IconPosition; + use Filament\Support\Enums\IconSize; +@endphp + +@props([ + 'badge' => null, + 'badgeColor' => 'primary', + 'badgeSize' => 'xs', + 'color' => 'primary', + 'disabled' => false, + 'form' => null, + 'formId' => null, + 'grouped' => false, + 'href' => null, + 'icon' => null, + 'iconAlias' => null, + 'iconPosition' => IconPosition::Before, + 'iconSize' => null, + 'keyBindings' => null, + 'labeledFrom' => null, + 'labelSrOnly' => false, + 'loadingIndicator' => true, + 'outlined' => false, + 'size' => ActionSize::Medium, + 'spaMode' => null, + 'tag' => 'button', + 'target' => null, + 'tooltip' => null, + 'type' => 'button', +]) + +@php + if (! $iconPosition instanceof IconPosition) { + $iconPosition = filled($iconPosition) ? (IconPosition::tryFrom($iconPosition) ?? $iconPosition) : null; + } + + if (! $size instanceof ActionSize) { + $size = filled($size) ? (ActionSize::tryFrom($size) ?? $size) : null; + } + + $iconSize ??= match ($size) { + ActionSize::ExtraSmall, ActionSize::Small => IconSize::Small, + default => IconSize::Medium, + }; + + if (! $iconSize instanceof IconSize) { + $iconSize = filled($iconSize) ? (IconSize::tryFrom($iconSize) ?? $iconSize) : null; + } + + $buttonClasses = \Illuminate\Support\Arr::toCssClasses([ + ...[ + 'fi-btn relative grid-flow-col items-center justify-center font-semibold outline-none transition duration-75 focus-visible:ring-2', + 'pointer-events-none opacity-70' => $disabled, + 'rounded-lg' => ! $grouped, + 'flex-1 [&:nth-child(1_of_.fi-btn)]:rounded-s-lg [&:nth-last-child(1_of_.fi-btn)]:rounded-e-lg [&:not(:nth-child(1_of_.fi-btn))]:shadow-[-1px_0_0_0_theme(colors.gray.200)] [&:not(:nth-last-child(1_of_.fi-btn))]:me-px dark:[&:not(:nth-child(1_of_.fi-btn))]:shadow-[-1px_0_0_0_theme(colors.white/20%)]' => $grouped, + 'cursor-pointer' => $tag === 'label', + match ($color) { + 'gray' => null, + default => 'fi-color-custom', + }, + // @deprecated `fi-btn-color-*` has been replaced by `fi-color-*` and `fi-color-custom`. + is_string($color) ? "fi-btn-color-{$color}" : null, + is_string($color) ? "fi-color-{$color}" : null, + ($size instanceof ActionSize) ? "fi-size-{$size->value}" : null, + // @deprecated `fi-btn-size-*` has been replaced by `fi-size-*`. + ($size instanceof ActionSize) ? "fi-btn-size-{$size->value}" : null, + match ($size) { + ActionSize::ExtraSmall => 'gap-1 px-2 py-1.5 text-xs', + ActionSize::Small => 'gap-1 px-2.5 py-1.5 text-sm', + ActionSize::Medium => 'gap-1.5 px-3 py-2 text-sm', + ActionSize::Large => 'gap-1.5 px-3.5 py-2.5 text-sm', + ActionSize::ExtraLarge => 'gap-1.5 px-4 py-3 text-sm', + default => $size, + }, + 'hidden' => $labeledFrom, + match ($labeledFrom) { + 'sm' => 'sm:inline-grid', + 'md' => 'md:inline-grid', + 'lg' => 'lg:inline-grid', + 'xl' => 'xl:inline-grid', + '2xl' => '2xl:inline-grid', + default => 'inline-grid', + }, + ], + ...( + $outlined ? + [ + 'fi-btn-outlined ring-1', + match ($color) { + 'gray' => 'text-gray-950 ring-gray-300 hover:bg-gray-400/10 focus-visible:ring-gray-400/40 dark:text-white dark:ring-gray-700', + default => 'text-custom-600 ring-custom-600 hover:bg-custom-400/10 dark:text-custom-400 dark:ring-custom-500', + }, + ] : + [ + 'shadow-sm' => ! $grouped, + 'bg-white text-gray-950 hover:bg-gray-50 dark:bg-white/5 dark:text-white dark:hover:bg-white/10' => ($color === 'gray') || ($tag === 'label'), + 'ring-1 ring-gray-950/10 dark:ring-white/20' => (($color === 'gray') || ($tag === 'label')) && (! $grouped), + 'bg-custom-600 text-white hover:bg-custom-500 focus-visible:ring-custom-500/50 dark:bg-custom-500 dark:hover:bg-custom-400 dark:focus-visible:ring-custom-400/50' => ($color !== 'gray') && ($tag !== 'label'), + '[input:checked+&]:bg-custom-600 [input:checked+&]:text-white [input:checked+&]:ring-0 [input:checked+&]:hover:bg-custom-500 dark:[input:checked+&]:bg-custom-500 dark:[input:checked+&]:hover:bg-custom-400 [input:checked:focus-visible+&]:ring-custom-500/50 dark:[input:checked:focus-visible+&]:ring-custom-400/50 [input:focus-visible+&]:z-10 [input:focus-visible+&]:ring-2 [input:focus-visible+&]:ring-gray-950/10 dark:[input:focus-visible+&]:ring-white/20' => ($color !== 'gray') && ($tag === 'label'), + '[input:checked+&]:bg-gray-400 [input:checked+&]:text-white [input:checked+&]:ring-0 [input:checked+&]:hover:bg-gray-300 dark:[input:checked+&]:bg-gray-600 dark:[input:checked+&]:hover:bg-gray-500' => ($color === 'gray'), + ] + ), + ]); + + $buttonStyles = \Illuminate\Support\Arr::toCssStyles([ + \Filament\Support\get_color_css_variables( + $color, + shades: [400, 500, 600], + alias: 'button', + ) => $color !== 'gray', + ]); + + $iconClasses = \Illuminate\Support\Arr::toCssClasses([ + 'fi-btn-icon transition duration-75', + match ($iconSize) { + IconSize::Small => 'h-4 w-4', + IconSize::Medium => 'h-5 w-5', + IconSize::Large => 'h-6 w-6', + default => $iconSize, + }, + 'text-gray-400 dark:text-gray-500' => ($color === 'gray') || ($tag === 'label'), + 'text-white' => ($color !== 'gray') && ($tag !== 'label') && (! $outlined), + '[:checked+*>&]:text-white' => $tag === 'label', + ]); + + $badgeContainerClasses = 'fi-btn-badge-ctn absolute start-full top-0 z-[1] w-max -translate-x-1/2 -translate-y-1/2 rounded-md bg-white dark:bg-gray-900 rtl:translate-x-1/2'; + + $labelClasses = \Illuminate\Support\Arr::toCssClasses([ + 'fi-btn-label', + 'sr-only' => $labelSrOnly, + ]); + + $wireTarget = $loadingIndicator ? $attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first() : null; + + $hasFormProcessingLoadingIndicator = $type === 'submit' && filled($form); + $hasLoadingIndicator = filled($wireTarget) || $hasFormProcessingLoadingIndicator; + + if ($hasLoadingIndicator) { + $loadingIndicatorTarget = html_entity_decode($wireTarget ?: $form, ENT_QUOTES); + } + + $hasTooltip = filled($tooltip); +@endphp + +@if ($labeledFrom) + +@endif + +<{{ $tag }} + @if ($tag === 'a') + {{ \Filament\Support\generate_href_html($href, $target === '_blank', $spaMode) }} + @endif + @if (($keyBindings || $hasTooltip) && (! $hasFormProcessingLoadingIndicator)) + x-data="{}" + @endif + @if ($keyBindings) + x-bind:id="$id('key-bindings')" + x-mousetrap.global.{{ collect($keyBindings)->map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()" + @endif + @if ($hasTooltip) + x-tooltip="{ + content: @js($tooltip), + theme: $store.theme, + }" + @endif + @if ($hasFormProcessingLoadingIndicator) + x-data="{ + form: null, + isProcessing: false, + processingMessage: null, + }" + x-init=" + form = $el.closest('form') + + form?.addEventListener('form-processing-started', (event) => { + isProcessing = true + processingMessage = event.detail.message + }) + + form?.addEventListener('form-processing-finished', () => { + isProcessing = false + }) + " + x-bind:class="{ 'enabled:opacity-70 enabled:cursor-wait': isProcessing }" + @endif + {{ + $attributes + ->merge([ + 'disabled' => $disabled, + 'form' => $formId, + 'type' => $tag === 'button' ? $type : null, + 'wire:loading.attr' => $tag === 'button' ? 'disabled' : null, + 'wire:target' => ($hasLoadingIndicator && $loadingIndicatorTarget) ? $loadingIndicatorTarget : null, + 'x-bind:disabled' => $hasFormProcessingLoadingIndicator ? 'isProcessing' : null, + ], escape: false) + ->class([$buttonClasses]) + ->style([$buttonStyles]) + }} +> + @if ($iconPosition === IconPosition::Before) + @if ($icon) + + @endif + + @if ($hasLoadingIndicator) + + @endif + + @if ($hasFormProcessingLoadingIndicator) + + @endif + @endif + + + {{ $slot }} + + + @if ($hasFormProcessingLoadingIndicator) + + @endif + + @if ($iconPosition === IconPosition::After) + @if ($icon) + + @endif + + @if ($hasLoadingIndicator) + + @endif + + @if ($hasFormProcessingLoadingIndicator) + + @endif + @endif + + @if (filled($badge)) +
+ + {{ $badge }} + +
+ @endif + diff --git a/resources/views/vendor/filament/components/card.blade.php b/resources/views/vendor/filament/components/card.blade.php new file mode 100644 index 0000000..141f864 --- /dev/null +++ b/resources/views/vendor/filament/components/card.blade.php @@ -0,0 +1,3 @@ + + {{ $slot }} + diff --git a/resources/views/vendor/filament/components/dropdown/header.blade.php b/resources/views/vendor/filament/components/dropdown/header.blade.php new file mode 100644 index 0000000..dab4683 --- /dev/null +++ b/resources/views/vendor/filament/components/dropdown/header.blade.php @@ -0,0 +1,71 @@ +@php + use Filament\Support\Enums\IconSize; +@endphp + +@props([ + 'color' => 'gray', + 'icon' => null, + 'iconSize' => IconSize::Medium, + 'tag' => 'div', +]) + +<{{ $tag }} + {{ + $attributes + ->class([ + 'fi-dropdown-header flex w-full gap-2 p-3 text-sm', + match ($color) { + 'gray' => null, + default => 'fi-color-custom', + }, + // @deprecated `fi-dropdown-header-color-*` has been replaced by `fi-color-*` and `fi-color-custom`. + is_string($color) ? "fi-dropdown-header-color-{$color}" : null, + is_string($color) ? "fi-color-{$color}" : null, + ]) + }} +> + @if (filled($icon)) + 'h-4 w-4', + IconSize::Medium, 'md' => 'h-5 w-5', + IconSize::Large, 'lg' => 'h-6 w-6', + default => $iconSize, + }, + match ($color) { + 'gray' => 'text-gray-400 dark:text-gray-500', + default => 'text-custom-500 dark:text-custom-400', + }, + ]) + @style([ + \Filament\Support\get_color_css_variables( + $color, + shades: [400, 500], + alias: 'dropdown.header.icon', + ) => $color !== 'gray', + ]) + /> + @endif + + 'text-gray-700 dark:text-gray-200', + default => 'text-custom-600 dark:text-custom-400', + }, + ]) + @style([ + \Filament\Support\get_color_css_variables( + $color, + shades: [400, 600], + alias: 'dropdown.header.label', + ) => $color !== 'gray', + ]) + > + {{ $slot }} + + diff --git a/resources/views/vendor/filament/components/dropdown/index.blade.php b/resources/views/vendor/filament/components/dropdown/index.blade.php new file mode 100644 index 0000000..551571a --- /dev/null +++ b/resources/views/vendor/filament/components/dropdown/index.blade.php @@ -0,0 +1,87 @@ +@props([ + 'availableHeight' => null, + 'availableWidth' => null, + 'flip' => true, + 'maxHeight' => null, + 'offset' => 8, + 'placement' => null, + 'shift' => false, + 'size' => false, + 'sizePadding' => 16, + 'teleport' => false, + 'trigger' => null, + 'width' => null, +]) + +@php + use Filament\Support\Enums\MaxWidth; + + $sizeConfig = collect([ + 'availableHeight' => $availableHeight, + 'availableWidth' => $availableWidth, + 'padding' => $sizePadding, + ])->filter()->toJson(); +@endphp + +
class(['fi-dropdown']) }} +> +
attributes->class(['fi-dropdown-trigger flex cursor-pointer']) }} + > + {{ $trigger }} +
+ + @if (! \Filament\Support\is_slot_empty($slot)) +
has('wire:key')) + wire:ignore.self + wire:key="{{ $attributes->get('wire:key') }}.panel" + @endif + @class([ + 'fi-dropdown-panel absolute z-10 w-screen divide-y divide-gray-100 rounded-lg bg-white shadow-lg ring-1 ring-gray-950/5 transition dark:divide-white/5 dark:bg-gray-900 dark:ring-white/10', + match ($width) { + // These max width classes need to be `!important` otherwise they will be usurped by the Floating UI "size" middleware. + MaxWidth::ExtraSmall, 'xs' => '!max-w-xs', + MaxWidth::Small, 'sm' => '!max-w-sm', + MaxWidth::Medium, 'md' => '!max-w-md', + MaxWidth::Large, 'lg' => '!max-w-lg', + MaxWidth::ExtraLarge, 'xl' => '!max-w-xl', + MaxWidth::TwoExtraLarge, '2xl' => '!max-w-2xl', + MaxWidth::ThreeExtraLarge, '3xl' => '!max-w-3xl', + MaxWidth::FourExtraLarge, '4xl' => '!max-w-4xl', + MaxWidth::FiveExtraLarge, '5xl' => '!max-w-5xl', + MaxWidth::SixExtraLarge, '6xl' => '!max-w-6xl', + MaxWidth::SevenExtraLarge, '7xl' => '!max-w-7xl', + null => '!max-w-[14rem]', + default => $width, + }, + 'overflow-y-auto' => $maxHeight || $size, + ]) + @style([ + "max-height: {$maxHeight}" => $maxHeight, + ]) + > + {{ $slot }} +
+ @endif +
diff --git a/resources/views/vendor/filament/components/dropdown/list/index.blade.php b/resources/views/vendor/filament/components/dropdown/list/index.blade.php new file mode 100644 index 0000000..fceb894 --- /dev/null +++ b/resources/views/vendor/filament/components/dropdown/list/index.blade.php @@ -0,0 +1,3 @@ +
class(['fi-dropdown-list p-1']) }}> + {{ $slot }} +
diff --git a/resources/views/vendor/filament/components/dropdown/list/item.blade.php b/resources/views/vendor/filament/components/dropdown/list/item.blade.php new file mode 100644 index 0000000..99b9cf7 --- /dev/null +++ b/resources/views/vendor/filament/components/dropdown/list/item.blade.php @@ -0,0 +1,275 @@ +@php + use Filament\Support\Enums\IconSize; +@endphp + +@props([ + 'badge' => null, + 'badgeColor' => null, + 'badgeTooltip' => null, + 'color' => 'gray', + 'disabled' => false, + 'href' => null, + 'icon' => null, + 'iconAlias' => null, + 'iconColor' => null, + 'iconSize' => IconSize::Medium, + 'image' => null, + 'keyBindings' => null, + 'loadingIndicator' => true, + 'spaMode' => null, + 'tag' => 'button', + 'target' => null, + 'tooltip' => null, +]) + +@php + $buttonClasses = \Illuminate\Support\Arr::toCssClasses([ + 'fi-dropdown-list-item flex w-full items-center gap-2 whitespace-nowrap rounded-md p-2 text-sm transition-colors duration-75 outline-none disabled:pointer-events-none disabled:opacity-70', + 'pointer-events-none opacity-70' => $disabled, + match ($color) { + 'gray' => 'hover:bg-gray-50 focus-visible:bg-gray-50 dark:hover:bg-white/5 dark:focus-visible:bg-white/5', + default => 'fi-color-custom hover:bg-custom-50 focus-visible:bg-custom-50 dark:hover:bg-custom-400/10 dark:focus-visible:bg-custom-400/10', + }, + // @deprecated `fi-dropdown-list-item-color-*` has been replaced by `fi-color-*` and `fi-color-custom`. + is_string($color) ? "fi-dropdown-list-item-color-{$color}" : null, + is_string($color) ? "fi-color-{$color}" : null, + ]); + + $buttonStyles = \Illuminate\Support\Arr::toCssStyles([ + \Filament\Support\get_color_css_variables( + $color, + shades: [50, 400], + alias: 'dropdown.list.item', + ) => $color !== 'gray', + ]); + + $iconColor ??= $color; + + $iconClasses = \Illuminate\Support\Arr::toCssClasses([ + 'fi-dropdown-list-item-icon', + match ($iconSize) { + IconSize::Small, 'sm' => 'h-4 w-4', + IconSize::Medium, 'md' => 'h-5 w-5', + IconSize::Large, 'lg' => 'h-6 w-6', + default => $iconSize, + }, + match ($iconColor) { + 'gray' => 'text-gray-400 dark:text-gray-500', + default => 'text-custom-500 dark:text-custom-400', + }, + ]); + + $iconStyles = \Illuminate\Support\Arr::toCssStyles([ + \Filament\Support\get_color_css_variables( + $iconColor, + shades: [400, 500], + alias: 'dropdown.list.item.icon', + ) => $iconColor !== 'gray', + ]); + + $imageClasses = 'fi-dropdown-list-item-image h-5 w-5 rounded-full bg-cover bg-center'; + + $labelClasses = \Illuminate\Support\Arr::toCssClasses([ + 'fi-dropdown-list-item-label flex-1 truncate text-start', + match ($color) { + 'gray' => 'text-gray-700 dark:text-gray-200', + default => 'text-custom-600 dark:text-custom-400 ', + }, + ]); + + $labelStyles = \Illuminate\Support\Arr::toCssStyles([ + \Filament\Support\get_color_css_variables( + $color, + shades: [400, 600], + alias: 'dropdown.list.item.label', + ) => $color !== 'gray', + ]); + + $wireTarget = $loadingIndicator ? $attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first() : null; + + $hasLoadingIndicator = filled($wireTarget); + + if ($hasLoadingIndicator) { + $loadingIndicatorTarget = html_entity_decode($wireTarget, ENT_QUOTES); + } + + $hasTooltip = filled($tooltip); +@endphp + +@if ($tag === 'button') + +@elseif ($tag === 'a') + map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()" + @endif + @if ($hasTooltip) + x-tooltip="{ + content: @js($tooltip), + theme: $store.theme, + }" + @endif + {{ + $attributes + ->class([$buttonClasses]) + ->style([$buttonStyles]) + }} + > + @if ($icon) + + @endif + + @if ($image) +
+ @endif + + + {{ $slot }} + + + @if (filled($badge)) + + {{ $badge }} + + @endif +
+@elseif ($tag === 'form') +
only(['action', 'class', 'method', 'wire:submit']) }} + > + @csrf + + +
+@endif diff --git a/resources/views/vendor/filament/components/fieldset.blade.php b/resources/views/vendor/filament/components/fieldset.blade.php new file mode 100644 index 0000000..7c8370f --- /dev/null +++ b/resources/views/vendor/filament/components/fieldset.blade.php @@ -0,0 +1,25 @@ +@props([ + 'label' => null, + 'labelHidden' => false, +]) + +
class([ + 'fi-fieldset rounded-xl border border-gray-200 p-6 dark:border-white/10', + ]) + }} +> + @if (filled($label)) + $labelHidden, + ]) + > + {{ $label }} + + @endif + + {{ $slot }} +
diff --git a/resources/views/vendor/filament/components/grid/column.blade.php b/resources/views/vendor/filament/components/grid/column.blade.php new file mode 100644 index 0000000..119dbcf --- /dev/null +++ b/resources/views/vendor/filament/components/grid/column.blade.php @@ -0,0 +1,62 @@ +@props([ + 'default' => 1, + 'sm' => null, + 'md' => null, + 'lg' => null, + 'xl' => null, + 'twoXl' => null, + 'defaultStart' => null, + 'smStart' => null, + 'mdStart' => null, + 'lgStart' => null, + 'xlStart' => null, + 'twoXlStart' => null, + 'hidden' => false, +]) + +@php + $getSpanValue = function ($span): string { + if ($span === 'full') { + return '1 / -1'; + } + + return "span {$span} / span {$span}"; + }; +@endphp + +
class([ + 'hidden' => $hidden || $default === 'hidden', + 'col-[--col-span-default]' => $default && (! $hidden), + 'sm:col-[--col-span-sm]' => $sm && (! $hidden), + 'md:col-[--col-span-md]' => $md && (! $hidden), + 'lg:col-[--col-span-lg]' => $lg && (! $hidden), + 'xl:col-[--col-span-xl]' => $xl && (! $hidden), + '2xl:col-[--col-span-2xl]' => $twoXl && (! $hidden), + 'col-start-[--col-start-default]' => $defaultStart && (! $hidden), + 'sm:col-start-[--col-start-sm]' => $smStart && (! $hidden), + 'md:col-start-[--col-start-md]' => $mdStart && (! $hidden), + 'lg:col-start-[--col-start-lg]' => $lgStart && (! $hidden), + 'xl:col-start-[--col-start-xl]' => $xlStart && (! $hidden), + '2xl:col-start-[--col-start-2xl]' => $twoXlStart && (! $hidden), + ]) + ->style([ + "--col-span-default: {$getSpanValue($default)}" => $default, + "--col-span-sm: {$getSpanValue($sm)}" => $sm, + "--col-span-md: {$getSpanValue($md)}" => $md, + "--col-span-lg: {$getSpanValue($lg)}" => $lg, + "--col-span-xl: {$getSpanValue($xl)}" => $xl, + "--col-span-2xl: {$getSpanValue($twoXl)}" => $twoXl, + "--col-start-default: {$defaultStart}" => $defaultStart, + "--col-start-sm: {$smStart}" => $smStart, + "--col-start-md: {$mdStart}" => $mdStart, + "--col-start-lg: {$lgStart}" => $lgStart, + "--col-start-xl: {$xlStart}" => $xlStart, + "--col-start-2xl: {$twoXlStart}" => $twoXlStart, + ]) + }} +> + {{ $slot }} +
diff --git a/resources/views/vendor/filament/components/grid/index.blade.php b/resources/views/vendor/filament/components/grid/index.blade.php new file mode 100644 index 0000000..c4fba7b --- /dev/null +++ b/resources/views/vendor/filament/components/grid/index.blade.php @@ -0,0 +1,53 @@ +@props([ + 'isGrid' => true, + 'default' => 1, + 'direction' => 'row', + 'sm' => null, + 'md' => null, + 'lg' => null, + 'xl' => null, + 'twoXl' => null, +]) + +
class([ + 'grid' => $isGrid && $direction === 'row', + 'grid-cols-[--cols-default]' => $default && ($direction === 'row'), + 'columns-[--cols-default]' => $default && ($direction === 'column'), + 'sm:grid-cols-[--cols-sm]' => $sm && ($direction === 'row'), + 'sm:columns-[--cols-sm]' => $sm && ($direction === 'column'), + 'md:grid-cols-[--cols-md]' => $md && ($direction === 'row'), + 'md:columns-[--cols-md]' => $md && ($direction === 'column'), + 'lg:grid-cols-[--cols-lg]' => $lg && ($direction === 'row'), + 'lg:columns-[--cols-lg]' => $lg && ($direction === 'column'), + 'xl:grid-cols-[--cols-xl]' => $xl && ($direction === 'row'), + 'xl:columns-[--cols-xl]' => $xl && ($direction === 'column'), + '2xl:grid-cols-[--cols-2xl]' => $twoXl && ($direction === 'row'), + '2xl:columns-[--cols-2xl]' => $twoXl && ($direction === 'column'), + ]) + ->style( + match ($direction) { + 'column' => [ + "--cols-default: {$default}" => $default, + "--cols-sm: {$sm}" => $sm, + "--cols-md: {$md}" => $md, + "--cols-lg: {$lg}" => $lg, + "--cols-xl: {$xl}" => $xl, + "--cols-2xl: {$twoXl}" => $twoXl, + ], + 'row' => [ + "--cols-default: repeat({$default}, minmax(0, 1fr))" => $default, + "--cols-sm: repeat({$sm}, minmax(0, 1fr))" => $sm, + "--cols-md: repeat({$md}, minmax(0, 1fr))" => $md, + "--cols-lg: repeat({$lg}, minmax(0, 1fr))" => $lg, + "--cols-xl: repeat({$xl}, minmax(0, 1fr))" => $xl, + "--cols-2xl: repeat({$twoXl}, minmax(0, 1fr))" => $twoXl, + ], + }, + ) + }} +> + {{ $slot }} +
diff --git a/resources/views/vendor/filament/components/icon-button.blade.php b/resources/views/vendor/filament/components/icon-button.blade.php new file mode 100644 index 0000000..f700788 --- /dev/null +++ b/resources/views/vendor/filament/components/icon-button.blade.php @@ -0,0 +1,242 @@ +@php + use Filament\Support\Enums\ActionSize; + use Filament\Support\Enums\IconSize; +@endphp + +@props([ + 'badge' => null, + 'badgeColor' => 'primary', + 'badgeSize' => 'xs', + 'color' => 'primary', + 'disabled' => false, + 'form' => null, + 'formId' => null, + 'href' => null, + 'icon' => null, + 'iconAlias' => null, + 'iconSize' => null, + 'keyBindings' => null, + 'label' => null, + 'loadingIndicator' => true, + 'size' => ActionSize::Medium, + 'spaMode' => null, + 'tag' => 'button', + 'target' => null, + 'tooltip' => null, + 'type' => 'button', +]) + +@php + if (! $size instanceof ActionSize) { + $size = filled($size) ? (ActionSize::tryFrom($size) ?? $size) : null; + } + + $iconSize ??= match ($size) { + ActionSize::ExtraSmall => IconSize::Small, + ActionSize::Small, ActionSize::Medium => IconSize::Medium, + ActionSize::Large, ActionSize::ExtraLarge => IconSize::Large, + default => IconSize::Medium, + }; + + if (! $iconSize instanceof IconSize) { + $iconSize = filled($iconSize) ? (IconSize::tryFrom($iconSize) ?? $iconSize) : null; + } + + $buttonClasses = \Illuminate\Support\Arr::toCssClasses([ + 'fi-icon-btn relative flex items-center justify-center rounded-lg outline-none transition duration-75 focus-visible:ring-2', + 'pointer-events-none opacity-70' => $disabled, + ...match ($size) { + ActionSize::ExtraSmall => [ + match ($iconSize) { + IconSize::Small => '-m-1.5', + IconSize::Medium => '-m-1', + IconSize::Large => '-m-0.5', + }, + 'h-7 w-7', + ], + ActionSize::Small => [ + match ($iconSize) { + IconSize::Small => '-m-2', + IconSize::Medium => '-m-1.5', + IconSize::Large => '-m-1', + }, + 'h-8 w-8', + ], + ActionSize::Medium => [ + match ($iconSize) { + IconSize::Small => '-m-2.5', + IconSize::Medium => '-m-2', + IconSize::Large => '-m-1.5', + }, + 'h-9 w-9', + ], + ActionSize::Large => [ + match ($iconSize) { + IconSize::Small => '-m-3', + IconSize::Medium => '-m-2.5', + IconSize::Large => '-m-2', + }, + 'h-10 w-10', + ], + ActionSize::ExtraLarge => [ + match ($iconSize) { + IconSize::Small => '-m-3.5', + IconSize::Medium => '-m-3', + IconSize::Large => '-m-2.5', + }, + 'h-11 w-11', + ], + }, + match ($color) { + 'gray' => 'text-gray-400 hover:text-gray-500 focus-visible:ring-primary-600 dark:text-gray-500 dark:hover:text-gray-400 dark:focus-visible:ring-primary-500', + default => 'fi-color-custom text-custom-500 hover:text-custom-600 focus-visible:ring-custom-600 dark:text-custom-400 dark:hover:text-custom-300 dark:focus-visible:ring-custom-500', + }, + is_string($color) ? "fi-color-{$color}" : null, + ]); + + $buttonStyles = \Filament\Support\get_color_css_variables( + $color, + shades: [300, 400, 500, 600], + alias: 'icon-button', + ); + + $iconClasses = \Illuminate\Support\Arr::toCssClasses([ + 'fi-icon-btn-icon', + match ($iconSize) { + IconSize::Small => 'h-4 w-4', + IconSize::Medium => 'h-5 w-5', + IconSize::Large => 'h-6 w-6', + default => $iconSize, + }, + ]); + + $badgeContainerClasses = 'fi-icon-btn-badge-ctn absolute start-full top-1 z-[1] w-max -translate-x-1/2 -translate-y-1/2 rounded-md bg-white dark:bg-gray-900 rtl:translate-x-1/2'; + + $wireTarget = $loadingIndicator ? $attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first() : null; + + $hasLoadingIndicator = filled($wireTarget) || ($type === 'submit' && filled($form)); + + if ($hasLoadingIndicator) { + $loadingIndicatorTarget = html_entity_decode($wireTarget ?: $form, ENT_QUOTES); + } + + $hasTooltip = filled($tooltip); +@endphp + +@if ($tag === 'button') + +@elseif ($tag === 'a') + map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()" + @endif + @if ($hasTooltip) + x-tooltip="{ + content: @js($tooltip), + theme: $store.theme, + }" + @endif + {{ + $attributes + ->merge([ + 'title' => $hasTooltip ? null : $label, + ], escape: true) + ->class([$buttonClasses]) + ->style([$buttonStyles]) + }} + > + @if ($label) + + {{ $label }} + + @endif + + + + @if (filled($badge)) +
+ + {{ $badge }} + +
+ @endif +
+@endif diff --git a/resources/views/vendor/filament/components/icon.blade.php b/resources/views/vendor/filament/components/icon.blade.php new file mode 100644 index 0000000..86a4a3d --- /dev/null +++ b/resources/views/vendor/filament/components/icon.blade.php @@ -0,0 +1,29 @@ +@props([ + 'alias' => null, + 'class' => '', + 'icon' => null, +]) + +@php + $icon = ($alias ? \Filament\Support\Facades\FilamentIcon::resolve($alias) : null) ?: ($icon ?? $slot); +@endphp + +@if ($icon instanceof \Illuminate\Contracts\Support\Htmlable) + class($class) }}> + {{ $icon }} + +@elseif (str_contains($icon, '/')) + merge(['src' => $icon]) + ->class($class) + }} + /> +@else + @svg( + $icon, + $class, + array_filter($attributes->getAttributes()), + ) +@endif diff --git a/resources/views/vendor/filament/components/input/checkbox.blade.php b/resources/views/vendor/filament/components/input/checkbox.blade.php new file mode 100644 index 0000000..8f5c2c0 --- /dev/null +++ b/resources/views/vendor/filament/components/input/checkbox.blade.php @@ -0,0 +1,29 @@ +@props([ + 'alpineValid' => null, + 'valid' => true, +]) + +@php + $hasAlpineValidClasses = filled($alpineValid); + + $validInputClasses = 'text-primary-600 ring-gray-950/10 focus:ring-primary-600 checked:focus:ring-primary-500/50 dark:text-primary-500 dark:ring-white/20 dark:checked:bg-primary-500 dark:focus:ring-primary-500 dark:checked:focus:ring-primary-400/50 dark:disabled:ring-white/10'; + $invalidInputClasses = 'fi-invalid text-danger-600 ring-danger-600 focus:ring-danger-600 checked:focus:ring-danger-500/50 dark:text-danger-500 dark:ring-danger-500 dark:checked:bg-danger-500 dark:focus:ring-danger-500 dark:checked:focus:ring-danger-400/50'; +@endphp + +class([ + 'fi-checkbox-input rounded border-none bg-white shadow-sm ring-1 transition duration-75 checked:ring-0 focus:ring-2 focus:ring-offset-0 disabled:pointer-events-none disabled:bg-gray-50 disabled:text-gray-50 disabled:checked:bg-gray-400 disabled:checked:text-gray-400 dark:bg-white/5 dark:disabled:bg-transparent dark:disabled:checked:bg-gray-600', + $validInputClasses => (! $hasAlpineValidClasses) && $valid, + $invalidInputClasses => (! $hasAlpineValidClasses) && (! $valid), + ]) + }} +/> diff --git a/resources/views/vendor/filament/components/input/index.blade.php b/resources/views/vendor/filament/components/input/index.blade.php new file mode 100644 index 0000000..7b458b5 --- /dev/null +++ b/resources/views/vendor/filament/components/input/index.blade.php @@ -0,0 +1,22 @@ +@props([ + 'inlinePrefix' => false, + 'inlineSuffix' => false, +]) + +class([ + 'fi-input block w-full border-none py-1.5 text-base text-gray-950 transition duration-75 placeholder:text-gray-400 focus:ring-0 disabled:text-gray-500 disabled:[-webkit-text-fill-color:theme(colors.gray.500)] disabled:placeholder:[-webkit-text-fill-color:theme(colors.gray.400)] dark:text-white dark:placeholder:text-gray-500 dark:disabled:text-gray-400 dark:disabled:[-webkit-text-fill-color:theme(colors.gray.400)] dark:disabled:placeholder:[-webkit-text-fill-color:theme(colors.gray.500)] sm:text-sm sm:leading-6', + // A fully transparent white background color is used + // instead of transparent to fix a Safari bug + // where the date/time input "placeholder" colors too dark. + // + // https://github.com/filamentphp/filament/issues/7087 + 'bg-white/0', + 'ps-0' => $inlinePrefix, + 'ps-3' => ! $inlinePrefix, + 'pe-0' => $inlineSuffix, + 'pe-3' => ! $inlineSuffix, + ]) + }} +/> diff --git a/resources/views/vendor/filament/components/input/radio.blade.php b/resources/views/vendor/filament/components/input/radio.blade.php new file mode 100644 index 0000000..3a1e61c --- /dev/null +++ b/resources/views/vendor/filament/components/input/radio.blade.php @@ -0,0 +1,15 @@ +@props([ + 'valid' => true, +]) + +class([ + 'fi-radio-input border-none bg-white shadow-sm ring-1 transition duration-75 checked:ring-0 focus:ring-2 focus:ring-offset-0 disabled:bg-gray-50 disabled:text-gray-50 disabled:checked:bg-gray-400 disabled:checked:text-gray-400 dark:bg-white/5 dark:disabled:bg-transparent dark:disabled:checked:bg-gray-600', + 'text-primary-600 ring-gray-950/10 focus:ring-primary-600 checked:focus:ring-primary-500/50 dark:text-primary-500 dark:ring-white/20 dark:checked:bg-primary-500 dark:focus:ring-primary-500 dark:checked:focus:ring-primary-400/50 dark:disabled:ring-white/10' => $valid, + 'fi-invalid text-danger-600 ring-danger-600 focus:ring-danger-600 checked:focus:ring-danger-500/50 dark:text-danger-500 dark:ring-danger-500 dark:checked:bg-danger-500 dark:focus:ring-danger-500 dark:checked:focus:ring-danger-400/50' => ! $valid, + ]) + }} +/> diff --git a/resources/views/vendor/filament/components/input/select.blade.php b/resources/views/vendor/filament/components/input/select.blade.php new file mode 100644 index 0000000..b15bd89 --- /dev/null +++ b/resources/views/vendor/filament/components/input/select.blade.php @@ -0,0 +1,16 @@ +@props([ + 'inlinePrefix' => false, + 'inlineSuffix' => false, +]) + + diff --git a/resources/views/vendor/filament/components/input/wrapper.blade.php b/resources/views/vendor/filament/components/input/wrapper.blade.php new file mode 100644 index 0000000..c8f869f --- /dev/null +++ b/resources/views/vendor/filament/components/input/wrapper.blade.php @@ -0,0 +1,212 @@ +@props([ + 'alpineDisabled' => null, + 'alpineValid' => null, + 'disabled' => false, + 'inlinePrefix' => false, + 'inlineSuffix' => false, + 'prefix' => null, + 'prefixActions' => [], + 'prefixIcon' => null, + 'prefixIconColor' => 'gray', + 'prefixIconAlias' => null, + 'suffix' => null, + 'suffixActions' => [], + 'suffixIcon' => null, + 'suffixIconColor' => 'gray', + 'suffixIconAlias' => null, + 'valid' => true, +]) + +@php + $prefixActions = array_filter( + $prefixActions, + fn (\Filament\Forms\Components\Actions\Action $prefixAction): bool => $prefixAction->isVisible(), + ); + + $suffixActions = array_filter( + $suffixActions, + fn (\Filament\Forms\Components\Actions\Action $suffixAction): bool => $suffixAction->isVisible(), + ); + + $hasPrefix = count($prefixActions) || $prefixIcon || filled($prefix); + $hasSuffix = count($suffixActions) || $suffixIcon || filled($suffix); + + $hasAlpineDisabledClasses = filled($alpineDisabled); + $hasAlpineValidClasses = filled($alpineValid); + $hasAlpineClasses = $hasAlpineDisabledClasses || $hasAlpineValidClasses; + + $enabledWrapperClasses = 'bg-white dark:bg-white/5 [&:not(:has(.fi-ac-action:focus))]:focus-within:ring-2'; + $disabledWrapperClasses = 'fi-disabled bg-gray-50 dark:bg-transparent'; + $validWrapperClasses = 'ring-gray-950/10'; + $invalidWrapperClasses = 'fi-invalid ring-danger-600 dark:ring-danger-500'; + $enabledValidWrapperClasses = 'dark:ring-white/20 [&:not(:has(.fi-ac-action:focus))]:focus-within:ring-primary-600 dark:[&:not(:has(.fi-ac-action:focus))]:focus-within:ring-primary-500'; + $enabledInvalidWrapperClasses = '[&:not(:has(.fi-ac-action:focus))]:focus-within:ring-danger-600 dark:[&:not(:has(.fi-ac-action:focus))]:focus-within:ring-danger-500'; + $disabledValidWrapperClasses = 'dark:ring-white/10'; + + $actionsClasses = 'flex items-center gap-3'; + $labelClasses = 'fi-input-wrp-label whitespace-nowrap text-sm text-gray-500 dark:text-gray-400'; + + $getIconClasses = fn (string | array $color = 'gray'): string => \Illuminate\Support\Arr::toCssClasses([ + 'fi-input-wrp-icon h-5 w-5', + match ($color) { + 'gray' => 'text-gray-400 dark:text-gray-500', + default => 'text-custom-500', + }, + ]); + + $getIconStyles = fn (string | array $color = 'gray'): string => \Illuminate\Support\Arr::toCssStyles([ + \Filament\Support\get_color_css_variables( + $color, + shades: [500], + alias: 'input-wrapper.icon', + ) => $color !== 'gray', + ]); + + $wireTarget = $attributes->whereStartsWith(['wire:target'])->first(); + + $hasLoadingIndicator = filled($wireTarget); + + if ($hasLoadingIndicator) { + $loadingIndicatorTarget = html_entity_decode($wireTarget, ENT_QUOTES); + } +@endphp + +
except(['wire:target', 'tabindex']) + ->class([ + 'fi-input-wrp flex rounded-lg shadow-sm ring-1 transition duration-75', + $enabledWrapperClasses => (! $hasAlpineClasses) && (! $disabled), + $disabledWrapperClasses => (! $hasAlpineClasses) && $disabled, + $validWrapperClasses => (! $hasAlpineClasses) && $valid, + $invalidWrapperClasses => (! $hasAlpineClasses) && (! $valid), + $enabledValidWrapperClasses => (! $hasAlpineClasses) && (! $disabled) && $valid, + $enabledInvalidWrapperClasses => (! $hasAlpineClasses) && (! $disabled) && (! $valid), + $disabledValidWrapperClasses => (! $hasAlpineClasses) && $disabled && $valid, + ]) + }} +> + @if ($hasPrefix || $hasLoadingIndicator) + + @endif + +
$hasLoadingIndicator && (! $hasPrefix) && $inlinePrefix, + ]) + > + {{ $slot }} +
+ + @if ($hasSuffix) +
$inlineSuffix && filled($suffix), + 'ps-2' => $inlineSuffix && blank($suffix), + 'border-s border-gray-200 ps-3 dark:border-white/10' => ! $inlineSuffix, + ]) + > + @if (filled($suffix)) + + {{ $suffix }} + + @endif + + @if ($suffixIcon) + + @endif + + @if (count($suffixActions)) +
+ @foreach ($suffixActions as $suffixAction) + {{ $suffixAction }} + @endforeach +
+ @endif +
+ @endif +
diff --git a/resources/views/vendor/filament/components/link.blade.php b/resources/views/vendor/filament/components/link.blade.php new file mode 100644 index 0000000..4d7c2ea --- /dev/null +++ b/resources/views/vendor/filament/components/link.blade.php @@ -0,0 +1,304 @@ +@php + use Filament\Support\Enums\ActionSize; + use Filament\Support\Enums\FontWeight; + use Filament\Support\Enums\IconPosition; + use Filament\Support\Enums\IconSize; +@endphp + +@props([ + 'badge' => null, + 'badgeColor' => 'primary', + 'badgeSize' => 'xs', + 'color' => 'primary', + 'disabled' => false, + 'form' => null, + 'formId' => null, + 'href' => null, + 'icon' => null, + 'iconAlias' => null, + 'iconPosition' => IconPosition::Before, + 'iconSize' => null, + 'keyBindings' => null, + 'labelSrOnly' => false, + 'loadingIndicator' => true, + 'size' => ActionSize::Medium, + 'spaMode' => null, + 'tag' => 'a', + 'target' => null, + 'tooltip' => null, + 'type' => 'button', + 'weight' => FontWeight::SemiBold, +]) + +@php + if (! $iconPosition instanceof IconPosition) { + $iconPosition = filled($iconPosition) ? (IconPosition::tryFrom($iconPosition) ?? $iconPosition) : null; + } + + if (! $size instanceof ActionSize) { + $size = filled($size) ? (ActionSize::tryFrom($size) ?? $size) : null; + } + + $iconSize ??= match ($size) { + ActionSize::ExtraSmall, ActionSize::Small => IconSize::Small, + default => IconSize::Medium, + }; + + if (! $iconSize instanceof IconSize) { + $iconSize = filled($iconSize) ? (IconSize::tryFrom($iconSize) ?? $iconSize) : null; + } + + $linkClasses = \Illuminate\Support\Arr::toCssClasses([ + 'fi-link group/link relative inline-flex items-center justify-center outline-none', + 'pointer-events-none opacity-70' => $disabled, + ($size instanceof ActionSize) ? "fi-size-{$size->value}" : null, + // @deprecated `fi-link-size-*` has been replaced by `fi-size-*`. + ($size instanceof ActionSize) ? "fi-link-size-{$size->value}" : null, + match ($size) { + ActionSize::ExtraSmall => 'gap-1', + ActionSize::Small => 'gap-1', + ActionSize::Medium => 'gap-1.5', + ActionSize::Large => 'gap-1.5', + ActionSize::ExtraLarge => 'gap-1.5', + default => $size, + }, + match ($color) { + 'gray' => null, + default => 'fi-color-custom', + }, + is_string($color) ? "fi-color-{$color}" : null, + ]); + + if (! $labelSrOnly) { + $labelClasses = \Illuminate\Support\Arr::toCssClasses([ + match ($weight) { + FontWeight::Thin, 'thin' => 'font-thin', + FontWeight::ExtraLight, 'extralight' => 'font-extralight', + FontWeight::Light, 'light' => 'font-light', + FontWeight::Medium, 'medium' => 'font-medium', + FontWeight::Normal, 'normal' => 'font-normal', + FontWeight::SemiBold, 'semibold' => 'font-semibold', + FontWeight::Bold, 'bold' => 'font-bold', + FontWeight::ExtraBold, 'extrabold' => 'font-extrabold', + FontWeight::Black, 'black' => 'font-black', + default => $weight, + }, + match ($size) { + ActionSize::ExtraSmall => 'text-xs', + ActionSize::Small => 'text-sm', + ActionSize::Medium => 'text-sm', + ActionSize::Large => 'text-sm', + ActionSize::ExtraLarge => 'text-sm', + default => null, + }, + match ($color) { + 'gray' => 'text-gray-700 dark:text-gray-200', + default => 'text-custom-600 dark:text-custom-400', + }, + 'group-hover/link:underline group-focus-visible/link:underline', + ]); + } else { + $labelClasses = 'sr-only'; + } + + $labelStyles = \Illuminate\Support\Arr::toCssStyles([ + \Filament\Support\get_color_css_variables( + $color, + shades: [400, 600], + alias: 'link.label', + ) => $color !== 'gray', + ]); + + $iconClasses = \Illuminate\Support\Arr::toCssClasses([ + 'fi-link-icon', + match ($iconSize) { + IconSize::Small => 'h-4 w-4', + IconSize::Medium => 'h-5 w-5', + IconSize::Large => 'h-6 w-6', + default => $iconSize, + }, + match ($color) { + 'gray' => 'text-gray-400 dark:text-gray-500', + default => 'text-custom-600 dark:text-custom-400', + }, + ]); + + $iconStyles = \Illuminate\Support\Arr::toCssStyles([ + \Filament\Support\get_color_css_variables( + $color, + shades: [400, 600], + alias: 'link.icon', + ) => $color !== 'gray', + ]); + + $badgeContainerClasses = 'fi-link-badge-ctn absolute start-full top-0 z-[1] w-max -translate-x-1/4 -translate-y-3/4 rounded-md bg-white dark:bg-gray-900 rtl:translate-x-1/4'; + + $wireTarget = $loadingIndicator ? $attributes->whereStartsWith(['wire:target', 'wire:click'])->filter(fn ($value): bool => filled($value))->first() : null; + + $hasLoadingIndicator = filled($wireTarget) || ($type === 'submit' && filled($form)); + + if ($hasLoadingIndicator) { + $loadingIndicatorTarget = html_entity_decode($wireTarget ?: $form, ENT_QUOTES); + } + + $hasTooltip = filled($tooltip); +@endphp + +@if ($tag === 'a') + map(fn (string $keyBinding): string => str_replace('+', '-', $keyBinding))->implode('.') }}="document.getElementById($el.id).click()" + @endif + @if ($hasTooltip) + x-tooltip="{ + content: @js($tooltip), + theme: $store.theme, + }" + @endif + {{ $attributes->class([$linkClasses]) }} + > + @if ($icon && $iconPosition === IconPosition::Before) + + @endif + + + {{ $slot }} + + + @if ($icon && $iconPosition === IconPosition::After) + + @endif + + @if (filled($badge)) +
+ + {{ $badge }} + +
+ @endif +
+ @trim +@elseif ($tag === 'button') + + @trim +@endif diff --git a/resources/views/vendor/filament/components/loading-indicator.blade.php b/resources/views/vendor/filament/components/loading-indicator.blade.php new file mode 100644 index 0000000..c8929da --- /dev/null +++ b/resources/views/vendor/filament/components/loading-indicator.blade.php @@ -0,0 +1,18 @@ +class(['animate-spin']) }} +> + + + diff --git a/resources/views/vendor/filament/components/loading-section.blade.php b/resources/views/vendor/filament/components/loading-section.blade.php new file mode 100644 index 0000000..3e77c0a --- /dev/null +++ b/resources/views/vendor/filament/components/loading-section.blade.php @@ -0,0 +1,33 @@ +@php + if ((! isset($columnSpan)) || (! is_array($columnSpan))) { + $columnSpan = [ + 'default' => $columnSpan ?? null, + ]; + } + + if ((! isset($columnStart)) || (! is_array($columnStart))) { + $columnStart = [ + 'default' => $columnStart ?? null, + ]; + } + + $height ??= '8rem'; +@endphp + + + + diff --git a/resources/views/vendor/filament/components/modal/description.blade.php b/resources/views/vendor/filament/components/modal/description.blade.php new file mode 100644 index 0000000..343fdef --- /dev/null +++ b/resources/views/vendor/filament/components/modal/description.blade.php @@ -0,0 +1,5 @@ +

class(['fi-modal-description text-sm text-gray-500 dark:text-gray-400']) }} +> + {{ $slot }} +

diff --git a/resources/views/vendor/filament/components/modal/heading.blade.php b/resources/views/vendor/filament/components/modal/heading.blade.php new file mode 100644 index 0000000..be4fadc --- /dev/null +++ b/resources/views/vendor/filament/components/modal/heading.blade.php @@ -0,0 +1,5 @@ +

class(['fi-modal-heading text-base font-semibold leading-6 text-gray-950 dark:text-white']) }} +> + {{ $slot }} +

diff --git a/resources/views/vendor/filament/components/modal/index.blade.php b/resources/views/vendor/filament/components/modal/index.blade.php new file mode 100644 index 0000000..7a9fbe8 --- /dev/null +++ b/resources/views/vendor/filament/components/modal/index.blade.php @@ -0,0 +1,379 @@ +@php + use Filament\Support\Enums\Alignment; + use Filament\Support\Enums\MaxWidth; + use Filament\Support\Facades\FilamentView; +@endphp + +@props([ + 'alignment' => Alignment::Start, + 'ariaLabelledby' => null, + 'autofocus' => \Filament\Support\View\Components\Modal::$isAutofocused, + 'closeButton' => \Filament\Support\View\Components\Modal::$hasCloseButton, + 'closeByClickingAway' => \Filament\Support\View\Components\Modal::$isClosedByClickingAway, + 'closeByEscaping' => \Filament\Support\View\Components\Modal::$isClosedByEscaping, + 'closeEventName' => 'close-modal', + 'description' => null, + 'displayClasses' => 'inline-block', + 'extraModalWindowAttributeBag' => null, + 'footer' => null, + 'footerActions' => [], + 'footerActionsAlignment' => Alignment::Start, + 'header' => null, + 'heading' => null, + 'icon' => null, + 'iconAlias' => null, + 'iconColor' => 'primary', + 'id' => null, + 'openEventName' => 'open-modal', + 'slideOver' => false, + 'stickyFooter' => false, + 'stickyHeader' => false, + 'trigger' => null, + 'visible' => true, + 'width' => 'sm', +]) + +@php + $hasDescription = filled($description); + $hasFooter = (! \Filament\Support\is_slot_empty($footer)) || (is_array($footerActions) && count($footerActions)) || (! is_array($footerActions) && (! \Filament\Support\is_slot_empty($footerActions))); + $hasHeading = filled($heading); + $hasIcon = filled($icon); + $hasSlot = ! \Filament\Support\is_slot_empty($slot); + + if (! $alignment instanceof Alignment) { + $alignment = filled($alignment) ? (Alignment::tryFrom($alignment) ?? $alignment) : null; + } + + if (! $footerActionsAlignment instanceof Alignment) { + $footerActionsAlignment = filled($footerActionsAlignment) ? (Alignment::tryFrom($footerActionsAlignment) ?? $footerActionsAlignment) : null; + } + + if (! $width instanceof MaxWidth) { + $width = filled($width) ? (MaxWidth::tryFrom($width) ?? $width) : null; + } + + $closeEventHandler = filled($id) ? '$dispatch(' . \Illuminate\Support\Js::from($closeEventName) . ', { id: ' . \Illuminate\Support\Js::from($id) . ' })' : 'close()'; +@endphp + +
$width === MaxWidth::Screen, + $displayClasses, + ]) +> + @if ($trigger) +
attributes->get('disabled')) + x-on:click="open" + @endif + {{ $trigger->attributes->class(['fi-modal-trigger flex cursor-pointer']) }} + > + {{ $trigger }} +
+ @endif + +
+ + +
! ($slideOver || ($width === MaxWidth::Screen)), + 'cursor-pointer' => $closeByClickingAway, + ]) + > +
class([ + 'relative grid min-h-full grid-rows-[1fr_auto_1fr] justify-items-center sm:grid-rows-[1fr_auto_3fr]', + 'p-4' => ! ($slideOver || ($width === MaxWidth::Screen)), + ]) + }} + > +
class([ + 'fi-modal-window pointer-events-auto relative row-start-2 flex w-full cursor-default flex-col bg-white shadow-xl ring-1 ring-gray-950/5 dark:bg-gray-900 dark:ring-white/10', + 'fi-modal-slide-over-window ms-auto overflow-y-auto' => $slideOver, + // Using an arbitrary value instead of the h-dvh class that was added in Tailwind CSS v3.4.0 + // to ensure compatibility with custom themes that may use an older version of Tailwind CSS. + 'h-[100dvh]' => $slideOver || ($width === MaxWidth::Screen), + 'mx-auto rounded-xl' => ! ($slideOver || ($width === MaxWidth::Screen)), + 'hidden' => ! $visible, + match ($width) { + MaxWidth::ExtraSmall => 'max-w-xs', + MaxWidth::Small => 'max-w-sm', + MaxWidth::Medium => 'max-w-md', + MaxWidth::Large => 'max-w-lg', + MaxWidth::ExtraLarge => 'max-w-xl', + MaxWidth::TwoExtraLarge => 'max-w-2xl', + MaxWidth::ThreeExtraLarge => 'max-w-3xl', + MaxWidth::FourExtraLarge => 'max-w-4xl', + MaxWidth::FiveExtraLarge => 'max-w-5xl', + MaxWidth::SixExtraLarge => 'max-w-6xl', + MaxWidth::SevenExtraLarge => 'max-w-7xl', + MaxWidth::Full => 'max-w-full', + MaxWidth::MinContent => 'max-w-min', + MaxWidth::MaxContent => 'max-w-max', + MaxWidth::FitContent => 'max-w-fit', + MaxWidth::Prose => 'max-w-prose', + MaxWidth::ScreenSmall => 'max-w-screen-sm', + MaxWidth::ScreenMedium => 'max-w-screen-md', + MaxWidth::ScreenLarge => 'max-w-screen-lg', + MaxWidth::ScreenExtraLarge => 'max-w-screen-xl', + MaxWidth::ScreenTwoExtraLarge => 'max-w-screen-2xl', + MaxWidth::Screen => 'fixed inset-0', + default => $width, + }, + ]) + }} + > + @if ($heading || $header) +
(! $hasSlot) && (! $hasFooter), + 'fi-sticky sticky top-0 z-10 border-b border-gray-200 bg-white pb-6 dark:border-white/10 dark:bg-gray-900' => $stickyHeader, + 'rounded-t-xl' => $stickyHeader && ! ($slideOver || ($width === MaxWidth::Screen)), + match ($alignment) { + Alignment::Start, Alignment::Left => 'gap-x-5', + Alignment::Center => 'flex-col', + default => null, + }, + 'items-center' => $hasIcon && $hasHeading && (! $hasDescription) && in_array($alignment, [Alignment::Start, Alignment::Left]), + ]) + > + @if ($closeButton) +
! $slideOver, + 'end-6 top-6' => $slideOver, + ]) + > + +
+ @endif + + @if ($header) + {{ $header }} + @else + @if ($hasIcon) +
$alignment === Alignment::Center, + ]) + > +
'bg-gray-100 dark:bg-gray-500/20', + default => 'fi-color-custom bg-custom-100 dark:bg-custom-500/20', + }, + is_string($iconColor) ? "fi-color-{$iconColor}" : null, + match ($alignment) { + Alignment::Start, Alignment::Left => 'p-2', + Alignment::Center => 'p-3', + default => null, + }, + ]) + @style([ + \Filament\Support\get_color_css_variables( + $iconColor, + shades: [100, 400, 500, 600], + alias: 'modal.icon', + ) => $iconColor !== 'gray', + ]) + > + 'text-gray-500 dark:text-gray-400', + default => 'text-custom-600 dark:text-custom-400', + }, + ]) + /> +
+
+ @endif + +
$alignment === Alignment::Center, + ]) + > + $closeButton && ((! $hasIcon) || in_array($alignment, [Alignment::Start, Alignment::Left])), + 'ms-6' => $closeButton && (! $hasIcon) && ($alignment === Alignment::Center), + ]) + > + {{ $heading }} + + + @if ($hasDescription) + + {{ $description }} + + @endif +
+ @endif +
+ @endif + + @if ($hasSlot) +
($width === MaxWidth::Screen) || $slideOver, + 'pe-6 ps-[5.25rem]' => $hasIcon && ($alignment === Alignment::Start) && (! $stickyHeader), + 'px-6' => ! ($hasIcon && ($alignment === Alignment::Start) && (! $stickyHeader)), + ]) + > + {{ $slot }} +
+ @endif + + @if ($hasFooter) +
$hasIcon && ($alignment === Alignment::Start) && ($footerActionsAlignment !== Alignment::Center) && (! $stickyFooter), + 'px-6' => ! ($hasIcon && ($alignment === Alignment::Start) && ($footerActionsAlignment !== Alignment::Center) && (! $stickyFooter)), + 'fi-sticky sticky bottom-0 border-t border-gray-200 bg-white py-5 dark:border-white/10 dark:bg-gray-900' => $stickyFooter, + 'rounded-b-xl' => $stickyFooter && ! ($slideOver || ($width === MaxWidth::Screen)), + 'pb-6' => ! $stickyFooter, + 'mt-6' => (! $stickyFooter) && (! $hasSlot), + 'mt-auto' => $slideOver, + ]) + > + @if (! \Filament\Support\is_slot_empty($footer)) + {{ $footer }} + @else +
'flex flex-wrap items-center', + Alignment::Center => 'flex flex-col-reverse sm:grid sm:grid-cols-[repeat(auto-fit,minmax(0,1fr))]', + Alignment::End, Alignment::Right => 'flex flex-row-reverse flex-wrap items-center', + default => null, + }, + ]) + > + @if (is_array($footerActions)) + @foreach ($footerActions as $action) + {{ $action }} + @endforeach + @else + {{ $footerActions }} + @endif +
+ @endif +
+ @endif +
+
+
+
+
diff --git a/resources/views/vendor/filament/components/pagination/index.blade.php b/resources/views/vendor/filament/components/pagination/index.blade.php new file mode 100644 index 0000000..4768d5b --- /dev/null +++ b/resources/views/vendor/filament/components/pagination/index.blade.php @@ -0,0 +1,190 @@ +@props([ + 'currentPageOptionProperty' => 'tableRecordsPerPage', + 'extremeLinks' => false, + 'paginator', + 'pageOptions' => [], +]) + +@php + use Illuminate\Contracts\Pagination\CursorPaginator; + + $isRtl = __('filament-panels::layout.direction') === 'rtl'; + $isSimple = ! $paginator instanceof \Illuminate\Pagination\LengthAwarePaginator; +@endphp + + diff --git a/resources/views/vendor/filament/components/pagination/item.blade.php b/resources/views/vendor/filament/components/pagination/item.blade.php new file mode 100644 index 0000000..d997d70 --- /dev/null +++ b/resources/views/vendor/filament/components/pagination/item.blade.php @@ -0,0 +1,50 @@ +@props([ + 'active' => false, + 'ariaLabel' => null, + 'disabled' => false, + 'icon' => null, + 'iconAlias' => null, + 'label' => null, +]) + +
  • class([ + 'fi-pagination-item group/item border-x-[0.5px] border-gray-200 first:border-s-0 last:border-e-0 dark:border-white/10', + 'fi-disabled' => $disabled, + 'fi-active' => $active, + ]) + }} +> + +
  • diff --git a/resources/views/vendor/filament/components/section/description.blade.php b/resources/views/vendor/filament/components/section/description.blade.php new file mode 100644 index 0000000..b6b8644 --- /dev/null +++ b/resources/views/vendor/filament/components/section/description.blade.php @@ -0,0 +1,5 @@ +

    class(['fi-section-header-description overflow-hidden break-words text-sm text-gray-500 dark:text-gray-400']) }} +> + {{ $slot }} +

    diff --git a/resources/views/vendor/filament/components/section/heading.blade.php b/resources/views/vendor/filament/components/section/heading.blade.php new file mode 100644 index 0000000..d0b550c --- /dev/null +++ b/resources/views/vendor/filament/components/section/heading.blade.php @@ -0,0 +1,5 @@ +

    class(['fi-section-header-heading text-base font-semibold leading-6 text-gray-950 dark:text-white']) }} +> + {{ $slot }} +

    diff --git a/resources/views/vendor/filament/components/section/index.blade.php b/resources/views/vendor/filament/components/section/index.blade.php new file mode 100644 index 0000000..3b8a75a --- /dev/null +++ b/resources/views/vendor/filament/components/section/index.blade.php @@ -0,0 +1,213 @@ +@php + use Filament\Support\Enums\Alignment; + use Filament\Support\Enums\IconSize; +@endphp + +@props([ + 'aside' => false, + 'collapsed' => false, + 'collapsible' => false, + 'compact' => false, + 'contentBefore' => false, + 'description' => null, + 'footerActions' => [], + 'footerActionsAlignment' => Alignment::Start, + 'headerActions' => [], + 'headerEnd' => null, + 'heading' => null, + 'icon' => null, + 'iconColor' => 'gray', + 'iconSize' => IconSize::Large, + 'persistCollapsed' => false, +]) + +@php + $hasDescription = filled((string) $description); + $hasHeading = filled($heading); + $hasIcon = filled($icon); + + if (is_array($headerActions)) { + $headerActions = array_filter( + $headerActions, + fn ($headerAction): bool => $headerAction->isVisible(), + ); + } + + if (is_array($footerActions)) { + $footerActions = array_filter( + $footerActions, + fn ($footerAction): bool => $footerAction->isVisible(), + ); + } + + $hasHeaderActions = $headerActions instanceof \Illuminate\Contracts\Support\Htmlable + ? ! \Filament\Support\is_slot_empty($headerActions) + : filled($headerActions); + + $hasFooterActions = $footerActions instanceof \Illuminate\Contracts\Support\Htmlable + ? ! \Filament\Support\is_slot_empty($footerActions) + : filled($footerActions); + + $hasHeader = $hasIcon || $hasHeading || $hasDescription || $collapsible || $hasHeaderActions || filled((string) $headerEnd); +@endphp + +
    class([ + 'fi-section', + match ($aside) { + true => 'fi-aside grid grid-cols-1 items-start gap-x-6 gap-y-4 md:grid-cols-3', + false => 'rounded-xl bg-white shadow-sm ring-1 ring-gray-950/5 dark:bg-gray-900 dark:ring-white/10', + }, + ]) + }} +> + @if ($hasHeader) +
    $collapsible, + match ($compact) { + true => 'px-4 py-2.5', + false => 'px-6 py-4', + } => ! $aside, + ]) + > +
    + @if ($hasIcon) + 'text-gray-400 dark:text-gray-500', + default => 'fi-color-custom text-custom-500 dark:text-custom-400', + }, + is_string($iconColor) ? "fi-color-{$iconColor}" : null, + match ($iconSize) { + IconSize::Small, 'sm' => 'h-4 w-4 mt-1', + IconSize::Medium, 'md' => 'h-5 w-5 mt-0.5', + IconSize::Large, 'lg' => 'h-6 w-6', + default => $iconSize, + }, + ]) + @style([ + \Filament\Support\get_color_css_variables( + $iconColor, + shades: [400, 500], + alias: 'section.header.icon', + ) => $iconColor !== 'gray', + ]) + /> + @endif + + @if ($hasHeading || $hasDescription) +
    + @if ($hasHeading) + + {{ $heading }} + + @endif + + @if ($hasDescription) + + {{ $description }} + + @endif +
    + @endif + + @if ($hasHeaderActions) + + @endif + + {{ $headerEnd }} + + @if ($collapsible) + + @endif +
    + + @if ($hasHeaderActions) +
    + +
    + @endif +
    + @endif + +
    $hasHeader && (! $aside), + 'rounded-xl bg-white shadow-sm ring-1 ring-gray-950/5 dark:bg-gray-900 dark:ring-white/10 md:col-span-2' => $aside, + 'md:order-first' => $contentBefore, + ]) + > +
    'p-4', + false => 'p-6', + }, + ]) + > + {{ $slot }} +
    + + @if ($hasFooterActions) +
    ! $compact, + 'px-4 py-2.5' => $compact, + ]) + > + +
    + @endif +
    +
    diff --git a/resources/views/vendor/filament/components/tabs/index.blade.php b/resources/views/vendor/filament/components/tabs/index.blade.php new file mode 100644 index 0000000..d57c7fe --- /dev/null +++ b/resources/views/vendor/filament/components/tabs/index.blade.php @@ -0,0 +1,21 @@ +@props([ + 'contained' => false, + 'label' => null, +]) + + diff --git a/resources/views/vendor/filament/components/tabs/item.blade.php b/resources/views/vendor/filament/components/tabs/item.blade.php new file mode 100644 index 0000000..9e4ddd6 --- /dev/null +++ b/resources/views/vendor/filament/components/tabs/item.blade.php @@ -0,0 +1,123 @@ +@php + use Filament\Support\Enums\IconPosition; +@endphp + +@props([ + 'active' => false, + 'alpineActive' => null, + 'badge' => null, + 'badgeColor' => null, + 'badgeTooltip' => null, + 'badgeIcon' => null, + 'badgeIconPosition' => IconPosition::Before, + 'href' => null, + 'icon' => null, + 'iconColor' => 'gray', + 'iconPosition' => IconPosition::Before, + 'spaMode' => null, + 'tag' => 'button', + 'target' => null, + 'type' => 'button', +]) + +@php + if (! $iconPosition instanceof IconPosition) { + $iconPosition = filled($iconPosition) ? (IconPosition::tryFrom($iconPosition) ?? $iconPosition) : null; + } + + $hasAlpineActiveClasses = filled($alpineActive); + + $inactiveItemClasses = 'hover:bg-gray-50 focus-visible:bg-gray-50 dark:hover:bg-white/5 dark:focus-visible:bg-white/5'; + + // @deprecated `fi-tabs-item-active` has been replaced by `fi-active`. + $activeItemClasses = 'fi-active fi-tabs-item-active bg-gray-50 dark:bg-white/5'; + + $inactiveLabelClasses = 'text-gray-500 group-hover:text-gray-700 group-focus-visible:text-gray-700 dark:text-gray-400 dark:group-hover:text-gray-200 dark:group-focus-visible:text-gray-200'; + + $activeLabelClasses = 'text-primary-600 dark:text-primary-400'; + + $iconClasses = 'fi-tabs-item-icon h-5 w-5 shrink-0 transition duration-75'; + + $inactiveIconClasses = 'text-gray-400 dark:text-gray-500'; + + $activeIconClasses = 'text-primary-600 dark:text-primary-400'; +@endphp + +<{{ $tag }} + @if ($tag === 'button') + type="{{ $type }}" + @elseif ($tag === 'a') + {{ \Filament\Support\generate_href_html($href, $target === '_blank', $spaMode) }} + @endif + @if ($hasAlpineActiveClasses) + x-bind:class="{ + @js($inactiveItemClasses): {{-- format-ignore-start --}} ! ({{ $alpineActive }}) {{-- format-ignore-end --}}, + @js($activeItemClasses): {{ $alpineActive }}, + }" + @endif + {{ + $attributes + ->merge([ + 'aria-selected' => $active, + 'role' => 'tab', + ]) + ->class([ + 'fi-tabs-item group flex items-center justify-center gap-x-2 whitespace-nowrap rounded-lg px-3 py-2 text-sm font-medium outline-none transition duration-75', + $inactiveItemClasses => (! $hasAlpineActiveClasses) && (! $active), + $activeItemClasses => (! $hasAlpineActiveClasses) && $active, + ]) + }} +> + @if ($icon && $iconPosition === IconPosition::Before) + (! $hasAlpineActiveClasses) && (! $active), + $activeIconClasses => (! $hasAlpineActiveClasses) && $active, + ]) + /> + @endif + + (! $hasAlpineActiveClasses) && (! $active), + $activeLabelClasses => (! $hasAlpineActiveClasses) && $active, + ]) + > + {{ $slot }} + + + @if ($icon && $iconPosition === IconPosition::After) + (! $hasAlpineActiveClasses) && (! $active), + $activeIconClasses => (! $hasAlpineActiveClasses) && $active, + ]) + /> + @endif + + @if (filled($badge)) + + {{ $badge }} + + @endif + diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php index b9d609c..87feff9 100644 --- a/resources/views/welcome.blade.php +++ b/resources/views/welcome.blade.php @@ -4,13 +4,15 @@ - Laravel + CRI DIGITAL MAUFACTURING + CRI DIGITAL MANUFACTURING + @if (file_exists(public_path('build/manifest.json')) || file_exists(public_path('hot'))) @vite(['resources/css/app.css', 'resources/js/app.js']) @else diff --git a/storage/app/public/.gitignore b/storage/app/public/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/storage/app/public/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore