Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

Appearance.php 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. <?php
  2. namespace App\Filament\Company\Pages\Setting;
  3. use App\Enums\Font;
  4. use App\Enums\MaxContentWidth;
  5. use App\Enums\ModalWidth;
  6. use App\Enums\PrimaryColor;
  7. use App\Enums\RecordsPerPage;
  8. use App\Enums\TableSortDirection;
  9. use App\Models\Setting\Appearance as AppearanceModel;
  10. use Filament\Actions\Action;
  11. use Filament\Actions\ActionGroup;
  12. use Filament\Forms\Components\Component;
  13. use Filament\Forms\Components\Section;
  14. use Filament\Forms\Components\Select;
  15. use Filament\Forms\Form;
  16. use Filament\Notifications\Notification;
  17. use Filament\Pages\Concerns\InteractsWithFormActions;
  18. use Filament\Pages\Page;
  19. use Filament\Support\Exceptions\Halt;
  20. use Illuminate\Auth\Access\AuthorizationException;
  21. use Illuminate\Database\Eloquent\Model;
  22. use Livewire\Attributes\Locked;
  23. use Wallo\FilamentSelectify\Components\ButtonGroup;
  24. use Wallo\FilamentSelectify\Components\ToggleButton;
  25. use function Filament\authorize;
  26. /**
  27. * @property Form $form
  28. */
  29. class Appearance extends Page
  30. {
  31. use InteractsWithFormActions;
  32. protected static ?string $navigationIcon = 'heroicon-o-paint-brush';
  33. protected static ?string $navigationLabel = 'Appearance';
  34. protected static ?string $navigationGroup = 'Settings';
  35. protected static ?string $slug = 'settings/appearance';
  36. protected ?string $heading = 'Appearance';
  37. protected static string $view = 'filament.company.pages.setting.appearance';
  38. public ?array $data = [];
  39. #[Locked]
  40. public ?AppearanceModel $record = null;
  41. public function mount(): void
  42. {
  43. $this->record = AppearanceModel::firstOrNew([
  44. 'company_id' => auth()->user()->currentCompany->id,
  45. ]);
  46. abort_unless(static::canView($this->record), 404);
  47. $this->fillForm();
  48. }
  49. public function fillForm(): void
  50. {
  51. $data = $this->record->attributesToArray();
  52. $data = $this->mutateFormDataBeforeFill($data);
  53. $this->form->fill($data);
  54. }
  55. protected function mutateFormDataBeforeFill(array $data): array
  56. {
  57. return $data;
  58. }
  59. protected function mutateFormDataBeforeSave(array $data): array
  60. {
  61. return $data;
  62. }
  63. public function save(): void
  64. {
  65. try {
  66. $data = $this->form->getState();
  67. $data = $this->mutateFormDataBeforeSave($data);
  68. $this->handleRecordUpdate($this->record, $data);
  69. } catch (Halt $exception) {
  70. return;
  71. }
  72. $this->getSavedNotification()?->send();
  73. if ($redirectUrl = $this->getRedirectUrl()) {
  74. $this->redirect($redirectUrl);
  75. }
  76. }
  77. protected function getSavedNotification(): ?Notification
  78. {
  79. $title = $this->getSavedNotificationTitle();
  80. if (blank($title)) {
  81. return null;
  82. }
  83. return Notification::make()
  84. ->success()
  85. ->title($this->getSavedNotificationTitle());
  86. }
  87. protected function getSavedNotificationTitle(): ?string
  88. {
  89. return __('filament-panels::pages/tenancy/edit-tenant-profile.notifications.saved.title');
  90. }
  91. protected function getRedirectUrl(): ?string
  92. {
  93. return null;
  94. }
  95. public function form(Form $form): Form
  96. {
  97. return $form
  98. ->schema([
  99. $this->getGeneralSection(),
  100. $this->getLayoutSection(),
  101. $this->getDataPresentationSection(),
  102. ])
  103. ->model($this->record)
  104. ->statePath('data')
  105. ->operation('edit');
  106. }
  107. protected function getGeneralSection(): Component
  108. {
  109. return Section::make('General')
  110. ->schema([
  111. Select::make('primary_color')
  112. ->label('Primary Color')
  113. ->native(false)
  114. ->allowHtml()
  115. ->selectablePlaceholder(false)
  116. ->rule('required')
  117. ->options(collect(PrimaryColor::cases())
  118. ->mapWithKeys(static fn ($case) => [
  119. $case->value => "<span class='flex items-center gap-x-4'>
  120. <span class='rounded-full w-4 h-4' style='background:rgb(" . $case->getColor()[600] . ")'></span>
  121. <span>" . str($case->value)->title() . "</span>
  122. </span>"
  123. ]),
  124. ),
  125. Select::make('font')
  126. ->label('Font')
  127. ->native(false)
  128. ->selectablePlaceholder(false)
  129. ->rule('required')
  130. ->allowHtml()
  131. ->options(collect(Font::cases())
  132. ->mapWithKeys(static fn ($case) => [
  133. $case->value => "<span style='font-family:{$case->getLabel()}'>{$case->getLabel()}</span>"
  134. ]),
  135. ),
  136. ])->columns();
  137. }
  138. protected function getLayoutSection(): Component
  139. {
  140. return Section::make('Layout')
  141. ->schema([
  142. Select::make('max_content_width')
  143. ->label('Max Content Width')
  144. ->native(false)
  145. ->selectablePlaceholder(false)
  146. ->rule('required')
  147. ->options(MaxContentWidth::class),
  148. Select::make('modal_width')
  149. ->label('Modal Width')
  150. ->native(false)
  151. ->selectablePlaceholder(false)
  152. ->rule('required')
  153. ->options(ModalWidth::class),
  154. ButtonGroup::make('has_top_navigation')
  155. ->label('Navigation Layout')
  156. ->boolean('Top Navigation', 'Side Navigation')
  157. ->rule('required'),
  158. ToggleButton::make('is_table_striped')
  159. ->label('Striped Tables')
  160. ->onLabel('Enabled')
  161. ->offLabel('Disabled')
  162. ->rule('required'),
  163. ])->columns();
  164. }
  165. protected function getDataPresentationSection(): Component
  166. {
  167. return Section::make('Data Presentation')
  168. ->schema([
  169. Select::make('table_sort_direction')
  170. ->label('Table Sort Direction')
  171. ->native(false)
  172. ->selectablePlaceholder(false)
  173. ->rule('required')
  174. ->options(TableSortDirection::class),
  175. Select::make('records_per_page')
  176. ->label('Records Per Page')
  177. ->native(false)
  178. ->selectablePlaceholder(false)
  179. ->rule('required')
  180. ->options(RecordsPerPage::class),
  181. ])->columns();
  182. }
  183. protected function handleRecordUpdate(AppearanceModel $record, array $data): AppearanceModel
  184. {
  185. $record_array = array_map('strval', $record->toArray());
  186. $data_array = array_map('strval', $data);
  187. $diff = array_diff_assoc($data_array, $record_array);
  188. $keysToWatch = [
  189. 'primary_color',
  190. 'max_content_width',
  191. 'has_top_navigation',
  192. 'font',
  193. ];
  194. foreach ($diff as $key => $value) {
  195. if (in_array($key, $keysToWatch, true)) {
  196. $this->dispatch('appearanceUpdated');
  197. }
  198. }
  199. // If the primary color or font has changed, we need to update the associated models accent_color column.
  200. if (array_key_exists('primary_color', $diff) || array_key_exists('font', $diff)) {
  201. $primaryColorToHex = PrimaryColor::from($data['primary_color'])->getHexCode();
  202. $font = Font::from($data['font'])->value;
  203. $this->record->company->defaultBill()->update([
  204. 'accent_color' => $primaryColorToHex,
  205. 'font' => $font,
  206. ]);
  207. $this->record->company->defaultInvoice()->update([
  208. 'accent_color' => $primaryColorToHex,
  209. 'font' => $font,
  210. ]);
  211. }
  212. $record->update($data);
  213. return $record;
  214. }
  215. /**
  216. * @return array<Action | ActionGroup>
  217. */
  218. protected function getFormActions(): array
  219. {
  220. return [
  221. $this->getSaveFormAction(),
  222. ];
  223. }
  224. protected function getSaveFormAction(): Action
  225. {
  226. return Action::make('save')
  227. ->label(__('filament-panels::pages/tenancy/edit-tenant-profile.form.actions.save.label'))
  228. ->submit('save')
  229. ->keyBindings(['mod+s']);
  230. }
  231. public static function canView(Model $record): bool
  232. {
  233. try {
  234. return authorize('update', $record)->allowed();
  235. } catch (AuthorizationException $exception) {
  236. return $exception->toResponse()->allowed();
  237. }
  238. }
  239. }