diff --git a/laravel12_filament4/.env.example b/laravel12_filament4/.env.example
new file mode 100644
index 00000000..857aaebe
--- /dev/null
+++ b/laravel12_filament4/.env.example
@@ -0,0 +1,35 @@
+APP_NAME="OV500 Laravel Filament"
+APP_ENV=local
+APP_KEY=
+APP_DEBUG=true
+APP_URL=http://localhost
+
+APP_LOCALE=en
+APP_FALLBACK_LOCALE=en
+APP_FAKER_LOCALE=en_US
+
+LOG_CHANNEL=stack
+LOG_LEVEL=debug
+
+DB_CONNECTION=mysql
+DB_HOST=127.0.0.1
+DB_PORT=3306
+DB_DATABASE=switch
+DB_USERNAME=root
+DB_PASSWORD=
+
+KAMAILIO_DB_CONNECTION=kamailio
+KAMAILIO_DB_DATABASE=kamailio
+CDR_DB_CONNECTION=switchcdr
+CDR_DB_DATABASE=switchcdr
+
+SESSION_DRIVER=database
+SESSION_LIFETIME=120
+CACHE_STORE=database
+QUEUE_CONNECTION=database
+
+MAIL_MAILER=log
+MAIL_FROM_ADDRESS="noreply@example.com"
+MAIL_FROM_NAME="${APP_NAME}"
+
+VITE_APP_NAME="${APP_NAME}"
diff --git a/laravel12_filament4/.gitignore b/laravel12_filament4/.gitignore
new file mode 100644
index 00000000..4ae36d78
--- /dev/null
+++ b/laravel12_filament4/.gitignore
@@ -0,0 +1,12 @@
+/vendor
+/node_modules
+/.env
+/.env.backup
+/.phpunit.result.cache
+/Homestead.json
+/Homestead.yaml
+/auth.json
+/npm-debug.log
+/yarn-error.log
+/storage/*.key
+/public/build
diff --git a/laravel12_filament4/README.md b/laravel12_filament4/README.md
new file mode 100644
index 00000000..6e97dea3
--- /dev/null
+++ b/laravel12_filament4/README.md
@@ -0,0 +1,47 @@
+# OV500 Laravel 12 + Filament 4 Portal Scaffold
+
+This directory is a Laravel 12 / Filament 4 rebuild target for the legacy OV500 CodeIgniter portal. It is designed for an incremental migration: keep the existing SQL dumps in `../config/database`, import them into MariaDB/MySQL, and point this application at those databases.
+
+## What is included
+
+- Laravel 12 application skeleton and Composer/NPM manifests.
+- Filament 4 admin panel registered at `/admin`.
+- Multi-database connection placeholders for `switch`, `kamailio`, and `switchcdr`.
+- Eloquent models and Filament CRUD resources for the first OV500 business modules:
+ - Customers
+ - Resellers
+ - Carriers
+ - Customer SIP accounts
+ - Customer rates
+ - Carrier rates
+ - DID inventory
+ - Invoices
+ - Tickets
+
+## Setup
+
+```bash
+cd laravel12_filament4
+composer install
+cp .env.example .env
+php artisan key:generate
+php artisan filament:install --panels
+php artisan make:filament-user
+npm install
+npm run build
+php artisan serve
+```
+
+> Note: this repository environment could not reach Packagist through the configured proxy, so dependencies were not installed here. Run the commands above in an environment with normal Composer network access.
+
+## Database migration path
+
+1. Import the legacy schemas from `../config/database/switch.sql`, `../config/database/kamailio.sql`, and `../config/database/switchcdr.sql`.
+2. Configure `.env` with the `DB_*`, `KAMAILIO_DB_*`, and `CDR_DB_*` credentials.
+3. Validate each Filament resource against live data and then migrate module-specific business logic from `../portal/application/modules`.
+4. Add Laravel migrations only after the legacy schema is stabilized or replaced.
+
+## Compatibility notes
+
+- Filament 4 requires Laravel 11.28+ and PHP 8.2+, and this scaffold pins Laravel Framework `^12.0` with Filament `^4.0`.
+- The legacy OV500 tables use non-standard timestamp and primary-key names, so each model explicitly declares table, key, timestamp, and guarded/fillable behavior.
diff --git a/laravel12_filament4/app/Filament/Resources/BillInvoices/BillInvoiceResource.php b/laravel12_filament4/app/Filament/Resources/BillInvoices/BillInvoiceResource.php
new file mode 100644
index 00000000..7ea216e2
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/BillInvoices/BillInvoiceResource.php
@@ -0,0 +1,41 @@
+ Pages\ListBillInvoices::route('/'),
+ 'create' => Pages\CreateBillInvoice::route('/create'),
+ 'edit' => Pages\EditBillInvoice::route('/{record}/edit'),
+ ];
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/BillInvoices/Pages/CreateBillInvoice.php b/laravel12_filament4/app/Filament/Resources/BillInvoices/Pages/CreateBillInvoice.php
new file mode 100644
index 00000000..8dce511f
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/BillInvoices/Pages/CreateBillInvoice.php
@@ -0,0 +1,11 @@
+components([
+ TextInput::make('invoice_id')->label('Invoice Id')->required()->maxLength(255),
+ TextInput::make('account_id')->label('Account Id')->required()->maxLength(255),
+ TextInput::make('company_name')->label('Company Name')->maxLength(255),
+ Textarea::make('email_address')->label('Email Address')->columnSpanFull(),
+ TextInput::make('phone_number')->label('Phone Number')->maxLength(255),
+ DateTimePicker::make('bill_date')->label('Bill Date'),
+ Select::make('billing_cycle')->label('Billing Cycle')->options(['weekly' => 'weekly', 'monthly' => 'monthly', 'MONTHLY' => 'MONTHLY', 'DAILY' => 'DAILY', 'WEEKLY' => 'WEEKLY']),
+ TextInput::make('payment_terms')->label('Payment Terms')->maxLength(255),
+ DateTimePicker::make('next_billing_date')->label('Next Billing Date'),
+ DateTimePicker::make('billing_date_from')->label('Billing Date From'),
+ DateTimePicker::make('billing_date_to')->label('Billing Date To'),
+ TextInput::make('last_bill_amount')->label('Last Bill Amount')->maxLength(255),
+ TextInput::make('currency_symbol')->label('Currency Symbol')->maxLength(255),
+ TextInput::make('payments')->label('Payments')->maxLength(255),
+ ]);
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/BillInvoices/Tables/BillInvoicesTable.php b/laravel12_filament4/app/Filament/Resources/BillInvoices/Tables/BillInvoicesTable.php
new file mode 100644
index 00000000..dddaf412
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/BillInvoices/Tables/BillInvoicesTable.php
@@ -0,0 +1,38 @@
+columns([
+ TextColumn::make('id')->label('Id')->sortable()->searchable(),
+ TextColumn::make('invoice_id')->label('Invoice Id')->sortable()->searchable(),
+ TextColumn::make('account_id')->label('Account Id')->sortable()->searchable(),
+ TextColumn::make('company_name')->label('Company Name')->sortable()->searchable(),
+ TextColumn::make('email_address')->label('Email Address')->sortable()->searchable(),
+ TextColumn::make('phone_number')->label('Phone Number')->sortable()->searchable(),
+ TextColumn::make('bill_date')->label('Bill Date')->dateTime()->sortable(),
+ TextColumn::make('billing_cycle')->label('Billing Cycle')->sortable()->searchable(),
+ ])
+ ->filters([
+ // Add module-specific filters during the next migration pass.
+ ])
+ ->recordActions([
+ EditAction::make(),
+ ])
+ ->toolbarActions([
+ BulkActionGroup::make([
+ DeleteBulkAction::make(),
+ ]),
+ ]);
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/CarrierRates/CarrierRateResource.php b/laravel12_filament4/app/Filament/Resources/CarrierRates/CarrierRateResource.php
new file mode 100644
index 00000000..5fd863bb
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/CarrierRates/CarrierRateResource.php
@@ -0,0 +1,41 @@
+ Pages\ListCarrierRates::route('/'),
+ 'create' => Pages\CreateCarrierRate::route('/create'),
+ 'edit' => Pages\EditCarrierRate::route('/{record}/edit'),
+ ];
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/CarrierRates/Pages/CreateCarrierRate.php b/laravel12_filament4/app/Filament/Resources/CarrierRates/Pages/CreateCarrierRate.php
new file mode 100644
index 00000000..3cdb1088
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/CarrierRates/Pages/CreateCarrierRate.php
@@ -0,0 +1,11 @@
+components([
+ TextInput::make('ratecard_id')->label('Ratecard Id')->required()->maxLength(255),
+ TextInput::make('prefix')->label('Prefix')->required()->maxLength(255),
+ TextInput::make('destination')->label('Destination')->required()->maxLength(255),
+ TextInput::make('setup_charge')->label('Setup Charge')->maxLength(255),
+ TextInput::make('rental')->label('Rental')->maxLength(255),
+ TextInput::make('rate')->label('Rate')->maxLength(255),
+ TextInput::make('connection_charge')->label('Connection Charge')->maxLength(255),
+ TextInput::make('minimal_time')->label('Minimal Time')->maxLength(255),
+ TextInput::make('resolution_time')->label('Resolution Time')->maxLength(255),
+ Select::make('rates_status')->label('Rates Status')->options(['0' => '0', '1' => '1']),
+ TextInput::make('account_id')->label('Account Id')->required()->maxLength(255),
+ ]);
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/CarrierRates/Tables/CarrierRatesTable.php b/laravel12_filament4/app/Filament/Resources/CarrierRates/Tables/CarrierRatesTable.php
new file mode 100644
index 00000000..4601b09e
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/CarrierRates/Tables/CarrierRatesTable.php
@@ -0,0 +1,38 @@
+columns([
+ TextColumn::make('rate_id')->label('Rate Id')->sortable()->searchable(),
+ TextColumn::make('ratecard_id')->label('Ratecard Id')->sortable()->searchable(),
+ TextColumn::make('prefix')->label('Prefix')->sortable()->searchable(),
+ TextColumn::make('destination')->label('Destination')->sortable()->searchable(),
+ TextColumn::make('setup_charge')->label('Setup Charge')->sortable()->searchable(),
+ TextColumn::make('rental')->label('Rental')->sortable()->searchable(),
+ TextColumn::make('rate')->label('Rate')->sortable()->searchable(),
+ TextColumn::make('connection_charge')->label('Connection Charge')->sortable()->searchable(),
+ ])
+ ->filters([
+ // Add module-specific filters during the next migration pass.
+ ])
+ ->recordActions([
+ EditAction::make(),
+ ])
+ ->toolbarActions([
+ BulkActionGroup::make([
+ DeleteBulkAction::make(),
+ ]),
+ ]);
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/Carriers/CarrierResource.php b/laravel12_filament4/app/Filament/Resources/Carriers/CarrierResource.php
new file mode 100644
index 00000000..bc9f07a3
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/Carriers/CarrierResource.php
@@ -0,0 +1,41 @@
+ Pages\ListCarriers::route('/'),
+ 'create' => Pages\CreateCarrier::route('/create'),
+ 'edit' => Pages\EditCarrier::route('/{record}/edit'),
+ ];
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/Carriers/Pages/CreateCarrier.php b/laravel12_filament4/app/Filament/Resources/Carriers/Pages/CreateCarrier.php
new file mode 100644
index 00000000..23a0447f
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/Carriers/Pages/CreateCarrier.php
@@ -0,0 +1,11 @@
+components([
+ TextInput::make('carrier_id')->label('Carrier Id')->maxLength(255),
+ TextInput::make('carrier_name')->label('Carrier Name')->required()->maxLength(255),
+ TextInput::make('tariff_id')->label('Tariff Id')->maxLength(255),
+ Select::make('carrier_type')->label('Carrier Type')->options(['INBOUND' => 'INBOUND', 'OUTBOUND' => 'OUTBOUND']),
+ TextInput::make('carrier_status')->label('Carrier Status')->maxLength(255),
+ TextInput::make('carrier_cps')->label('Carrier Cps')->maxLength(255),
+ TextInput::make('carrier_cc')->label('Carrier Cc')->maxLength(255),
+ TextInput::make('provider_id')->label('Provider Id')->maxLength(255),
+ TextInput::make('account_id')->label('Account Id')->required()->maxLength(255),
+ DateTimePicker::make('created_dt')->label('Created Dt'),
+ DateTimePicker::make('updated_dt')->label('Updated Dt'),
+ ]);
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/Carriers/Tables/CarriersTable.php b/laravel12_filament4/app/Filament/Resources/Carriers/Tables/CarriersTable.php
new file mode 100644
index 00000000..591eff52
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/Carriers/Tables/CarriersTable.php
@@ -0,0 +1,38 @@
+columns([
+ TextColumn::make('id')->label('Id')->sortable()->searchable(),
+ TextColumn::make('carrier_id')->label('Carrier Id')->sortable()->searchable(),
+ TextColumn::make('carrier_name')->label('Carrier Name')->sortable()->searchable(),
+ TextColumn::make('tariff_id')->label('Tariff Id')->sortable()->searchable(),
+ TextColumn::make('carrier_type')->label('Carrier Type')->sortable()->searchable(),
+ TextColumn::make('carrier_status')->label('Carrier Status')->sortable()->searchable(),
+ TextColumn::make('carrier_cps')->label('Carrier Cps')->sortable()->searchable(),
+ TextColumn::make('carrier_cc')->label('Carrier Cc')->sortable()->searchable(),
+ ])
+ ->filters([
+ // Add module-specific filters during the next migration pass.
+ ])
+ ->recordActions([
+ EditAction::make(),
+ ])
+ ->toolbarActions([
+ BulkActionGroup::make([
+ DeleteBulkAction::make(),
+ ]),
+ ]);
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/CustomerRates/CustomerRateResource.php b/laravel12_filament4/app/Filament/Resources/CustomerRates/CustomerRateResource.php
new file mode 100644
index 00000000..6f2d6dd9
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/CustomerRates/CustomerRateResource.php
@@ -0,0 +1,41 @@
+ Pages\ListCustomerRates::route('/'),
+ 'create' => Pages\CreateCustomerRate::route('/create'),
+ 'edit' => Pages\EditCustomerRate::route('/{record}/edit'),
+ ];
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/CustomerRates/Pages/CreateCustomerRate.php b/laravel12_filament4/app/Filament/Resources/CustomerRates/Pages/CreateCustomerRate.php
new file mode 100644
index 00000000..9df6ff31
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/CustomerRates/Pages/CreateCustomerRate.php
@@ -0,0 +1,11 @@
+components([
+ TextInput::make('ratecard_id')->label('Ratecard Id')->required()->maxLength(255),
+ TextInput::make('prefix')->label('Prefix')->required()->maxLength(255),
+ TextInput::make('destination')->label('Destination')->required()->maxLength(255),
+ TextInput::make('setup_charge')->label('Setup Charge')->maxLength(255),
+ TextInput::make('rental')->label('Rental')->maxLength(255),
+ TextInput::make('rate')->label('Rate')->maxLength(255),
+ TextInput::make('connection_charge')->label('Connection Charge')->maxLength(255),
+ TextInput::make('minimal_time')->label('Minimal Time')->maxLength(255),
+ TextInput::make('resolution_time')->label('Resolution Time')->maxLength(255),
+ Select::make('rates_status')->label('Rates Status')->options(['0' => '0', '1' => '1']),
+ TextInput::make('account_id')->label('Account Id')->required()->maxLength(255),
+ ]);
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/CustomerRates/Tables/CustomerRatesTable.php b/laravel12_filament4/app/Filament/Resources/CustomerRates/Tables/CustomerRatesTable.php
new file mode 100644
index 00000000..2242b25a
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/CustomerRates/Tables/CustomerRatesTable.php
@@ -0,0 +1,38 @@
+columns([
+ TextColumn::make('rate_id')->label('Rate Id')->sortable()->searchable(),
+ TextColumn::make('ratecard_id')->label('Ratecard Id')->sortable()->searchable(),
+ TextColumn::make('prefix')->label('Prefix')->sortable()->searchable(),
+ TextColumn::make('destination')->label('Destination')->sortable()->searchable(),
+ TextColumn::make('setup_charge')->label('Setup Charge')->sortable()->searchable(),
+ TextColumn::make('rental')->label('Rental')->sortable()->searchable(),
+ TextColumn::make('rate')->label('Rate')->sortable()->searchable(),
+ TextColumn::make('connection_charge')->label('Connection Charge')->sortable()->searchable(),
+ ])
+ ->filters([
+ // Add module-specific filters during the next migration pass.
+ ])
+ ->recordActions([
+ EditAction::make(),
+ ])
+ ->toolbarActions([
+ BulkActionGroup::make([
+ DeleteBulkAction::make(),
+ ]),
+ ]);
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/CustomerSipAccounts/CustomerSipAccountResource.php b/laravel12_filament4/app/Filament/Resources/CustomerSipAccounts/CustomerSipAccountResource.php
new file mode 100644
index 00000000..f97d31f9
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/CustomerSipAccounts/CustomerSipAccountResource.php
@@ -0,0 +1,41 @@
+ Pages\ListCustomerSipAccounts::route('/'),
+ 'create' => Pages\CreateCustomerSipAccount::route('/create'),
+ 'edit' => Pages\EditCustomerSipAccount::route('/{record}/edit'),
+ ];
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/CustomerSipAccounts/Pages/CreateCustomerSipAccount.php b/laravel12_filament4/app/Filament/Resources/CustomerSipAccounts/Pages/CreateCustomerSipAccount.php
new file mode 100644
index 00000000..77ecddbe
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/CustomerSipAccounts/Pages/CreateCustomerSipAccount.php
@@ -0,0 +1,11 @@
+components([
+ TextInput::make('username')->label('Username')->required()->maxLength(255),
+ TextInput::make('secret')->label('Secret')->maxLength(255),
+ Textarea::make('ipaddress')->label('Ipaddress')->columnSpanFull(),
+ Select::make('status')->label('Status')->options(['open' => 'open', 'closed' => 'closed', 'assigned' => 'assigned', 'working' => 'working', 'waiting-confirmation' => 'waiting-confirmation', 'not-fixed' => 'not-fixed']),
+ TextInput::make('account_id')->label('Account Id')->required()->maxLength(255),
+ TextInput::make('sip_cc')->label('Sip Cc')->maxLength(255),
+ TextInput::make('sip_cps')->label('Sip Cps')->maxLength(255),
+ TextInput::make('extension_no')->label('Extension No')->maxLength(255),
+ TextInput::make('display_name')->label('Display Name')->maxLength(255),
+ TextInput::make('caller_id')->label('Caller Id')->maxLength(255),
+ TextInput::make('name')->label('Name')->maxLength(255),
+ Textarea::make('email_address')->label('Email Address')->columnSpanFull(),
+ TextInput::make('phone_number')->label('Phone Number')->maxLength(255),
+ TextInput::make('user_type')->label('User Type')->maxLength(255),
+ ]);
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/CustomerSipAccounts/Tables/CustomerSipAccountsTable.php b/laravel12_filament4/app/Filament/Resources/CustomerSipAccounts/Tables/CustomerSipAccountsTable.php
new file mode 100644
index 00000000..62967222
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/CustomerSipAccounts/Tables/CustomerSipAccountsTable.php
@@ -0,0 +1,38 @@
+columns([
+ TextColumn::make('id')->label('Id')->sortable()->searchable(),
+ TextColumn::make('username')->label('Username')->sortable()->searchable(),
+ TextColumn::make('secret')->label('Secret')->sortable()->searchable(),
+ TextColumn::make('ipaddress')->label('Ipaddress')->sortable()->searchable(),
+ TextColumn::make('status')->label('Status')->sortable()->searchable(),
+ TextColumn::make('account_id')->label('Account Id')->sortable()->searchable(),
+ TextColumn::make('sip_cc')->label('Sip Cc')->sortable()->searchable(),
+ TextColumn::make('sip_cps')->label('Sip Cps')->sortable()->searchable(),
+ ])
+ ->filters([
+ // Add module-specific filters during the next migration pass.
+ ])
+ ->recordActions([
+ EditAction::make(),
+ ])
+ ->toolbarActions([
+ BulkActionGroup::make([
+ DeleteBulkAction::make(),
+ ]),
+ ]);
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/Customers/CustomerResource.php b/laravel12_filament4/app/Filament/Resources/Customers/CustomerResource.php
new file mode 100644
index 00000000..c13b69ec
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/Customers/CustomerResource.php
@@ -0,0 +1,41 @@
+ Pages\ListCustomers::route('/'),
+ 'create' => Pages\CreateCustomer::route('/create'),
+ 'edit' => Pages\EditCustomer::route('/{record}/edit'),
+ ];
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/Customers/Pages/CreateCustomer.php b/laravel12_filament4/app/Filament/Resources/Customers/Pages/CreateCustomer.php
new file mode 100644
index 00000000..cc4a5a3f
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/Customers/Pages/CreateCustomer.php
@@ -0,0 +1,11 @@
+components([
+ TextInput::make('account_id')->label('Account Id')->required()->maxLength(255),
+ TextInput::make('company_name')->label('Company Name')->required()->maxLength(255),
+ TextInput::make('contact_name')->label('Contact Name')->maxLength(255),
+ TextInput::make('name')->label('Name')->maxLength(255),
+ TextInput::make('phone')->label('Phone')->maxLength(255),
+ Textarea::make('emailaddress')->label('Emailaddress')->columnSpanFull(),
+ Select::make('billing_type')->label('Billing Type')->options(['prepaid' => 'prepaid', 'postpaid' => 'postpaid', 'netoff' => 'netoff']),
+ Select::make('billing_cycle')->label('Billing Cycle')->options(['weekly' => 'weekly', 'monthly' => 'monthly', 'MONTHLY' => 'MONTHLY', 'DAILY' => 'DAILY', 'WEEKLY' => 'WEEKLY']),
+ TextInput::make('payment_terms')->label('Payment Terms')->maxLength(255),
+ DateTimePicker::make('next_billing_date')->label('Next Billing Date'),
+ DateTimePicker::make('created_dt')->label('Created Dt'),
+ DateTimePicker::make('updated_dt')->label('Updated Dt'),
+ ]);
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/Customers/Tables/CustomersTable.php b/laravel12_filament4/app/Filament/Resources/Customers/Tables/CustomersTable.php
new file mode 100644
index 00000000..af0ffa85
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/Customers/Tables/CustomersTable.php
@@ -0,0 +1,38 @@
+columns([
+ TextColumn::make('customer_id')->label('Customer Id')->sortable()->searchable(),
+ TextColumn::make('account_id')->label('Account Id')->sortable()->searchable(),
+ TextColumn::make('company_name')->label('Company Name')->sortable()->searchable(),
+ TextColumn::make('contact_name')->label('Contact Name')->sortable()->searchable(),
+ TextColumn::make('name')->label('Name')->sortable()->searchable(),
+ TextColumn::make('phone')->label('Phone')->sortable()->searchable(),
+ TextColumn::make('emailaddress')->label('Emailaddress')->sortable()->searchable(),
+ TextColumn::make('billing_type')->label('Billing Type')->sortable()->searchable(),
+ ])
+ ->filters([
+ // Add module-specific filters during the next migration pass.
+ ])
+ ->recordActions([
+ EditAction::make(),
+ ])
+ ->toolbarActions([
+ BulkActionGroup::make([
+ DeleteBulkAction::make(),
+ ]),
+ ]);
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/Dids/DidResource.php b/laravel12_filament4/app/Filament/Resources/Dids/DidResource.php
new file mode 100644
index 00000000..b1d613c9
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/Dids/DidResource.php
@@ -0,0 +1,41 @@
+ Pages\ListDids::route('/'),
+ 'create' => Pages\CreateDid::route('/create'),
+ 'edit' => Pages\EditDid::route('/{record}/edit'),
+ ];
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/Dids/Pages/CreateDid.php b/laravel12_filament4/app/Filament/Resources/Dids/Pages/CreateDid.php
new file mode 100644
index 00000000..a7d97dba
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/Dids/Pages/CreateDid.php
@@ -0,0 +1,11 @@
+components([
+ TextInput::make('did_number')->label('Did Number')->required()->maxLength(255),
+ Select::make('did_status')->label('Did Status')->options(['NEW' => 'NEW', 'USED' => 'USED', 'DEAD' => 'DEAD', 'BLOCKED' => 'BLOCKED']),
+ TextInput::make('carrier_id')->label('Carrier Id')->maxLength(255),
+ TextInput::make('account_id')->label('Account Id')->required()->maxLength(255),
+ DateTimePicker::make('assign_date')->label('Assign Date'),
+ DateTimePicker::make('create_date')->label('Create Date'),
+ TextInput::make('channels')->label('Channels')->maxLength(255),
+ TextInput::make('did_name')->label('Did Name')->maxLength(255),
+ Select::make('number_type')->label('Number Type')->options(['TFN' => 'TFN', 'DID' => 'DID']),
+ ]);
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/Dids/Tables/DidsTable.php b/laravel12_filament4/app/Filament/Resources/Dids/Tables/DidsTable.php
new file mode 100644
index 00000000..078eb79e
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/Dids/Tables/DidsTable.php
@@ -0,0 +1,38 @@
+columns([
+ TextColumn::make('did_id')->label('Did Id')->sortable()->searchable(),
+ TextColumn::make('did_number')->label('Did Number')->sortable()->searchable(),
+ TextColumn::make('did_status')->label('Did Status')->sortable()->searchable(),
+ TextColumn::make('carrier_id')->label('Carrier Id')->sortable()->searchable(),
+ TextColumn::make('account_id')->label('Account Id')->sortable()->searchable(),
+ TextColumn::make('assign_date')->label('Assign Date')->dateTime()->sortable(),
+ TextColumn::make('create_date')->label('Create Date')->dateTime()->sortable(),
+ TextColumn::make('channels')->label('Channels')->sortable()->searchable(),
+ ])
+ ->filters([
+ // Add module-specific filters during the next migration pass.
+ ])
+ ->recordActions([
+ EditAction::make(),
+ ])
+ ->toolbarActions([
+ BulkActionGroup::make([
+ DeleteBulkAction::make(),
+ ]),
+ ]);
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/Resellers/Pages/CreateReseller.php b/laravel12_filament4/app/Filament/Resources/Resellers/Pages/CreateReseller.php
new file mode 100644
index 00000000..1e037dff
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/Resellers/Pages/CreateReseller.php
@@ -0,0 +1,11 @@
+ Pages\ListResellers::route('/'),
+ 'create' => Pages\CreateReseller::route('/create'),
+ 'edit' => Pages\EditReseller::route('/{record}/edit'),
+ ];
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/Resellers/Schemas/ResellerForm.php b/laravel12_filament4/app/Filament/Resources/Resellers/Schemas/ResellerForm.php
new file mode 100644
index 00000000..b57a7a3d
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/Resellers/Schemas/ResellerForm.php
@@ -0,0 +1,25 @@
+components([
+ TextInput::make('account_id')->label('Account Id')->required()->maxLength(255),
+ TextInput::make('company_name')->label('Company Name')->required()->maxLength(255),
+ TextInput::make('contact_name')->label('Contact Name')->maxLength(255),
+ TextInput::make('phone')->label('Phone')->maxLength(255),
+ Textarea::make('emailaddress')->label('Emailaddress')->columnSpanFull(),
+ TextInput::make('pincode')->label('Pincode')->maxLength(255),
+ ]);
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/Resellers/Tables/ResellersTable.php b/laravel12_filament4/app/Filament/Resources/Resellers/Tables/ResellersTable.php
new file mode 100644
index 00000000..390e53a4
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/Resellers/Tables/ResellersTable.php
@@ -0,0 +1,37 @@
+columns([
+ TextColumn::make('id')->label('Id')->sortable()->searchable(),
+ TextColumn::make('account_id')->label('Account Id')->sortable()->searchable(),
+ TextColumn::make('company_name')->label('Company Name')->sortable()->searchable(),
+ TextColumn::make('contact_name')->label('Contact Name')->sortable()->searchable(),
+ TextColumn::make('phone')->label('Phone')->sortable()->searchable(),
+ TextColumn::make('emailaddress')->label('Emailaddress')->sortable()->searchable(),
+ TextColumn::make('pincode')->label('Pincode')->sortable()->searchable(),
+ ])
+ ->filters([
+ // Add module-specific filters during the next migration pass.
+ ])
+ ->recordActions([
+ EditAction::make(),
+ ])
+ ->toolbarActions([
+ BulkActionGroup::make([
+ DeleteBulkAction::make(),
+ ]),
+ ]);
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/Tickets/Pages/CreateTicket.php b/laravel12_filament4/app/Filament/Resources/Tickets/Pages/CreateTicket.php
new file mode 100644
index 00000000..d17ffbe9
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/Tickets/Pages/CreateTicket.php
@@ -0,0 +1,11 @@
+components([
+ TextInput::make('parent_id')->label('Parent Id')->maxLength(255),
+ TextInput::make('ticket_number')->label('Ticket Number')->required()->maxLength(255),
+ TextInput::make('subject')->label('Subject')->required()->maxLength(255),
+ Textarea::make('content')->label('Content')->columnSpanFull(),
+ TextInput::make('account_id')->label('Account Id')->required()->maxLength(255),
+ TextInput::make('company_name')->label('Company Name')->maxLength(255),
+ TextInput::make('category_id')->label('Category Id')->maxLength(255),
+ TextInput::make('assigned_to_id')->label('Assigned To Id')->maxLength(255),
+ TextInput::make('assigned_to_user_name')->label('Assigned To User Name')->maxLength(255),
+ Select::make('status')->label('Status')->options(['open' => 'open', 'closed' => 'closed', 'assigned' => 'assigned', 'working' => 'working', 'waiting-confirmation' => 'waiting-confirmation', 'not-fixed' => 'not-fixed']),
+ Select::make('hide_from_customer')->label('Hide From Customer')->options(['Y' => 'Y', 'N' => 'N']),
+ TextInput::make('created_by_name')->label('Created By Name')->maxLength(255),
+ DateTimePicker::make('create_date')->label('Create Date'),
+ DateTimePicker::make('close_date')->label('Close Date'),
+ ]);
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/Tickets/Tables/TicketsTable.php b/laravel12_filament4/app/Filament/Resources/Tickets/Tables/TicketsTable.php
new file mode 100644
index 00000000..8ea9c878
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/Tickets/Tables/TicketsTable.php
@@ -0,0 +1,38 @@
+columns([
+ TextColumn::make('ticket_id')->label('Ticket Id')->sortable()->searchable(),
+ TextColumn::make('parent_id')->label('Parent Id')->sortable()->searchable(),
+ TextColumn::make('ticket_number')->label('Ticket Number')->sortable()->searchable(),
+ TextColumn::make('subject')->label('Subject')->sortable()->searchable(),
+ TextColumn::make('content')->label('Content')->sortable()->searchable(),
+ TextColumn::make('account_id')->label('Account Id')->sortable()->searchable(),
+ TextColumn::make('company_name')->label('Company Name')->sortable()->searchable(),
+ TextColumn::make('category_id')->label('Category Id')->sortable()->searchable(),
+ ])
+ ->filters([
+ // Add module-specific filters during the next migration pass.
+ ])
+ ->recordActions([
+ EditAction::make(),
+ ])
+ ->toolbarActions([
+ BulkActionGroup::make([
+ DeleteBulkAction::make(),
+ ]),
+ ]);
+ }
+}
diff --git a/laravel12_filament4/app/Filament/Resources/Tickets/TicketResource.php b/laravel12_filament4/app/Filament/Resources/Tickets/TicketResource.php
new file mode 100644
index 00000000..d4fa94dc
--- /dev/null
+++ b/laravel12_filament4/app/Filament/Resources/Tickets/TicketResource.php
@@ -0,0 +1,41 @@
+ Pages\ListTickets::route('/'),
+ 'create' => Pages\CreateTicket::route('/create'),
+ 'edit' => Pages\EditTicket::route('/{record}/edit'),
+ ];
+ }
+}
diff --git a/laravel12_filament4/app/Http/Controllers/LegacyPortalController.php b/laravel12_filament4/app/Http/Controllers/LegacyPortalController.php
new file mode 100644
index 00000000..4c82c98e
--- /dev/null
+++ b/laravel12_filament4/app/Http/Controllers/LegacyPortalController.php
@@ -0,0 +1,73 @@
+
+ */
+ private const CONTROLLERS = [
+ 'account' => ['label' => 'Account profile', 'status' => 'pending'],
+ 'bundle' => ['label' => 'Bundle plans', 'status' => 'pending'],
+ 'carriers' => ['label' => 'Carriers', 'target' => '/admin/carriers', 'status' => 'redirect'],
+ 'currency' => ['label' => 'Currency', 'status' => 'pending'],
+ 'dashboard' => ['label' => 'Dashboard', 'target' => '/admin', 'status' => 'redirect'],
+ 'dialplans' => ['label' => 'Dial plans', 'status' => 'pending'],
+ 'dids' => ['label' => 'DID inventory', 'target' => '/admin/dids', 'status' => 'redirect'],
+ 'download' => ['label' => 'Downloads', 'status' => 'pending'],
+ 'endpoints' => ['label' => 'Customer SIP accounts', 'target' => '/admin/customer-sip-accounts', 'status' => 'redirect'],
+ 'login' => ['label' => 'Login', 'target' => '/admin/login', 'status' => 'redirect'],
+ 'logout' => ['label' => 'Logout', 'target' => '/admin/logout', 'status' => 'redirect'],
+ 'module' => ['label' => 'Modules', 'status' => 'pending'],
+ 'page' => ['label' => 'Pages', 'status' => 'pending'],
+ 'providers' => ['label' => 'Providers', 'target' => '/admin/carriers', 'status' => 'redirect'],
+ 'ratecard' => ['label' => 'Rate cards', 'target' => '/admin/customer-rates', 'status' => 'redirect'],
+ 'rates' => ['label' => 'Rates', 'target' => '/admin/customer-rates', 'status' => 'redirect'],
+ 'recyclebin' => ['label' => 'Recycle bin', 'status' => 'pending'],
+ 'reports' => ['label' => 'Reports', 'status' => 'pending'],
+ 'roles' => ['label' => 'Roles', 'status' => 'pending'],
+ 'routes' => ['label' => 'Routes', 'status' => 'pending'],
+ 'sitesetup' => ['label' => 'Site setup', 'status' => 'pending'],
+ 'sysconfig' => ['label' => 'System configuration', 'status' => 'pending'],
+ 'tariffs' => ['label' => 'Tariffs', 'target' => '/admin/customer-rates', 'status' => 'redirect'],
+ 'upload' => ['label' => 'Uploads', 'status' => 'pending'],
+ 'users' => ['label' => 'Users', 'status' => 'pending'],
+ ];
+
+ public function dashboard(): RedirectResponse
+ {
+ return redirect('/admin');
+ }
+
+ public function handle(
+ Request $request,
+ string $legacyController,
+ ?string $legacyAction = null,
+ ?string $legacyParameters = null,
+ ): RedirectResponse|View {
+ $controller = strtolower($legacyController);
+ $definition = self::CONTROLLERS[$controller] ?? null;
+
+ if ($definition !== null && ($definition['status'] ?? null) === 'redirect' && isset($definition['target'])) {
+ return redirect($definition['target']);
+ }
+
+ return view('legacy.controller', [
+ 'action' => $legacyAction,
+ 'controller' => $controller,
+ 'controllers' => self::CONTROLLERS,
+ 'definition' => $definition,
+ 'parameters' => $legacyParameters,
+ 'requestedPath' => $request->path(),
+ ]);
+ }
+}
diff --git a/laravel12_filament4/app/Models/BillInvoice.php b/laravel12_filament4/app/Models/BillInvoice.php
new file mode 100644
index 00000000..5961c25d
--- /dev/null
+++ b/laravel12_filament4/app/Models/BillInvoice.php
@@ -0,0 +1,28 @@
+ 'datetime',
+ 'next_billing_date' => 'datetime',
+ 'billing_date_from' => 'datetime',
+ 'billing_date_to' => 'datetime',
+ 'last_bill_amount' => 'decimal:6',
+ 'payments' => 'decimal:6',
+ 'usage_amount' => 'decimal:6',
+ 'current_due_amount' => 'decimal:6',
+ 'bill_amount' => 'decimal:6',
+ ];
+}
diff --git a/laravel12_filament4/app/Models/Carrier.php b/laravel12_filament4/app/Models/Carrier.php
new file mode 100644
index 00000000..c3bd1183
--- /dev/null
+++ b/laravel12_filament4/app/Models/Carrier.php
@@ -0,0 +1,21 @@
+ 'datetime',
+ 'updated_dt' => 'datetime',
+ ];
+}
diff --git a/laravel12_filament4/app/Models/CarrierRate.php b/laravel12_filament4/app/Models/CarrierRate.php
new file mode 100644
index 00000000..ab1f5b80
--- /dev/null
+++ b/laravel12_filament4/app/Models/CarrierRate.php
@@ -0,0 +1,25 @@
+ 'decimal:6',
+ 'setup_charge' => 'decimal:6',
+ 'rental' => 'decimal:6',
+ 'rate' => 'decimal:6',
+ 'connection_charge' => 'decimal:6',
+ 'rates_status' => 'decimal:6',
+ ];
+}
diff --git a/laravel12_filament4/app/Models/Customer.php b/laravel12_filament4/app/Models/Customer.php
new file mode 100644
index 00000000..df0f601d
--- /dev/null
+++ b/laravel12_filament4/app/Models/Customer.php
@@ -0,0 +1,22 @@
+ 'datetime',
+ 'created_dt' => 'datetime',
+ 'updated_dt' => 'datetime',
+ ];
+}
diff --git a/laravel12_filament4/app/Models/CustomerRate.php b/laravel12_filament4/app/Models/CustomerRate.php
new file mode 100644
index 00000000..fcf10fdc
--- /dev/null
+++ b/laravel12_filament4/app/Models/CustomerRate.php
@@ -0,0 +1,25 @@
+ 'decimal:6',
+ 'setup_charge' => 'decimal:6',
+ 'rental' => 'decimal:6',
+ 'rate' => 'decimal:6',
+ 'connection_charge' => 'decimal:6',
+ 'rates_status' => 'decimal:6',
+ ];
+}
diff --git a/laravel12_filament4/app/Models/CustomerSipAccount.php b/laravel12_filament4/app/Models/CustomerSipAccount.php
new file mode 100644
index 00000000..123dea33
--- /dev/null
+++ b/laravel12_filament4/app/Models/CustomerSipAccount.php
@@ -0,0 +1,20 @@
+ 'datetime',
+ 'create_date' => 'datetime',
+ ];
+}
diff --git a/laravel12_filament4/app/Models/Reseller.php b/laravel12_filament4/app/Models/Reseller.php
new file mode 100644
index 00000000..7fde8749
--- /dev/null
+++ b/laravel12_filament4/app/Models/Reseller.php
@@ -0,0 +1,20 @@
+ 'datetime',
+ 'close_date' => 'datetime',
+ ];
+}
diff --git a/laravel12_filament4/app/Models/User.php b/laravel12_filament4/app/Models/User.php
new file mode 100644
index 00000000..3fd8b8af
--- /dev/null
+++ b/laravel12_filament4/app/Models/User.php
@@ -0,0 +1,33 @@
+secret;
+ }
+
+ public function canAccessPanel(Panel $panel): bool
+ {
+ return (int) ($this->status_id ?? 0) === 1;
+ }
+}
diff --git a/laravel12_filament4/app/Providers/AppServiceProvider.php b/laravel12_filament4/app/Providers/AppServiceProvider.php
new file mode 100644
index 00000000..70a18d22
--- /dev/null
+++ b/laravel12_filament4/app/Providers/AppServiceProvider.php
@@ -0,0 +1,18 @@
+default()
+ ->id('admin')
+ ->path('admin')
+ ->login()
+ ->colors([
+ 'primary' => Color::Blue,
+ ])
+ ->discoverResources(in: app_path('Filament/Resources'), for: 'App\Filament\Resources')
+ ->discoverPages(in: app_path('Filament/Pages'), for: 'App\Filament\Pages')
+ ->pages([
+ \Filament\Pages\Dashboard::class,
+ ])
+ ->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\Filament\Widgets')
+ ->widgets([
+ Widgets\AccountWidget::class,
+ Widgets\FilamentInfoWidget::class,
+ ])
+ ->middleware([
+ EncryptCookies::class,
+ AddQueuedCookiesToResponse::class,
+ StartSession::class,
+ AuthenticateSession::class,
+ ShareErrorsFromSession::class,
+ VerifyCsrfToken::class,
+ SubstituteBindings::class,
+ DisableBladeIconComponents::class,
+ DispatchServingFilamentEvent::class,
+ ])
+ ->authMiddleware([
+ Authenticate::class,
+ ]);
+ }
+}
diff --git a/laravel12_filament4/artisan b/laravel12_filament4/artisan
new file mode 100755
index 00000000..e22c0064
--- /dev/null
+++ b/laravel12_filament4/artisan
@@ -0,0 +1,12 @@
+#!/usr/bin/env php
+handleCommand(new Symfony\Component\Console\Input\ArgvInput);
+
+exit($status);
diff --git a/laravel12_filament4/bootstrap/app.php b/laravel12_filament4/bootstrap/app.php
new file mode 100644
index 00000000..d2b6a52b
--- /dev/null
+++ b/laravel12_filament4/bootstrap/app.php
@@ -0,0 +1,19 @@
+withRouting(
+ web: __DIR__.'/../routes/web.php',
+ commands: __DIR__.'/../routes/console.php',
+ health: '/up',
+ )
+ ->withMiddleware(function (Middleware $middleware): void {
+ // Register portal-wide HTTP middleware here as modules are migrated.
+ })
+ ->withExceptions(function (Exceptions $exceptions): void {
+ // Customize exception reporting for switch and billing operations here.
+ })
+ ->create();
diff --git a/laravel12_filament4/bootstrap/providers.php b/laravel12_filament4/bootstrap/providers.php
new file mode 100644
index 00000000..22744d11
--- /dev/null
+++ b/laravel12_filament4/bootstrap/providers.php
@@ -0,0 +1,6 @@
+ env('APP_NAME', 'OV500 Laravel Filament'),
+ 'env' => env('APP_ENV', 'production'),
+ 'debug' => (bool) env('APP_DEBUG', false),
+ 'url' => env('APP_URL', 'http://localhost'),
+ 'timezone' => env('APP_TIMEZONE', 'UTC'),
+ 'locale' => env('APP_LOCALE', 'en'),
+ 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
+ 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'),
+ 'cipher' => 'AES-256-CBC',
+ 'key' => env('APP_KEY'),
+ 'previous_keys' => array_filter(explode(',', env('APP_PREVIOUS_KEYS', ''))),
+ 'maintenance' => [
+ 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),
+ 'store' => env('APP_MAINTENANCE_STORE', 'database'),
+ ],
+];
diff --git a/laravel12_filament4/config/database.php b/laravel12_filament4/config/database.php
new file mode 100644
index 00000000..ea6799c1
--- /dev/null
+++ b/laravel12_filament4/config/database.php
@@ -0,0 +1,98 @@
+ env('DB_CONNECTION', 'mysql'),
+
+ 'connections' => [
+ 'sqlite' => [
+ 'driver' => 'sqlite',
+ 'url' => env('DB_URL'),
+ 'database' => env('DB_DATABASE', database_path('database.sqlite')),
+ 'prefix' => '',
+ 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
+ ],
+
+ 'mysql' => [
+ 'driver' => 'mysql',
+ 'url' => env('DB_URL'),
+ 'host' => env('DB_HOST', '127.0.0.1'),
+ 'port' => env('DB_PORT', '3306'),
+ 'database' => env('DB_DATABASE', 'switch'),
+ 'username' => env('DB_USERNAME', 'root'),
+ 'password' => env('DB_PASSWORD', ''),
+ 'unix_socket' => env('DB_SOCKET', ''),
+ 'charset' => env('DB_CHARSET', 'utf8mb4'),
+ 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
+ 'prefix' => '',
+ 'prefix_indexes' => true,
+ 'strict' => true,
+ 'engine' => null,
+ 'options' => extension_loaded('pdo_mysql') ? array_filter([
+ PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
+ ]) : [],
+ ],
+
+ 'kamailio' => [
+ 'driver' => 'mysql',
+ 'host' => env('KAMAILIO_DB_HOST', env('DB_HOST', '127.0.0.1')),
+ 'port' => env('KAMAILIO_DB_PORT', env('DB_PORT', '3306')),
+ 'database' => env('KAMAILIO_DB_DATABASE', 'kamailio'),
+ 'username' => env('KAMAILIO_DB_USERNAME', env('DB_USERNAME', 'root')),
+ 'password' => env('KAMAILIO_DB_PASSWORD', env('DB_PASSWORD', '')),
+ 'unix_socket' => env('DB_SOCKET', ''),
+ 'charset' => 'utf8mb4',
+ 'collation' => 'utf8mb4_unicode_ci',
+ 'prefix' => '',
+ 'prefix_indexes' => true,
+ 'strict' => false,
+ 'engine' => null,
+ ],
+
+ 'switchcdr' => [
+ 'driver' => 'mysql',
+ 'host' => env('CDR_DB_HOST', env('DB_HOST', '127.0.0.1')),
+ 'port' => env('CDR_DB_PORT', env('DB_PORT', '3306')),
+ 'database' => env('CDR_DB_DATABASE', 'switchcdr'),
+ 'username' => env('CDR_DB_USERNAME', env('DB_USERNAME', 'root')),
+ 'password' => env('CDR_DB_PASSWORD', env('DB_PASSWORD', '')),
+ 'unix_socket' => env('DB_SOCKET', ''),
+ 'charset' => 'utf8mb4',
+ 'collation' => 'utf8mb4_unicode_ci',
+ 'prefix' => '',
+ 'prefix_indexes' => true,
+ 'strict' => false,
+ 'engine' => null,
+ ],
+ ],
+
+ 'migrations' => [
+ 'table' => 'migrations',
+ 'update_date_on_publish' => true,
+ ],
+
+ 'redis' => [
+ 'client' => env('REDIS_CLIENT', 'phpredis'),
+ 'options' => [
+ 'cluster' => env('REDIS_CLUSTER', 'redis'),
+ 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
+ ],
+ 'default' => [
+ 'url' => env('REDIS_URL'),
+ 'host' => env('REDIS_HOST', '127.0.0.1'),
+ 'username' => env('REDIS_USERNAME'),
+ 'password' => env('REDIS_PASSWORD'),
+ 'port' => env('REDIS_PORT', '6379'),
+ 'database' => env('REDIS_DB', '0'),
+ ],
+ 'cache' => [
+ 'url' => env('REDIS_URL'),
+ 'host' => env('REDIS_HOST', '127.0.0.1'),
+ 'username' => env('REDIS_USERNAME'),
+ 'password' => env('REDIS_PASSWORD'),
+ 'port' => env('REDIS_PORT', '6379'),
+ 'database' => env('REDIS_CACHE_DB', '1'),
+ ],
+ ],
+];
diff --git a/laravel12_filament4/database/seeders/DatabaseSeeder.php b/laravel12_filament4/database/seeders/DatabaseSeeder.php
new file mode 100644
index 00000000..40452929
--- /dev/null
+++ b/laravel12_filament4/database/seeders/DatabaseSeeder.php
@@ -0,0 +1,13 @@
+handleRequest(Illuminate\Http\Request::capture());
diff --git a/laravel12_filament4/resources/css/app.css b/laravel12_filament4/resources/css/app.css
new file mode 100644
index 00000000..34854310
--- /dev/null
+++ b/laravel12_filament4/resources/css/app.css
@@ -0,0 +1,6 @@
+@import 'tailwindcss';
+
+@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
+@source '../../storage/framework/views/*.php';
+@source '../**/*.blade.php';
+@source '../**/*.js';
diff --git a/laravel12_filament4/resources/js/app.js b/laravel12_filament4/resources/js/app.js
new file mode 100644
index 00000000..e59d6a0a
--- /dev/null
+++ b/laravel12_filament4/resources/js/app.js
@@ -0,0 +1 @@
+import './bootstrap';
diff --git a/laravel12_filament4/resources/js/bootstrap.js b/laravel12_filament4/resources/js/bootstrap.js
new file mode 100644
index 00000000..7d62edcf
--- /dev/null
+++ b/laravel12_filament4/resources/js/bootstrap.js
@@ -0,0 +1,4 @@
+import axios from 'axios';
+
+window.axios = axios;
+window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
diff --git a/laravel12_filament4/resources/views/legacy/controller.blade.php b/laravel12_filament4/resources/views/legacy/controller.blade.php
new file mode 100644
index 00000000..445575f9
--- /dev/null
+++ b/laravel12_filament4/resources/views/legacy/controller.blade.php
@@ -0,0 +1,71 @@
+
+
+
+
+
+ {{ config('app.name') }} - Legacy controller
+ @vite(['resources/css/app.css', 'resources/js/app.js'])
+
+
+
+ ← OV500 Laravel portal
+
+
+ Standalone Laravel route
+
+ {{ $definition['label'] ?? ucfirst($controller) }} controller
+
+
+
+ Laravel handled /{{ $requestedPath }}
+ directly, without booting the legacy CodeIgniter controller from
+ portal/application/controllers.
+
+
+ @if ($definition === null)
+
+ This legacy controller is not registered in the Laravel compatibility map yet.
+
+ @else
+
+
+
+
- Controller
+ - {{ $controller }}
+
+
+
- Action
+ - {{ $action ?? 'index' }}
+
+
+
- Status
+ - {{ $definition['status'] }}
+
+
+
+ @if ($parameters)
+
+ Extra path parameters: {{ $parameters }}
+
+ @endif
+
+ @endif
+
+
+
+ Registered legacy controller redirects
+
+
+
+
+
diff --git a/laravel12_filament4/resources/views/welcome.blade.php b/laravel12_filament4/resources/views/welcome.blade.php
new file mode 100644
index 00000000..cd68c830
--- /dev/null
+++ b/laravel12_filament4/resources/views/welcome.blade.php
@@ -0,0 +1,23 @@
+
+
+
+
+
+ {{ config('app.name') }}
+ @vite(['resources/css/app.css', 'resources/js/app.js'])
+
+
+
+ OV500
+ Laravel 12 + Filament 4 portal
+
+ This rebuild scaffold connects Laravel models and Filament resources to the existing switch,
+ Kamailio, and CDR databases while the CodeIgniter modules are migrated incrementally.
+
+
+
+
+
diff --git a/laravel12_filament4/routes/console.php b/laravel12_filament4/routes/console.php
new file mode 100644
index 00000000..f5ef9e91
--- /dev/null
+++ b/laravel12_filament4/routes/console.php
@@ -0,0 +1,7 @@
+info('OV500 Laravel 12 / Filament 4 portal scaffold');
+})->purpose('Display information about the OV500 Laravel rebuild');
diff --git a/laravel12_filament4/routes/web.php b/laravel12_filament4/routes/web.php
new file mode 100644
index 00000000..a79a12c6
--- /dev/null
+++ b/laravel12_filament4/routes/web.php
@@ -0,0 +1,13 @@
+name('home');
+
+Route::get('/dashboard', [LegacyPortalController::class, 'dashboard'])->name('legacy.dashboard');
+
+Route::get('/{legacyController}/{legacyAction?}/{legacyParameters?}', [LegacyPortalController::class, 'handle'])
+ ->where('legacyController', 'account|bundle|carriers|currency|dashboard|dialplans|dids|download|endpoints|login|logout|module|page|providers|ratecard|rates|recyclebin|reports|roles|routes|sitesetup|sysconfig|tariffs|upload|users')
+ ->where('legacyParameters', '.*')
+ ->name('legacy.controller');
diff --git a/laravel12_filament4/vite.config.js b/laravel12_filament4/vite.config.js
new file mode 100644
index 00000000..29fbfe9a
--- /dev/null
+++ b/laravel12_filament4/vite.config.js
@@ -0,0 +1,13 @@
+import { defineConfig } from 'vite';
+import laravel from 'laravel-vite-plugin';
+import tailwindcss from '@tailwindcss/vite';
+
+export default defineConfig({
+ plugins: [
+ laravel({
+ input: ['resources/css/app.css', 'resources/js/app.js'],
+ refresh: true,
+ }),
+ tailwindcss(),
+ ],
+});