user()->company; if (! $company) { return; } $this->isVerificationStage = in_array($company->verificationRequest?->status, [ VerificationStatus::PENDING, VerificationStatus::REJECTED, ]) || $company->verificationRequest()->pending()->exists(); $this->verificationRequest = $company->verificationRequest() ->latest() ->first(); $this->company = $company; $this->form->fill($company->toArray()); $documents = [ 'director_nik', 'deed_incorporation', 'trade_license', 'tax_id_number', 'taxable_enterprise', 'annual_tax_return', 'domicile_certificate', 'profile', ]; $mediaItems = $company->getMedia('companies'); foreach ($documents as $docType) { $media = $mediaItems ->where('custom_properties.doc_type', str($docType)->replace('_docs', '')->slug('-')) ->sortByDesc('created_at') ->first(); if ($media) { $path = $media->getPathRelativeToRoot(); $this->data["{$docType}_docs"] = [$path]; $this->originalDocuments["{$docType}_docs"] = [$path]; } } } public static function form(Schema $schema): Schema { $documents = [ [ 'title' => 'Akta Pendirian', 'text_name' => 'deed_incorporation', 'placeholder' => '*****', 'max' => 150, 'max_size' => 1024 * 10, 'file_name' => 'deed_incorporation_docs', 'accept' => ['application/pdf'], 'folder' => 'companies/deed-incorporation/', ], [ 'title' => 'SIUP / NIB', 'text_name' => 'trade_license', 'placeholder' => '*****', 'max' => 150, 'max_size' => 1024 * 10, 'file_name' => 'trade_license_docs', 'accept' => ['application/pdf'], 'folder' => 'companies/trade-license/', ], [ 'title' => 'NPWP', 'text_name' => 'tax_id_number', 'placeholder' => '*****', 'max' => 150, 'max_size' => 1024 * 10, 'file_name' => 'tax_id_number_docs', 'accept' => ['application/pdf'], 'folder' => 'companies/tax-id-number/', ], [ 'title' => 'PKP', 'text_name' => 'taxable_enterprise', 'placeholder' => '*****', 'max' => 150, 'max_size' => 1024 * 10, 'file_name' => 'taxable_enterprise_docs', 'accept' => ['application/pdf'], 'folder' => 'companies/taxable-enterprise/', ], [ 'title' => 'SPT Tahunan', 'text_name' => 'annual_tax_return', 'placeholder' => '*****', 'max' => 150, 'max_size' => 1024 * 10, 'file_name' => 'annual_tax_return_docs', 'accept' => ['application/pdf'], 'folder' => 'companies/annual-tax-return/', ], [ 'title' => 'Suket Domisili', 'text_name' => 'domicile_certificate', 'placeholder' => '*****', 'max' => 150, 'max_size' => 1024 * 10, 'file_name' => 'domicile_certificate_docs', 'accept' => ['application/pdf'], 'folder' => 'companies/domicile-certificate/', ], [ 'title' => 'Profil Perusahaan', 'text_name' => 'profile', 'placeholder' => '*****', 'max' => 150, 'max_size' => 1024 * 10, 'file_name' => 'profile_docs', 'accept' => ['application/pdf'], 'folder' => 'companies/profile/', ], ]; return $schema ->components([ Section::make(fn () => CheerfulNotification::getMessage('Informasi Perusahaan 🏗️✨', 'Profil Perusahaan')) ->description(fn () => CheerfulNotification::getMessage('Kelola informasi perusahaan Anda biar tetap resmi dan kredibel! 🚀✨', 'Kelola informasi detail perusahaan Anda di sini.')) ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-building-office-2' : null) ->schema([ TextInput::make('name') ->label('Nama') ->placeholder('PT ABC Indonesia Abadi') ->autocomplete(false) ->autofocus() ->required() ->maxLength(100), TextInput::make('director_name') ->label('Nama Direktur') ->placeholder('John Doe') ->autocomplete(false) ->required() ->maxLength(150), TextInput::make('email') ->label('Alamat Surel') ->placeholder('johndoe@example.com') ->autocomplete(false) ->required() ->maxLength(254), TextInput::make('phone_number') ->label('Nomor Telepon') ->placeholder('08xx xxxx xxxx') ->autocomplete(false) ->maxLength(20) ->tel(), Textarea::make('address') ->label('Alamat') ->placeholder('Jl. Kebontoro, Jakarta Pusat') ->autocomplete(false) ->required() ->columnSpanFull(), ]) ->columns(2) ->columnSpan(2), Group::make() ->schema( array_merge( [ Section::make(fn () => CheerfulNotification::getMessage('NIK Direktur 👤✨', 'NIK Direktur')) ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-user' : null) ->schema([ TextInput::make('director_nik') ->hiddenLabel() ->placeholder('32***') ->autocomplete(false) ->required() ->maxLength(16) ->rule('digits:16') ->inputMode('numeric') ->regex('/^[0-9]+$/'), AdvancedFileUpload::make('director_nik_docs') ->hiddenLabel() ->disk(config('filesystems.default')) ->acceptedFileTypes(['image/*']) ->maxSize(1024 * 3) ->directory('companies/director-nik/'.now()->toDateString()) ->required(), ]), ], collect($documents) ->map(function (array $doc): Section { return Section::make(fn () => CheerfulNotification::getMessage($doc['title'].' 📄✨', $doc['title'])) ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) ->schema([ TextInput::make($doc['text_name']) ->hiddenLabel() ->placeholder($doc['placeholder']) ->autocomplete(false) ->required() ->maxLength($doc['max']), AdvancedFileUpload::make($doc['file_name']) ->hiddenLabel() ->disk(config('filesystems.default')) ->acceptedFileTypes($doc['accept']) ->maxSize($doc['max_size']) ->directory($doc['folder'].now()->toDateString()) ->required(), ]); })->toArray() ) ) ->columns(2), ]) ->statePath('data'); } public function save(): void { $this->mountAction('save'); } public function saveAction(): Action { $needsReview = $this->company?->verificationRequest?->status !== null; return Action::make('save') ->label('Simpan') ->modalHeading(fn () => CheerfulNotification::getMessage('Ajukan Perubahan Profil 📝✨', 'Ubah Profil Perusahaan')) ->modalDescription(fn () => CheerfulNotification::getMessage('Yakin ingin mengubah data perusahaan? Jelaskan alasannya supaya proses verifikasi makin lancar! 😊', 'Silakan masukkan alasan Anda melakukan perubahan profil perusahaan.')) ->modal($needsReview) ->schema( $needsReview ? [ Textarea::make('change_reason') ->label('Alasan') ->placeholder('Jelaskan alasan perubahan data ini...') ->autocomplete(false) ->autofocus() ->required(), ] : [] ) ->action(function (array $data = []) use ($needsReview) { try { $this->performSave($data, $needsReview); } catch (ValidationException $e) { $this->unmountAction(); throw $e; } }) ->modalWidth(Width::Large); } public function performSave(array $data, bool $needsReview = false): void { if ($needsReview) { $existingRequest = DataChangeRequest::where('entity_type', CompanyModel::class) ->where('entity_id', $this->company->id) ->where('status', DataChangeStatus::PENDING) ->exists(); if ($existingRequest) { CheerfulNotification::warning( CheerfulNotification::getMessage('Pengajuan Sudah Ada ⏳', 'Pengajuan Sedang Diproses'), CheerfulNotification::getMessage('Masih ada pengajuan perubahan data yang sedang menunggu verifikasi. Silakan tunggu hingga selesai diproses.', 'Pengajuan perubahan data perusahaan Anda masih dalam proses verifikasi oleh admin.') ) ->send(); return; } $state = $this->form->getState(); $data = array_merge($state, $data); $fieldLabels = CompanyModel::dataChangeEntryLabels(); $editableFields = [ 'name', 'email', 'phone_number', 'address', 'director_name', 'director_nik', 'deed_incorporation', 'trade_license', 'tax_id_number', 'taxable_enterprise', 'annual_tax_return', 'domicile_certificate', 'profile', ]; $changedFields = []; $oldData = []; foreach ($editableFields as $field) { if ($this->company->{$field} !== ($data[$field] ?? null)) { $label = $fieldLabels[$field] ?? $field; $changedFields[$label] = $data[$field] ?? null; $oldData[$label] = $this->company->{$field}; } } $documents = [ 'director_nik_docs', 'deed_incorporation_docs', 'trade_license_docs', 'tax_id_number_docs', 'taxable_enterprise_docs', 'annual_tax_return_docs', 'domicile_certificate_docs', 'profile_docs', ]; foreach ($documents as $field) { $newValue = $this->data[$field] ?? []; $oldValue = $this->originalDocuments[$field] ?? []; // Compare as arrays since file uploads are often arrays if (json_encode($newValue) !== json_encode($oldValue)) { $label = $fieldLabels[$field] ?? $field; $changedFields[$label] = $newValue; $oldData[$label] = $oldValue; } } if (empty($changedFields)) { CheerfulNotification::info( CheerfulNotification::getMessage('Belum ada yang berubah ✨', 'Tidak Ada Perubahan'), CheerfulNotification::getMessage('Ubah data terlebih dulu lalu simpan kembali 💪', 'Silakan ubah data terlebih dahulu sebelum melakukan penyimpanan.') ) ->send(); return; } $dataChangeRequest = DataChangeRequest::create([ 'user_id' => auth()->id(), 'entity_type' => CompanyModel::class, 'entity_id' => $this->company->id, 'old_data' => $oldData, 'new_data' => $changedFields, 'change_reason' => $data['change_reason'], ]); CheerfulNotification::success( CheerfulNotification::getMessage('Permohonan Berhasil 🎉', 'Permohonan Berhasil'), CheerfulNotification::getMessage('Data sudah dikirim dan sedang menunggu verifikasi admin ⏳', 'Permohonan perubahan data perusahaan telah berhasil dikirimkan.') ) ->send(); User::superAdmin() ->get() ->each(function ($admin) use ($dataChangeRequest): void { $admin->notify(new BroadcastNotification([ 'title' => CheerfulNotification::getMessage('Ada Pengajuan Perubahan Data ✨', 'Pengajuan Perubahan Perusahaan'), 'body' => CheerfulNotification::getMessage('Halooo Admin! 👋 '.auth()->user()->name.' baru saja mengajukan perubahan data. Yuk, cek detailnya dan lakukan verifikasi ya! 🚀', 'Pengguna '.auth()->user()->name.' telah mengajukan perubahan data perusahaan.'), 'action' => [ Action::make('view') ->label('Lihat') ->url(DataChangesResource::getUrl('view', ['record' => $dataChangeRequest->id])), ], ])); }); } else { $data = $this->form->getState(); $company = CompanyModel::updateOrCreate([ 'id' => $this->company?->id, ], [ 'user_id' => auth()->id(), 'name' => $data['name'], 'email' => $data['email'], 'phone_number' => $data['phone_number'], 'address' => $data['address'], 'director_name' => $data['director_name'], 'director_nik' => $data['director_nik'], 'deed_incorporation' => $data['deed_incorporation'], 'trade_license' => $data['trade_license'], 'tax_id_number' => $data['tax_id_number'], 'taxable_enterprise' => $data['taxable_enterprise'], 'annual_tax_return' => $data['annual_tax_return'], 'domicile_certificate' => $data['domicile_certificate'], 'profile' => $data['profile'], ]); $this->company = $company; $documents = [ 'director_nik_docs', 'deed_incorporation_docs', 'trade_license_docs', 'tax_id_number_docs', 'taxable_enterprise_docs', 'annual_tax_return_docs', 'domicile_certificate_docs', 'profile_docs', ]; foreach ($documents as $field) { if (empty($this->data[$field])) { continue; } foreach ((array) $this->data[$field] as $filePath) { $fullPath = Storage::disk(config('filesystems.default'))->path($filePath); if (! file_exists($fullPath)) { continue; } $company ->addMediaFromDisk($filePath, config('filesystems.default')) ->preservingOriginal() ->withCustomProperties([ 'feature' => 'companies', 'date' => now()->toDateString(), 'doc_type' => str($field) ->replace('_docs', '') ->slug('-'), ]) ->toMediaCollection('companies'); } } if ($this->company) { CheerfulNotification::update()->send(); } else { CheerfulNotification::create()->send(); } } } }