diff --git a/app/Filament/Resources/StickerValidationResource.php b/app/Filament/Resources/StickerValidationResource.php
new file mode 100644
index 0000000..273e6a2
--- /dev/null
+++ b/app/Filament/Resources/StickerValidationResource.php
@@ -0,0 +1,165 @@
+schema([
+ Section::make('')
+ ->schema([
+ Forms\Components\Select::make('plant_id')
+ ->label('Plant')
+ ->reactive()
+ ->relationship('plant', 'name')
+ ->required(),
+ Forms\Components\Select::make('machine_id')
+ ->label('Work Center')
+ ->required()
+ ->reactive()
+ ->options(function (callable $get) {
+ $plantId = $get('plant_id');
+ if (empty($plantId)) {
+ return [];
+ }
+ return Machine::where('plant_id', $plantId)->pluck('work_center', 'id');
+ })
+ ->searchable(),
+ Forms\Components\TextInput::make('production_order')
+ ->label('Production Order')
+ ->reactive()
+ ->extraAttributes([
+ 'id' => 'production_order_input',
+ 'x-data' => '{ value: "" }',
+ 'x-model' => 'value',
+ 'wire:keydown.enter.prevent' => 'processProOrder(value)',
+ ]),
+ Forms\Components\TextInput::make('serial_number')
+ ->label('Serial Number')
+ ->reactive()
+ ->extraAttributes([
+ 'id' => 'serial_number_input',
+ 'x-data' => '{ value: "" }',
+ 'x-model' => 'value',
+ 'wire:keydown.enter.prevent' => 'processSno(value)',
+ ]),
+ 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),
+ ])
+ ->columns(4),
+ ]);
+ }
+
+ 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;
+ }),
+ Tables\Columns\TextColumn::make('plant.code')
+ ->label('Plant')
+ ->alignCenter()
+ ->sortable(),
+ Tables\Columns\TextColumn::make('machine.work_center')
+ ->label('Work Center')
+ ->alignCenter()
+ ->sortable(),
+ Tables\Columns\TextColumn::make('production_order')
+ ->label('Production Order')
+ ->alignCenter()
+ ->sortable(),
+ Tables\Columns\TextColumn::make('serial_number')
+ ->label('Serial Number')
+ ->alignCenter()
+ ->sortable(),
+ Tables\Columns\TextColumn::make('status')
+ ->label('Status')
+ ->alignCenter()
+ ->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\ListStickerValidations::route('/'),
+ 'create' => Pages\CreateStickerValidation::route('/create'),
+ 'view' => Pages\ViewStickerValidation::route('/{record}'),
+ 'edit' => Pages\EditStickerValidation::route('/{record}/edit'),
+ ];
+ }
+
+ public static function getEloquentQuery(): Builder
+ {
+ return parent::getEloquentQuery()
+ ->withoutGlobalScopes([
+ SoftDeletingScope::class,
+ ]);
+ }
+}
diff --git a/app/Filament/Resources/StickerValidationResource/Pages/CreateStickerValidation.php b/app/Filament/Resources/StickerValidationResource/Pages/CreateStickerValidation.php
new file mode 100644
index 0000000..37b33f8
--- /dev/null
+++ b/app/Filament/Resources/StickerValidationResource/Pages/CreateStickerValidation.php
@@ -0,0 +1,298 @@
+getCancelFormAction(),
+ ];
+ }
+
+ protected function getRedirectUrl(): string
+ {
+ return $this->getResource()::getUrl('create');
+ }
+
+
+ public function processProOrder($value)
+ {
+ $plantId = $this->form->getState()['plant_id'];
+
+ $this->plantId = $plantId;
+
+ $this->ref_number = $value;
+
+ $this->dispatch('refreshEmptySticker', $plantId, $value);
+
+ $this->dispatch('focus-serial-number');
+ }
+
+ public function processSno($serNo)
+ {
+ $plantId = $this->form->getState()['plant_id'];
+
+ $this->plantId = $plantId;
+
+ $workCenter = $this->form->getState()['machine_id'];
+
+ $this->workCenter = $workCenter;
+
+ $this->ref_number = $this->form->getState()['production_order'];
+
+ $this->serNo = $serNo;
+
+ $user = Filament::auth()->user();
+
+ $operatorName = $user->name;
+
+ if (! preg_match('/^([a-zA-Z0-9]{6,})\|([1-9][a-zA-Z0-9]{8,})\|?$/', $this->serNo, $matches)) {
+ Notification::make()
+ ->danger()
+ ->title('Invalid Serial QR Format')
+ ->body('Scan valid Serial QR code proceed!
Sample formats are:
123456|1234567890123| OR 123456|1234567890123')
+ ->seconds(3)
+ ->send();
+
+ $this->dispatch('playWarnSound');
+
+ $this->form->fill([
+ 'plant_id' => $this->plantId,
+ 'machine_id' => $this->workCenter,
+ 'production_order' => $this->ref_number,
+ 'serial_number' => null,
+ ]);
+
+ $this->dispatch('focus-serial-number');
+
+ return;
+ }
+
+ if (preg_match('/^([a-zA-Z0-9]{6,})\|([1-9][a-zA-Z0-9]{8,})\|?$/', $this->serNo, $matches)) {
+ $itemCode = $matches[1];
+ $serialNumber = $matches[2];
+
+ $recFound = ProductionQuantity::where('plant_id', $this->plantId)
+ ->where('production_order', $this->ref_number)
+ ->where('serial_number', $serialNumber)
+ ->first();
+
+ if(!$recFound){
+ Notification::make()
+ ->danger()
+ ->title('Unknown Serial Number')
+ ->body("Scanned serial number '$serialNumber' not found for the given plant and production order")
+ ->seconds(3)
+ ->send();
+ $this->form->fill([
+ 'plant_id' => $this->plantId,
+ 'machine_id' => $this->workCenter,
+ 'production_order' => $this->ref_number,
+ 'serial_number' => null,
+ ]);
+ return;
+ }
+
+ $duplicate = StickerValidation::where('plant_id', $this->plantId)
+ ->where('production_order', $this->ref_number)
+ ->where('serial_number', $serialNumber)
+ ->first();
+
+ if ($duplicate) {
+ Notification::make()
+ ->danger()
+ ->title('Duplicate Serial Number')
+ ->body("Serial number $serialNumber already exists for this plant and production order!")
+ ->seconds(3)
+ ->send();
+
+ $this->form->fill([
+ 'plant_id' => $this->plantId,
+ 'machine_id' => $this->workCenter,
+ 'production_order' => $this->ref_number,
+ 'serial_number' => null,
+ ]);
+ return;
+ }
+
+ $itemC = Item::where('code', $itemCode)
+ ->where('plant_id',$this->plantId)
+ ->first();
+
+ $itemId = $itemC->id;
+
+ $item = ItemCharacteristic::where('item_id', $itemId)
+ ->where('plant_id',$this->plantId)
+ ->first();
+
+ $itemI = $item->id;
+
+ $mapping = StickerMappingMaster::where('plant_id', $this->plantId)
+ ->where('item_characteristic_id', $itemI)
+ ->first();
+
+ if (!$mapping) {
+ Notification::make()
+ ->danger()
+ ->title('Sticker Mapping Not Found')
+ ->body("No sticker mapping found for this item and plant.")
+ ->send();
+ return;
+ }
+
+ $stickers = [];
+
+ for ($i = 1; $i <= 8; $i++) {
+ $machineColumn = "sticker{$i}_machine_id";
+ $ipColumn = "sticker{$i}_print_ip";
+ $stickerColumn = "sticker_structure{$i}_id";
+ $itemColumn = "item_characteristic_id";
+
+ if (
+ !empty($mapping->$machineColumn) &&
+ !empty($mapping->$stickerColumn)
+ ) {
+ $stickers[] = [
+ 'machine_id' => $mapping->$machineColumn,
+ 'sticker_id' => $mapping->$stickerColumn,
+ 'item_characteristic' => $mapping->$itemColumn,
+ 'print_ip' => $mapping->$ipColumn,
+ ];
+ }
+ }
+
+ if (empty($stickers)) {
+ Notification::make()
+ ->danger()
+ ->title('No Sticker Configuration Found')
+ ->body('No sticker and machine mappings configured for this item and plant.')
+ ->send();
+ return;
+ }
+
+ $urls = [];
+
+ foreach ($stickers as $sticker) {
+ $urls[] = route('stickers1.pdf', [
+ 'stickerId' => $sticker['sticker_id'],
+ 'plant_id' => $this->plantId,
+ 'item_characteristic_id' => $sticker['item_characteristic'],
+ 'serial_number' => $serialNumber,
+ ]);
+ }
+
+ $this->dispatch('open-stickers-sequence', urls: $urls);
+
+ // $pdfPath = storage_path('app/private/uploads/StickerTemplateOcr/multi.pdf');
+
+ // if (! file_exists($pdfPath)) {
+ // Notification::make()
+ // ->danger()
+ // ->title('Pdf Not Found')
+ // ->body("pdf file not exist.")
+ // ->send();
+ // return;
+ // }
+
+ // if (! $printerName) {
+ // Notification::make()
+ // ->danger()
+ // ->title('Printer Not Found')
+ // ->body("No CUPS printer configured for IP: $iotsPrintIp")
+ // ->send();
+ // return;
+ // }
+
+ // putenv('CUPS_SERVER=printer.iotsignin.com');
+
+ // $cmd = "lp -d " . escapeshellarg($printerName)
+ // . " -o fit-to-page "
+ // . escapeshellarg($pdfPath);
+
+ // exec($cmd, $out, $status);
+
+ // if ($status != 0) {
+ // Notification::make()
+ // ->danger()
+ // ->title('Print Failed')
+ // ->body('CUPS print command failed.')
+ // ->send();
+ // return;
+ // }
+
+ //dd($iotsPrintIp, $matchedSticker);
+
+ StickerValidation::create([
+ 'plant_id' => $this->plantId,
+ 'machine_id' => $this->workCenter,
+ 'production_order' => $this->ref_number ?? null,
+ 'serial_number' => $serialNumber,
+ 'status' => 'Printed',
+ // 'sticker_id' => $matchedSticker,
+ 'created_by' => $operatorName,
+ 'created_at' => now(),
+ 'updated_at' => now(),
+ ]);
+
+ Notification::make()
+ ->success()
+ ->title('Sticker Recorded')
+ ->body("Item: $itemCode, Serial: $serialNumber recorded successfully!")
+ ->seconds(3)
+ ->send();
+
+ $this->form->fill([
+ 'plant_id' => $this->plantId,
+ 'machine_id' => $this->workCenter,
+ 'production_order' => $this->ref_number,
+ 'serial_number' => null,
+ ]);
+
+ $this->dispatch('refreshEmptySticker', $plantId, $this->ref_number);
+
+ }
+
+ }
+
+ private function getPrinterNameByIp(string $ip): ?string
+ {
+ exec('lpstat -v', $output);
+
+ foreach ($output as $line) {
+ // Example:
+ // device for TSC_WC_01: socket://192.168.1.50:9100
+ if (str_contains($line, $ip)) {
+ preg_match('/device for (.+?):/', $line, $matches);
+ return $matches[1] ?? null;
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/app/Filament/Resources/StickerValidationResource/Pages/EditStickerValidation.php b/app/Filament/Resources/StickerValidationResource/Pages/EditStickerValidation.php
new file mode 100644
index 0000000..a783f95
--- /dev/null
+++ b/app/Filament/Resources/StickerValidationResource/Pages/EditStickerValidation.php
@@ -0,0 +1,22 @@
+