From 1c3dfef558ff67e46df45cbb952923636ddaf88c Mon Sep 17 00:00:00 2001 From: dhanabalan Date: Wed, 27 May 2026 11:27:55 +0530 Subject: [PATCH 1/5] Added employee master migration file --- ...2_115608_create_employee_masters_table.php | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 database/migrations/2026_05_22_115608_create_employee_masters_table.php diff --git a/database/migrations/2026_05_22_115608_create_employee_masters_table.php b/database/migrations/2026_05_22_115608_create_employee_masters_table.php new file mode 100644 index 0000000..df4e2c6 --- /dev/null +++ b/database/migrations/2026_05_22_115608_create_employee_masters_table.php @@ -0,0 +1,45 @@ + Date: Wed, 27 May 2026 11:28:47 +0530 Subject: [PATCH 2/5] Added employee master model file --- app/Models/EmployeeMaster.php | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 app/Models/EmployeeMaster.php diff --git a/app/Models/EmployeeMaster.php b/app/Models/EmployeeMaster.php new file mode 100644 index 0000000..7c8cf8b --- /dev/null +++ b/app/Models/EmployeeMaster.php @@ -0,0 +1,31 @@ +belongsTo(Plant::class); + } +} From 9b15dd6c1197a1e77d7a7de0ec1bf97320e22e72 Mon Sep 17 00:00:00 2001 From: dhanabalan Date: Wed, 27 May 2026 11:30:01 +0530 Subject: [PATCH 3/5] Added employee master resource pages --- .../Resources/EmployeeMasterResource.php | 217 ++++++++++++++++++ .../Pages/CreateEmployeeMaster.php | 12 + .../Pages/EditEmployeeMaster.php | 22 ++ .../Pages/ListEmployeeMasters.php | 19 ++ .../Pages/ViewEmployeeMaster.php | 19 ++ 5 files changed, 289 insertions(+) create mode 100644 app/Filament/Resources/EmployeeMasterResource.php create mode 100644 app/Filament/Resources/EmployeeMasterResource/Pages/CreateEmployeeMaster.php create mode 100644 app/Filament/Resources/EmployeeMasterResource/Pages/EditEmployeeMaster.php create mode 100644 app/Filament/Resources/EmployeeMasterResource/Pages/ListEmployeeMasters.php create mode 100644 app/Filament/Resources/EmployeeMasterResource/Pages/ViewEmployeeMaster.php diff --git a/app/Filament/Resources/EmployeeMasterResource.php b/app/Filament/Resources/EmployeeMasterResource.php new file mode 100644 index 0000000..80090bf --- /dev/null +++ b/app/Filament/Resources/EmployeeMasterResource.php @@ -0,0 +1,217 @@ +schema([ + Forms\Components\Select::make('plant_id') + ->label('Plant') + ->relationship('plant', 'name') + ->required(), + Forms\Components\TextInput::make('name') + ->label('Name') + ->required() + ->reactive() + ->extraInputAttributes([ + 'oninput' => 'this.value = this.value.replace(/[^a-zA-Z\s]/g, "")', + ]), + Forms\Components\TextInput::make('code') + ->label('ID') + ->extraInputAttributes([ + 'oninput' => 'this.value = this.value.replace(/[^a-zA-Z0-9]/g, "")',]) + ->required() + ->unique( + table: 'employee_masters', + column: 'code', + ignoreRecord: true + ) + ->validationMessages([ + 'unique' => 'Duplicate employee code already exists.', + ]), + Forms\Components\TextInput::make('department') + ->label('Department') + ->required(), + Forms\Components\TextInput::make('designation') + ->label('Designation') + ->extraInputAttributes([ + 'oninput' => 'this.value = this.value.replace(/[^a-zA-Z\s]/g, "")',]) + ->required(), + Forms\Components\TextInput::make('email') + ->label('Email') + ->email() + ->required(), + Forms\Components\TextInput::make('mobile_number') + ->label('Mobile Number') + ->length(10) + ->reactive() + ->extraInputAttributes([ + 'oninput' => 'this.value = this.value.replace(/[^0-9]/g, "").slice(0, 10)', // blocks non-numbers + limits to 10 chars + 'maxlength' => 10, + ]) + ->required() + ->unique( + table: 'employee_masters', + column: 'mobile_number', + ignoreRecord: true + ) + ->validationMessages([ + 'unique' => 'Duplicate mobile number already exists.', + ]), + Forms\Components\Hidden::make('created_by') + ->label('Created by') + ->default(Filament::auth()->user()?->name ?? ''), + Forms\Components\Hidden::make('updated_by') + ->label('Updated by') + ->default(Filament::auth()->user()?->name ?? ''), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('No.') + ->label('No.') + ->getStateUsing(function ($record, $livewire, $column, $rowLoop) { + $paginator = $livewire->getTableRecords(); + $perPage = method_exists($paginator, 'perPage') ? $paginator->perPage() : 10; + $currentPage = method_exists($paginator, 'currentPage') ? $paginator->currentPage() : 1; + return ($currentPage - 1) * $perPage + $rowLoop->iteration; + }) + ->alignCenter(), + Tables\Columns\TextColumn::make('plant.name') + ->numeric() + ->sortable() + ->alignCenter(), + Tables\Columns\TextColumn::make('name') + ->label('Name') + ->sortable() + ->alignCenter() + ->searchable(), + Tables\Columns\TextColumn::make('code') + ->label('Employee ID') + ->sortable() + ->alignCenter() + ->searchable(), + Tables\Columns\TextColumn::make('department') + ->label('Department') + ->sortable() + ->alignCenter() + ->searchable(), + Tables\Columns\TextColumn::make('designation') + ->label('Designation') + ->sortable() + ->alignCenter() + ->searchable(), + Tables\Columns\TextColumn::make('email') + ->label('Email') + ->sortable() + ->alignCenter() + ->searchable(), + Tables\Columns\TextColumn::make('mobile_number') + ->label('Mobile Number') + ->sortable() + ->alignCenter() + ->searchable(), + Tables\Columns\TextColumn::make('created_at') + ->dateTime() + ->sortable() + ->alignCenter(), + Tables\Columns\TextColumn::make('updated_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true) + ->alignCenter(), + Tables\Columns\TextColumn::make('created_by') + ->label('Created by') + ->sortable() + ->alignCenter(), + Tables\Columns\TextColumn::make('updated_by') + ->label('Updated by') + ->sortable() + ->toggleable(isToggledHiddenByDefault: true) + ->alignCenter(), + Tables\Columns\TextColumn::make('deleted_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true) + ->alignCenter(), + ]) + ->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(EmployeeMasterImporter::class) + ->visible(function() { + return Filament::auth()->user()->can('view import employee master'); + }), + ExportAction::make() + ->exporter(EmployeeMasterExporter::class) + ->visible(function() { + return Filament::auth()->user()->can('view export employee master'); + }), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListEmployeeMasters::route('/'), + 'create' => Pages\CreateEmployeeMaster::route('/create'), + 'view' => Pages\ViewEmployeeMaster::route('/{record}'), + 'edit' => Pages\EditEmployeeMaster::route('/{record}/edit'), + ]; + } + + public static function getEloquentQuery(): Builder + { + return parent::getEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } +} diff --git a/app/Filament/Resources/EmployeeMasterResource/Pages/CreateEmployeeMaster.php b/app/Filament/Resources/EmployeeMasterResource/Pages/CreateEmployeeMaster.php new file mode 100644 index 0000000..d1f36eb --- /dev/null +++ b/app/Filament/Resources/EmployeeMasterResource/Pages/CreateEmployeeMaster.php @@ -0,0 +1,12 @@ + Date: Wed, 27 May 2026 11:30:56 +0530 Subject: [PATCH 4/5] Added employee master importer and exporter pages --- .../Exports/EmployeeMasterExporter.php | 63 ++++++++ .../Imports/EmployeeMasterImporter.php | 147 ++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 app/Filament/Exports/EmployeeMasterExporter.php create mode 100644 app/Filament/Imports/EmployeeMasterImporter.php diff --git a/app/Filament/Exports/EmployeeMasterExporter.php b/app/Filament/Exports/EmployeeMasterExporter.php new file mode 100644 index 0000000..3122260 --- /dev/null +++ b/app/Filament/Exports/EmployeeMasterExporter.php @@ -0,0 +1,63 @@ +label('NO') + ->state(function ($record) use (&$rowNumber) { + // Increment and return the row number + return ++$rowNumber; + }), + ExportColumn::make('plant.name') + ->label('PLANT'), + ExportColumn::make('name') + ->label('NAME'), + ExportColumn::make('code') + ->label('CODE'), + ExportColumn::make('department') + ->label('DEPARTMENT'), + ExportColumn::make('designation') + ->label('DESIGNATION'), + ExportColumn::make('email') + ->label('EMAIL'), + ExportColumn::make('mobile_number') + ->label('MOBILE NUMBER'), + ExportColumn::make('created_at') + ->label('CREATED AT'), + ExportColumn::make('updated_at') + ->label('UPDATED AT'), + ExportColumn::make('created_by') + ->label('CREATED BY'), + ExportColumn::make('updated_by') + ->label('UPDATED BY'), + ExportColumn::make('deleted_at') + ->label('DELETED AT') + ->enabledByDefault(false), + + ]; + } + + public static function getCompletedNotificationBody(Export $export): string + { + $body = 'Your employee master 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/EmployeeMasterImporter.php b/app/Filament/Imports/EmployeeMasterImporter.php new file mode 100644 index 0000000..4c103df --- /dev/null +++ b/app/Filament/Imports/EmployeeMasterImporter.php @@ -0,0 +1,147 @@ +requiredMapping() + ->exampleHeader('PLANT CODE') + ->example('1000') + ->label('PLANT CODE') + ->relationship(resolveUsing: 'code') + ->rules(['required']), + ImportColumn::make('name') + ->exampleHeader('EMPLOYEE NAME') + ->example('John Doe') + ->label('EMPLOYEE NAME'), + ImportColumn::make('code') + ->exampleHeader('EMPLOYEE ID') + ->example('EMP001') + ->label('EMPLOYEE ID'), + ImportColumn::make('department') + ->exampleHeader('DEPARTMENT') + ->example('HR') + ->label('DEPARTMENT'), + ImportColumn::make('designation') + ->exampleHeader('DESIGNATION') + ->example('Manager') + ->label('DESIGNATION'), + ImportColumn::make('email') + ->exampleHeader('EMAIL') + ->example('john.doe@example.com') + ->label('EMAIL'), + ImportColumn::make('mobile_number') + ->exampleHeader('MOBILE NUMBER') + ->example('1234567890') + ->label('MOBILE NUMBER'), + ]; + } + + public function resolveRecord(): ?EmployeeMaster + { + $warnMsg = []; + $plantCod = trim($this->data['plant']) ?? ''; + $employeeName = trim($this->data['name']) ?? ''; + $employeeCode = trim($this->data['code']) ?? ''; + $employeeDepartment = trim($this->data['department']) ?? ''; + $employeeDesignation = trim($this->data['designation']) ?? ''; + $employeeEmail = trim($this->data['email']) ?? ''; + $employeeMobile = trim($this->data['mobile_number']) ?? ''; + $createdBy = Filament::auth()->user()?->name ?? ''; + $updatedBy = $createdBy; + + $plantId = null; + + if ($plantCod == null || $plantCod == '' || ! $plantCod) { + $warnMsg[] = "Plant code can't be empty!"; + } elseif (! is_numeric($plantCod)) { + $warnMsg[] = "Plant code '{$plantCod}' should contain only numeric values!"; + } elseif (Str::length($plantCod) < 4 || Str::length($plantCod) > 7) { + $warnMsg[] = "Plant code '{$plantCod}' must be between 4 and 7 digits only!"; + } elseif (! preg_match('/^[1-9]\d{3,6}$/', $plantCod)) { + $warnMsg[] = "Invalid plant code '{$plantCod}' found!"; + } else { + $plant = Plant::where('code', $plantCod)->first(); + if (! $plant) { + $warnMsg[] = 'Plant not found!'; + } else { + $plantId = $plant->id; + } + } + + if ($employeeCode == null || $employeeCode == '' || ! $employeeCode) { + $warnMsg[] = "Employee ID can't be empty!"; + } elseif (Str::length($employeeCode) < 3 || Str::length($employeeCode) > 20) { + $warnMsg[] = "Employee ID '{$employeeCode}' must be between 3 and 20 characters only!"; + } + if ($employeeEmail != null && $employeeEmail != '' && ! filter_var($employeeEmail, FILTER_VALIDATE_EMAIL)) { + $warnMsg[] = "Invalid email address '{$employeeEmail}' found!"; + } + if ($employeeMobile != null && $employeeMobile != '' && ! is_numeric($employeeMobile)) { + $warnMsg[] = "Mobile number '{$employeeMobile}' should contain only numeric values!"; + } elseif (Str::length($employeeMobile) < 10 || Str::length($employeeMobile) > 10) { + $warnMsg[] = "Mobile number '{$employeeMobile}' must be exactly 10 digits!"; + } + else + { + $existingMobile = EmployeeMaster::where('mobile_number', $employeeMobile) + ->where('code', '!=', $employeeCode) + ->first(); + + if ($existingMobile) { + $warnMsg[] = "Mobile number '{$employeeMobile}' is already assigned to another employee!"; + } + } + + if (! empty($warnMsg)) { + throw new RowImportFailedException(implode(', ', $warnMsg)); + } + else + { + EmployeeMaster::updateOrCreate( + [ + 'plant_id' => $plantId ?? null, + 'code' => $employeeCode ?? null, + ], + [ + 'name' => $employeeName ?? null, + 'email' => $employeeEmail ?? null, + 'mobile_number' => $employeeMobile ?? null, + 'department' => $employeeDepartment ?? null, + 'designation' => $employeeDesignation ?? null, + 'created_by' => $createdBy ?? null, + 'updated_by' => $updatedBy ?? null, + ] + ); + } + + return null; + } + + public static function getCompletedNotificationBody(Import $import): string + { + $body = 'Your employee master 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; + } +} From 938514fe0db35ba41bd169d96470eb46df6cac23 Mon Sep 17 00:00:00 2001 From: dhanabalan Date: Wed, 27 May 2026 11:32:16 +0530 Subject: [PATCH 5/5] Added employee master policy file --- app/Policies/EmployeeMasterPolicy.php | 106 ++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 app/Policies/EmployeeMasterPolicy.php diff --git a/app/Policies/EmployeeMasterPolicy.php b/app/Policies/EmployeeMasterPolicy.php new file mode 100644 index 0000000..15bd601 --- /dev/null +++ b/app/Policies/EmployeeMasterPolicy.php @@ -0,0 +1,106 @@ +checkPermissionTo('view-any EmployeeMaster'); + } + + /** + * Determine whether the user can view the model. + */ + public function view(User $user, EmployeeMaster $employeemaster): bool + { + return $user->checkPermissionTo('view EmployeeMaster'); + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + return $user->checkPermissionTo('create EmployeeMaster'); + } + + /** + * Determine whether the user can update the model. + */ + public function update(User $user, EmployeeMaster $employeemaster): bool + { + return $user->checkPermissionTo('update EmployeeMaster'); + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(User $user, EmployeeMaster $employeemaster): bool + { + return $user->checkPermissionTo('delete EmployeeMaster'); + } + + /** + * Determine whether the user can delete any models. + */ + public function deleteAny(User $user): bool + { + return $user->checkPermissionTo('delete-any EmployeeMaster'); + } + + /** + * Determine whether the user can restore the model. + */ + public function restore(User $user, EmployeeMaster $employeemaster): bool + { + return $user->checkPermissionTo('restore EmployeeMaster'); + } + + /** + * Determine whether the user can restore any models. + */ + public function restoreAny(User $user): bool + { + return $user->checkPermissionTo('restore-any EmployeeMaster'); + } + + /** + * Determine whether the user can replicate the model. + */ + public function replicate(User $user, EmployeeMaster $employeemaster): bool + { + return $user->checkPermissionTo('replicate EmployeeMaster'); + } + + /** + * Determine whether the user can reorder the models. + */ + public function reorder(User $user): bool + { + return $user->checkPermissionTo('reorder EmployeeMaster'); + } + + /** + * Determine whether the user can permanently delete the model. + */ + public function forceDelete(User $user, EmployeeMaster $employeemaster): bool + { + return $user->checkPermissionTo('force-delete EmployeeMaster'); + } + + /** + * Determine whether the user can permanently delete any models. + */ + public function forceDeleteAny(User $user): bool + { + return $user->checkPermissionTo('force-delete-any EmployeeMaster'); + } +}