components([ Section::make(__('app.customer_product')) ->description(__('app.select_customer_product')) ->icon('heroicon-o-user-group') ->schema([ Select::make('customer_id') ->relationship('customer', 'name') ->searchable() ->required() ->live() ->columnSpan(1), Toggle::make('is_general') ->label(__('app.general_feedback')) ->default(false) ->live() ->columnSpan(1), Select::make('customer_product_id') ->label(__('app.product')) ->searchable() ->getSearchResultsUsing(function (string $search, $get): array { $customerId = $get('customer_id'); if (! $customerId) { return []; } $keyword = mb_strtolower($search); return CustomerProduct::where('customer_id', $customerId) ->whereHas('product', fn ($q) => $q->whereRaw('LOWER(name) LIKE ?', ["%{$keyword}%"])) ->with('product') ->limit(50) ->get() ->pluck('product.name', 'id') ->toArray(); }) ->getOptionLabelUsing(fn ($value): ?string => CustomerProduct::find($value)?->product?->name ) ->visible(fn ($get): bool => ! $get('is_general')) ->nullable() ->live() ->afterStateUpdated(function ($state, $set): void { if (empty($state)) { $set('contract_id', null); return; } $cp = CustomerProduct::find($state); if (! $cp) { return; } $activeContract = \App\Models\Contract::where('product_id', $cp->product_id) ->where('status', 'active') ->first(); if ($activeContract) { $set('contract_id', $activeContract->id); } else { $set('contract_id', null); } }), Select::make('contract_id') ->label(__('app.contract')) ->visible(fn ($get): bool => ! $get('is_general') && filled($get('customer_product_id'))) ->options(function ($get): array { $customerProductId = $get('customer_product_id'); if (! $customerProductId) { return []; } $cp = \App\Models\CustomerProduct::find($customerProductId); if (! $cp) { return []; } return \App\Models\Contract::where('product_id', $cp->product_id) ->where('status', 'active') ->with('customer') ->get() ->mapWithKeys(fn ($c) => [ $c->id => sprintf('%s (%s) - %s', \App\Enums\ContractType::from($c->type)->label(), $c->customer->name, $c->start_date?->format('d/m/Y') ?? 'N/A', ), ]) ->toArray(); }) ->afterStateHydrated(function ($component, $state, $get): void { if (filled($state)) { return; } $customerProductId = $get('customer_product_id'); if (! $customerProductId) { return; } $cp = \App\Models\CustomerProduct::find($customerProductId); if (! $cp) { return; } $activeContract = \App\Models\Contract::where('product_id', $cp->product_id) ->where('status', 'active') ->first(); if ($activeContract) { $component->state($activeContract->id); } }) ->nullable() ->searchable(), ]) ->columns(2), Section::make(__('app.feedback_details')) ->description(__('app.describe_feedback')) ->icon('heroicon-o-chat-bubble-left-right') ->schema([ Select::make('feedback_channel_id') ->relationship('feedbackChannel', 'name') ->required() ->preload() ->columnSpan(1), TextInput::make('title') ->required() ->maxLength(255) ->columnSpan(1), Textarea::make('content') ->required() ->rows(6) ->columnSpanFull(), Select::make('tags') ->relationship('tags', 'name') ->multiple() ->preload() ->columnSpanFull() ->createOptionForm([ TextInput::make('name')->required(), TextInput::make('slug')->required(), ColorPicker::make('color')->default('#3b82f6'), ]), ]) ->columns(2), Section::make(__('app.assignment_status')) ->description(__('app.assign_handler')) ->icon('heroicon-o-arrow-path-rounded-square') ->schema([ Select::make('status') ->options([ 'pending' => __('app.status_pending'), 'processing' => __('app.status_processing'), 'resolved' => __('app.status_resolved'), 'closed' => __('app.status_closed'), ]) ->default('pending') ->required() ->columnSpan(1), Select::make('assigned_to') ->relationship('assignedTo', 'name') ->searchable() ->nullable() ->columnSpan(1), Select::make('current_department_id') ->label(__('app.department')) ->options(Department::pluck('name', 'id')->toArray()) ->searchable() ->nullable() ->columnSpan(1), Toggle::make('is_escalated') ->label(__('app.escalated')) ->visible(fn (): bool => auth()->user()?->hasPermissionTo('close-ticket') ?? false) ->columnSpan(1), ]) ->columns(2), Section::make(__('app.attachments')) ->description(__('app.upload_files')) ->icon('heroicon-o-paper-clip') ->schema([ Select::make('attachment_collection') ->label(__('app.collection')) ->options([ 'general' => __('app.collection_general'), 'proof_of_resolution' => __('app.collection_proof_of_resolution'), 'customer_evidence' => __('app.collection_customer_evidence'), ]) ->default('general') ->columnSpan(1), FileUpload::make('attachments') ->label(__('app.files')) ->multiple() ->disk('public') ->directory('feedback-attachments') ->acceptedFileTypes(['image/jpeg', 'image/png', 'application/pdf', 'video/mp4', 'video/quicktime']) ->maxSize(51200) // 50MB in KB ->columnSpan(1), ]) ->columns(2), ]); } }