big update all menus

This commit is contained in:
Wian Drs
2026-08-07 09:16:13 +07:00
parent 75342dc7b7
commit e7575ba1c0
101 changed files with 3451 additions and 89 deletions
@@ -3,6 +3,7 @@
namespace App\Http\Requests\Customer;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class ActivateCustomerRequest extends FormRequest
{
@@ -14,11 +15,16 @@ class ActivateCustomerRequest extends FormRequest
public function rules(): array
{
return [
'activation_source' => ['required', Rule::in(['list_order', 'ticket'])],
'package_profile_id' => ['required', 'integer', 'exists:nas_package_profiles,id'],
'nas_mikrotik_id' => ['required', 'integer', 'exists:nas_mikrotiks,id'],
'billing_profile_id' => ['required', 'integer', 'exists:billing_profiles,id'],
'topology_id' => ['nullable', 'integer', 'min:1'],
'external_username' => ['nullable', 'string', 'max:255'],
'external_username' => ['required', 'string', 'max:255'],
'external_password' => ['required', 'string', 'min:4', 'max:255'],
'mac_address' => ['nullable', 'regex:/^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$/'],
'serial_number' => ['nullable', 'string', 'max:120'],
'assigned_ip' => ['nullable', 'ip'],
];
}
}
@@ -17,7 +17,6 @@ class StoreCustomerRequest extends FormRequest
return [
'tenant_id' => ['nullable', 'integer', 'exists:tenants,id'],
'user_id' => ['nullable', 'integer', 'exists:users,id'],
'customer_code' => ['nullable', 'string', 'max:100'],
'name' => ['required', 'string', 'max:255'],
'identity_number' => ['nullable', 'string', 'max:100'],
'email' => ['nullable', 'email', 'max:255'],
@@ -32,10 +31,10 @@ class StoreCustomerRequest extends FormRequest
'package_profile_id' => ['nullable', 'integer', 'exists:nas_package_profiles,id'],
'nas_mikrotik_id' => ['nullable', 'integer', 'exists:nas_mikrotiks,id'],
'billing_profile_id' => ['nullable', 'integer', 'min:1'],
'topology_id' => ['nullable', 'integer', 'min:1'],
'external_username' => ['nullable', 'string', 'max:255'],
'topology_id' => ['nullable', 'integer', 'exists:topology_nodes,id'],
'topology_port_number' => ['nullable', 'required_with:topology_id', 'integer', 'min:1'],
'sales_user_id' => ['nullable', 'integer', 'exists:users,id'],
'source' => ['nullable', Rule::in(['manual', 'mikrotik', 'radius'])],
'order_stage' => ['nullable', Rule::in(['registration', 'data_completion', 'ready_activation', 'activation_failed'])],
'notes' => ['nullable', 'string'],
'images' => ['nullable', 'array', 'max:20'],
'images.*.stored_file_id' => ['required', 'integer', 'exists:stored_files,id'],
@@ -49,6 +49,7 @@ class SyncMenuGroupMenusRequest extends FormRequest
'nullable',
'boolean',
],
'menus.*.can_activate' => ['nullable','boolean'],
'menus.*.can_export' => [
'nullable',
'boolean',
@@ -18,7 +18,7 @@ class IndexNasResourceRequest extends FormRequest
'search' => ['nullable', 'string', 'max:255'],
'status' => ['nullable', Rule::in(['active', 'inactive'])],
'connection_type' => ['nullable', Rule::in(['api', 'radius'])],
'service_type' => ['nullable', Rule::in(['ppp', 'hotspot', 'radius'])],
'service_type' => ['nullable', Rule::in(['ppp', 'hotspot', 'static_ip', 'dynamic_ip'])],
'page' => ['nullable', 'integer', 'min:1'],
'per_page' => ['nullable', 'integer', 'min:1', 'max:100'],
'sort_by' => ['nullable', Rule::in(['id', 'name', 'host', 'connection_type', 'service_type', 'vendor', 'device_type', 'status', 'created_at'])],
@@ -17,17 +17,42 @@ class StoreNasResourceRequest extends FormRequest
return array_merge($this->commonRules(), match ($this->route()->defaults['nas_resource'] ?? null) {
'mikrotik' => [
'connection_type' => ['required', Rule::in(['api', 'radius'])],
'backend_mode_override' => ['required', Rule::in(['routeros_api','platform_radius','external_radius'])],
'host' => ['required', 'string', 'max:255'],
'nas_identifier' => ['nullable', 'string', 'max:255'],
'nas_ip_address' => ['nullable', 'ip'],
'radius_host' => ['nullable', 'string', 'max:255'],
'api_port' => ['nullable', 'integer', 'between:1,65535'],
'api_username' => ['nullable', 'string', 'max:255'],
'api_password' => ['nullable', 'string', 'max:1000'],
'radius_auth_port' => ['nullable', 'integer', 'between:1,65535'],
'radius_accounting_port' => ['nullable', 'integer', 'between:1,65535'],
'radius_coa_port' => ['nullable', 'integer', 'between:1,65535'],
'radius_secret' => ['nullable', 'string', 'max:1000'],
'timeout' => ['nullable', 'integer', 'between:1,300'],
],
'package-profile' => [
'service_type' => ['required', Rule::in(['ppp', 'hotspot', 'radius'])],
'service_type' => ['required', Rule::in(['ppp', 'hotspot', 'static_ip', 'dynamic_ip'])],
'profile_code' => ['required', 'alpha_dash', 'max:80'],
'usage_mode' => ['nullable', Rule::in(['one_time', 'periodic', 'recurring_member'])],
'validity_start' => ['nullable', Rule::in(['activation', 'first_login'])],
'validity_value' => ['nullable', 'integer', 'min:1'],
'validity_unit' => ['nullable', Rule::in(['minutes', 'hours', 'days', 'months'])],
'uptime_limit_seconds' => ['nullable', 'integer', 'min:0'],
'data_limit_bytes' => ['nullable', 'integer', 'min:0'],
'grace_period_minutes' => ['nullable', 'integer', 'min:0', 'max:10080'],
'mac_lock_required' => ['nullable', 'boolean'],
'ip_pool' => ['nullable', 'string', 'max:255'],
'subnet_cidr' => ['nullable', 'string', 'max:64'],
'gateway' => ['nullable', 'ip'],
'dns_servers' => ['nullable', 'array', 'max:4'],
'dns_servers.*' => ['required', 'ip'],
'vlan_id' => ['nullable', 'integer', 'between:1,4094'],
'service_parameters' => ['nullable', 'array'],
'scripts' => ['nullable', 'array'],
'scripts.*.policy_id' => ['required', 'integer', 'exists:nas_script_policies,id'],
'scripts.*.version_id' => ['nullable', 'integer', 'exists:nas_script_policy_versions,id'],
'scripts.*.event' => ['required', Rule::in(['on_up','on_down','on_login','on_logout'])],
'external_profile_name' => ['nullable', 'string', 'max:255'],
'download_kbps' => ['nullable', 'integer', 'min:0'],
'upload_kbps' => ['nullable', 'integer', 'min:0'],
@@ -72,4 +97,42 @@ class StoreNasResourceRequest extends FormRequest
'notes' => ['nullable', 'string'],
];
}
public function withValidator($validator): void
{
$resource = $this->route()->defaults['nas_resource'] ?? null;
if ($resource === 'mikrotik') {
$validator->after(function ($validator) {
$mode = $this->input('backend_mode_override');
if (! $this->input('api_username')) $validator->errors()->add('api_username', 'API Username wajib diisi untuk monitoring Mikrotik.');
if ($this->isMethod('post') && ! $this->input('api_password')) $validator->errors()->add('api_password', 'API Password wajib diisi.');
if ($mode !== 'routeros_api') {
if (! $this->input('nas_identifier')) $validator->errors()->add('nas_identifier', 'NAS Identifier wajib diisi untuk integrasi RADIUS.');
if (! $this->input('nas_ip_address')) $validator->errors()->add('nas_ip_address', 'IP NAS wajib diisi untuk integrasi RADIUS.');
if ($this->isMethod('post') && ! $this->input('radius_secret')) $validator->errors()->add('radius_secret', 'Shared secret khusus NAS wajib diisi.');
if ($mode === 'external_radius' && ! $this->input('radius_host')) $validator->errors()->add('radius_host', 'Host RADIUS Eksternal wajib diisi.');
}
});
return;
}
if ($resource !== 'package-profile') return;
$validator->after(function ($validator) {
$type = $this->input('service_type');
$mode = $this->input('usage_mode');
if ($type === 'hotspot' && ! in_array($mode, ['one_time', 'periodic', 'recurring_member'], true)) {
$validator->errors()->add('usage_mode', 'Mode pemakaian wajib dipilih untuk profile Hotspot.');
}
if ($type === 'hotspot' && in_array($mode, ['one_time', 'periodic', 'recurring_member'], true)
&& (! $this->input('validity_value') || ! $this->input('validity_unit'))) {
$validator->errors()->add('validity_value', 'Durasi dan satuan masa aktif wajib diisi untuk profile Hotspot.');
}
if (in_array($type, ['static_ip', 'dynamic_ip'], true)) {
if (! $this->boolean('mac_lock_required')) $validator->errors()->add('mac_lock_required', 'Penguncian MAC wajib aktif untuk profile IP Static/Dinamis.');
if (! $this->input('subnet_cidr')) $validator->errors()->add('subnet_cidr', 'Subnet CIDR wajib diisi.');
if (! $this->input('gateway')) $validator->errors()->add('gateway', 'Gateway wajib diisi.');
if ($type === 'dynamic_ip' && ! $this->input('ip_pool')) $validator->errors()->add('ip_pool', 'IP Pool wajib diisi untuk IP Dinamis.');
}
});
}
}
@@ -24,7 +24,8 @@ class AssignTicketRequest extends FormRequest
{
return [
'technicians' => 'required|array|min:1',
'leader_id' => 'required|integer',
'technicians.*' => 'required|integer|exists:users,id',
'leader_id' => 'required|integer|in_array:technicians.*',
];
}
}
@@ -32,6 +32,9 @@ class IndexTicketRequest extends FormRequest
'page' => ['nullable', 'integer', 'min:1'],
/** Jumlah data per halaman, maksimal 100. */
'per_page' => ['nullable', 'integer', 'min:1', 'max:100'],
'sort_by' => ['nullable', Rule::in(['ticket_no', 'title', 'priority', 'status', 'created_at'])],
'sort_direction' => ['nullable', Rule::in(['asc', 'desc'])],
'assigned_to_me' => ['nullable', 'boolean'],
];
}
}
@@ -23,7 +23,9 @@ class PendingTicketRequest extends FormRequest
public function rules(): array
{
return [
'reason' => 'required|string|max:1000'
'reason' => 'required|string|max:1000',
'stored_file_ids' => ['required','array','min:1'],
'stored_file_ids.*' => ['integer','exists:stored_files,id'],
];
}
}
@@ -23,7 +23,9 @@ class ResolveTicketRequest extends FormRequest
public function rules(): array
{
return [
'notes' => 'required|string|max:2000'
'notes' => 'required|string|max:2000',
'stored_file_ids' => ['required','array','min:1'],
'stored_file_ids.*' => ['integer','exists:stored_files,id'],
];
}
}
@@ -25,7 +25,9 @@ class StartTicketRequest extends FormRequest
return [
'latitude' => 'nullable|numeric',
'longitude' => 'nullable|numeric',
'notes' => 'nullable|string|max:1000'
'notes' => 'nullable|string|max:1000',
'stored_file_ids' => ['required','array','min:1'],
'stored_file_ids.*' => ['integer','exists:stored_files,id'],
];
}
}
@@ -23,8 +23,9 @@ class StoreTicketRequest extends FormRequest
public function rules(): array
{
return [
'ticket_type_id' => 'required|integer',
'customer_id' => 'nullable|integer',
'tenant_id' => 'nullable|integer|exists:tenants,id',
'ticket_type_id' => 'required|integer|exists:ticket_types,id',
'customer_id' => 'nullable|integer|exists:customers,id',
'title' => 'required|string|max:255',
'description' => 'nullable|string',
'priority' => 'required|in:low,medium,high,critical'