schema([ Forms\Components\TextInput::make('register_id') ->label('Register ID') ->default(function () { $datePart = Carbon::now()->format('ymd'); // 260527 $prefix = 'CRI' . $datePart . 'V'; // Get latest record for today $lastRecord = VisitorEntry::where('register_id', 'like', $prefix . '%') ->latest('id') ->first(); if ($lastRecord) { $lastNumber = (int) substr($lastRecord->register_id, -3); $nextNumber = str_pad($lastNumber + 1, 3, '0', STR_PAD_LEFT); } else { $nextNumber = '001'; } return $prefix . $nextNumber; }) ->readOnly(), 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() ->extraAttributes([ 'id' => 'mobile_number_input', 'x-data' => '{ value: "" }', 'x-model' => 'value', 'wire:keydown.enter.prevent' => 'processMobile(value)', ]), Forms\Components\TextInput::make('name') ->label('Name') ->required() ->reactive() ->extraInputAttributes([ 'oninput' => 'this.value = this.value.replace(/[^a-zA-Z\s]/g, "")', ]), Forms\Components\Select::make('type') ->label('Type') ->reactive() ->options([ 'Student' => 'Student', 'Consultant' => 'Consultant', 'Vendor' => 'Vendor', 'Other' => 'Other', ]) ->required() ->dehydrateStateUsing(function ($state, callable $get) { return $state == 'Other' ? $get('other_type') : $state; }), Forms\Components\TextInput::make('other_type') ->label('Specify Type') ->reactive() ->visible(fn (callable $get) => $get('type') == 'Other') ->required(fn (callable $get) => $get('type') == 'Other') ->dehydrated(false), Forms\Components\TextInput::make('company') ->label('Company') ->required(), Forms\Components\Select::make('department') ->label('Employee Department') ->options( \App\Models\EmployeeMaster::distinct() ->pluck('department', 'department') ) ->required() ->reactive() ->afterStateUpdated(function (callable $set) { $set('employee_master_id', null); $set('code', null); }), // Forms\Components\Select::make('employee_master_id') // ->label('Recipient Employee') // ->required() // ->options(function (callable $get) { // $department = $get('department'); // if (!$department) { // return []; // } // return \App\Models\EmployeeMaster::where('department', $department) // ->pluck('name', 'id'); // }) // ->reactive() // ->afterStateUpdated(function (callable $set, callable $get, ?string $state) { // $department = $get('department'); // $employee = \App\Models\EmployeeMaster::where('id', $state) // ->where('department', $department) // ->first(); // $set('code', $employee ? $employee->code : ''); // }), Forms\Components\Select::make('employee_master_id') ->label('Recipient Employee') ->required() ->searchable() ->options(function (callable $get) { $department = $get('department'); // Always load ALL employees, filter by department if set if ($department) { return \App\Models\EmployeeMaster::where('department', $department) ->pluck('name', 'id'); } // Fallback: load all so fill() can always match the ID return \App\Models\EmployeeMaster::pluck('name', 'id'); }) ->reactive() ->afterStateUpdated(function (callable $set, ?string $state) { $employee = \App\Models\EmployeeMaster::find($state); $set('code', $employee?->code ?? ''); }), Forms\Components\TextInput::make('code') ->label('Employee Code') ->readOnly(), Forms\Components\Textarea::make('purpose_of_visit') ->label('Purpose of Visit') ->required(), Forms\Components\TextInput::make('number_of_person') ->numeric() ->default(1) ->required(), Forms\Components\DateTimePicker::make('in_time') ->label('In Time') ->required() ->default(now()), Forms\Components\DateTimePicker::make('out_time') ->label('Out Time'), Forms\Components\DateTimePicker::make('valid_upto') ->label('Valid Upto') ->after('in_time') ->validationMessages([ 'after' => 'Valid Upto must be after the In Time.', ]), Forms\Components\View::make('components.webcam-field') ->columnSpanFull(), Forms\Components\Hidden::make('photo'), 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') ->alignCenter() ->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; }), Tables\Columns\ImageColumn::make('photo') ->label('Photo') ->disk('public') ->height(50) ->width(50) // ->defaultImageUrl('https://ui-avatars.com/api/?name=Visitor&background=555&color=fff') ->defaultImageUrl(asset('images/profile.png')) ->alignCenter() ->extraImgAttributes(['style' => 'border-radius: 6px; object-fit: cover;']), Tables\Columns\TextColumn::make('register_id') ->label('Register ID') ->alignCenter() ->sortable(), Tables\Columns\TextColumn::make('type') ->label('Visitor Type') ->alignCenter() ->sortable(), Tables\Columns\TextColumn::make('name') ->label('Visitor Name') ->sortable() ->alignCenter() ->searchable(), Tables\Columns\TextColumn::make('mobile_number') ->label('Visitor Mobile Number') ->alignCenter() ->sortable(), Tables\Columns\TextColumn::make('employeeMaster.name') ->label('Recipient Name') ->alignCenter() ->sortable(), Tables\Columns\TextColumn::make('employeeMaster.code') ->label('Receipient ID') ->alignCenter() ->sortable(), Tables\Columns\TextColumn::make('employeeMaster.department') ->label('Receipient Department') ->alignCenter() ->sortable(), Tables\Columns\TextColumn::make('number_of_person') ->label('Number of Person') ->numeric() ->alignCenter() ->sortable(), Tables\Columns\TextColumn::make('in_time') ->label('In Time') ->dateTime() ->sortable() ->alignCenter(), Tables\Columns\TextColumn::make('out_time') ->label('Out Time') ->dateTime() ->sortable() ->alignCenter(), Tables\Columns\TextColumn::make('valid_upto') ->label('Valid Upto') ->dateTime() ->sortable() ->alignCenter() ->color(fn ($record) => $record->valid_upto && \Carbon\Carbon::parse($record->valid_upto)->isPast() ? 'danger' : 'success' ), Tables\Columns\TextColumn::make('created_at') ->label('Created At') ->dateTime() ->sortable() ->toggleable(isToggledHiddenByDefault: true) ->alignCenter(), Tables\Columns\TextColumn::make('updated_at') ->label('Updated At') ->dateTime() ->sortable() ->toggleable(isToggledHiddenByDefault: true) ->alignCenter(), Tables\Columns\TextColumn::make('deleted_at') ->label('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(), ]), ]); } public static function infolist(Infolist $infolist): Infolist { return $infolist ->schema([ // ── Visitor Photo (full width at the top) ── Section::make('Visitor Photo') ->schema([ ImageEntry::make('photo') ->label('') ->disk('public') ->height(220) ->defaultImageUrl(asset('images/profile.png')) ->extraImgAttributes([ 'style' => 'border-radius: 10px; object-fit: cover;' ]), ]) ->columnSpanFull(), // ── Visitor Details ── Section::make('Visitor Details') ->schema([ TextEntry::make('name') ->label('Visitor Name'), TextEntry::make('mobile_number') ->label('Mobile Number'), TextEntry::make('type') ->label('Visitor Type') ->badge() ->color(fn (string $state): string => match ($state) { 'internal' => 'success', 'external' => 'warning', default => 'gray', }), TextEntry::make('company') ->label('Company'), TextEntry::make('purpose_of_visit') ->label('Purpose of Visit') ->columnSpanFull(), ]) ->columns(2), // ── Employee Details ── Section::make('Recipient Details') ->schema([ TextEntry::make('employeeMaster.name') ->label('Recipient Employee'), TextEntry::make('code') ->label('Employee Code'), TextEntry::make('employeeMaster.department') ->label('Department'), TextEntry::make('number_of_person') ->label('Number of Persons'), ]) ->columns(2), // ── Visit Timing ── Section::make('Visit Timing') ->schema([ TextEntry::make('in_time') ->label('In Time') ->dateTime(), TextEntry::make('out_time') ->label('Out Time') ->dateTime(), TextEntry::make('valid_upto') ->label('Valid Upto') ->dateTime() ->badge() ->color(fn ($record) => $record->valid_upto && \Carbon\Carbon::parse($record->valid_upto)->isPast() ? 'danger' : 'success' ), ]) ->columns(2), ]); } public static function getRelations(): array { return [ // ]; } public static function getPages(): array { return [ 'index' => Pages\ListVisitorEntries::route('/'), 'create' => Pages\CreateVisitorEntry::route('/create'), 'view' => Pages\ViewVisitorEntry::route('/{record}'), 'edit' => Pages\EditVisitorEntry::route('/{record}/edit'), ]; } public static function getEloquentQuery(): Builder { return parent::getEloquentQuery() ->withoutGlobalScopes([ SoftDeletingScope::class, ]); } }