You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CompanyProfile.php 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. <?php
  2. namespace App\Filament\Company\Pages\Setting;
  3. use App\Enums\EntityType;
  4. use App\Models\Locale\{City, Country, State, Timezone};
  5. use App\Models\Setting\CompanyProfile as CompanyProfileModel;
  6. use Filament\Actions\{Action, ActionGroup};
  7. use Filament\Forms\Components\{Component, DatePicker, FileUpload, Group, Section, Select, TextInput};
  8. use Filament\Forms\{Form, Get, Set};
  9. use Filament\Notifications\Notification;
  10. use Filament\Pages\Concerns\InteractsWithFormActions;
  11. use Filament\Pages\Page;
  12. use Filament\Support\Exceptions\Halt;
  13. use Illuminate\Auth\Access\AuthorizationException;
  14. use Illuminate\Database\Eloquent\Model;
  15. use Illuminate\Support\Facades\Auth;
  16. use Livewire\Attributes\Locked;
  17. use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
  18. use function Filament\authorize;
  19. /**
  20. * @property Form $form
  21. */
  22. class CompanyProfile extends Page
  23. {
  24. use InteractsWithFormActions;
  25. protected static ?string $navigationIcon = 'heroicon-o-building-office-2';
  26. protected static ?string $navigationLabel = 'Company Profile';
  27. protected static ?string $navigationGroup = 'Settings';
  28. protected static ?string $slug = 'settings/company-profile';
  29. protected ?string $heading = 'Company Profile';
  30. protected static string $view = 'filament.company.pages.setting.company-profile';
  31. public ?array $data = [];
  32. #[Locked]
  33. public ?CompanyProfileModel $record = null;
  34. public function mount(): void
  35. {
  36. $this->record = CompanyProfileModel::firstOrNew([
  37. 'company_id' => auth()->user()->currentCompany->id,
  38. ]);
  39. abort_unless(static::canView($this->record), 404);
  40. $this->fillForm();
  41. }
  42. public function fillForm(): void
  43. {
  44. $data = $this->record->attributesToArray();
  45. $data = $this->mutateFormDataBeforeFill($data);
  46. $data['fiscal_year_start'] = now()->startOfYear()->toDateString();
  47. $data['fiscal_year_end'] = now()->endOfYear()->toDateString();
  48. $this->form->fill($data);
  49. }
  50. protected function mutateFormDataBeforeFill(array $data): array
  51. {
  52. return $data;
  53. }
  54. protected function mutateFormDataBeforeSave(array $data): array
  55. {
  56. return $data;
  57. }
  58. public function save(): void
  59. {
  60. try {
  61. $data = $this->form->getState();
  62. $data = $this->mutateFormDataBeforeSave($data);
  63. $this->handleRecordUpdate($this->record, $data);
  64. } catch (Halt $exception) {
  65. return;
  66. }
  67. $this->getSavedNotification()?->send();
  68. if ($redirectUrl = $this->getRedirectUrl()) {
  69. $this->redirect($redirectUrl);
  70. }
  71. }
  72. protected function getSavedNotification(): ?Notification
  73. {
  74. $title = $this->getSavedNotificationTitle();
  75. if (blank($title)) {
  76. return null;
  77. }
  78. return Notification::make()
  79. ->success()
  80. ->title($this->getSavedNotificationTitle());
  81. }
  82. protected function getSavedNotificationTitle(): ?string
  83. {
  84. return __('filament-panels::pages/tenancy/edit-tenant-profile.notifications.saved.title');
  85. }
  86. protected function getRedirectUrl(): ?string
  87. {
  88. return null;
  89. }
  90. public function form(Form $form): Form
  91. {
  92. return $form
  93. ->schema([
  94. $this->getIdentificationSection(),
  95. $this->getLocationDetailsSection(),
  96. $this->getLegalAndComplianceSection(),
  97. $this->getFiscalYearSection(),
  98. ])
  99. ->model($this->record)
  100. ->statePath('data')
  101. ->operation('edit');
  102. }
  103. protected function getIdentificationSection(): Component
  104. {
  105. return Section::make('Identification')
  106. ->schema([
  107. FileUpload::make('logo')
  108. ->label('Logo')
  109. ->disk('public')
  110. ->directory('logos/company')
  111. ->imageResizeMode('contain')
  112. ->imagePreviewHeight('250')
  113. ->imageCropAspectRatio('2:1')
  114. ->getUploadedFileNameForStorageUsing(
  115. static fn (TemporaryUploadedFile $file): string => (string) str($file->getClientOriginalName())
  116. ->prepend(Auth::user()->currentCompany->id . '_'),
  117. )
  118. ->openable()
  119. ->maxSize(2048)
  120. ->image()
  121. ->visibility('public')
  122. ->acceptedFileTypes(['image/png', 'image/jpeg']),
  123. Group::make()
  124. ->schema([
  125. TextInput::make('email')
  126. ->label('Email')
  127. ->email()
  128. ->maxLength(255)
  129. ->required(),
  130. TextInput::make('phone_number')
  131. ->label('Phone Number')
  132. ->tel()
  133. ->nullable(),
  134. ])->columns(1),
  135. ])->columns();
  136. }
  137. protected function getLocationDetailsSection(): Component
  138. {
  139. return Section::make('Location Details')
  140. ->schema([
  141. Select::make('country')
  142. ->label('Country')
  143. ->native(false)
  144. ->live()
  145. ->searchable()
  146. ->options(Country::getAvailableCountryOptions())
  147. ->afterStateUpdated(static function (Set $set) {
  148. $set('state_id', null);
  149. $set('timezone', null);
  150. $set('city_id', null);
  151. })
  152. ->required(),
  153. Select::make('state_id')
  154. ->label('State / Province')
  155. ->searchable()
  156. ->live()
  157. ->options(static fn (Get $get) => State::getStateOptions($get('country')))
  158. ->nullable(),
  159. Select::make('timezone')
  160. ->label('Timezone')
  161. ->searchable()
  162. ->options(static fn (Get $get) => Timezone::getTimezoneOptions($get('country')))
  163. ->nullable(),
  164. TextInput::make('address')
  165. ->label('Street Address')
  166. ->maxLength(255)
  167. ->nullable(),
  168. Select::make('city_id')
  169. ->label('City / Town')
  170. ->searchable()
  171. ->options(static fn (Get $get) => City::getCityOptions($get('country'), $get('state_id')))
  172. ->nullable(),
  173. TextInput::make('zip_code')
  174. ->label('Zip Code')
  175. ->maxLength(20)
  176. ->nullable(),
  177. ])->columns();
  178. }
  179. protected function getLegalAndComplianceSection(): Component
  180. {
  181. return Section::make('Legal & Compliance')
  182. ->schema([
  183. Select::make('entity_type')
  184. ->label('Entity Type')
  185. ->native(false)
  186. ->options(EntityType::class)
  187. ->required(),
  188. TextInput::make('tax_id')
  189. ->label('Tax ID')
  190. ->maxLength(50)
  191. ->nullable(),
  192. ])->columns();
  193. }
  194. protected function getFiscalYearSection(): Component
  195. {
  196. return Section::make('Fiscal Year')
  197. ->schema([
  198. DatePicker::make('fiscal_year_start')
  199. ->label('Start')
  200. ->native(false)
  201. ->seconds(false)
  202. ->rule('required'),
  203. DatePicker::make('fiscal_year_end')
  204. ->label('End')
  205. ->minDate(static fn (Get $get) => $get('fiscal_year_start'))
  206. ->native(false)
  207. ->seconds(false)
  208. ->rule('required'),
  209. ])->columns();
  210. }
  211. protected function handleRecordUpdate(CompanyProfileModel $record, array $data): CompanyProfileModel
  212. {
  213. $record->update($data);
  214. return $record;
  215. }
  216. /**
  217. * @return array<Action | ActionGroup>
  218. */
  219. protected function getFormActions(): array
  220. {
  221. return [
  222. $this->getSaveFormAction(),
  223. ];
  224. }
  225. protected function getSaveFormAction(): Action
  226. {
  227. return Action::make('save')
  228. ->label(__('filament-panels::pages/tenancy/edit-tenant-profile.form.actions.save.label'))
  229. ->submit('save')
  230. ->keyBindings(['mod+s']);
  231. }
  232. public static function canView(Model $record): bool
  233. {
  234. try {
  235. return authorize('update', $record)->allowed();
  236. } catch (AuthorizationException $exception) {
  237. return $exception->toResponse()->allowed();
  238. }
  239. }
  240. }