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.

CustomerResource.php 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. <?php
  2. namespace App\Filament\Resources;
  3. use App\Actions\OptionAction\CreateCurrency;
  4. use App\Filament\Resources\CustomerResource\Pages;
  5. use App\Filament\Resources\CustomerResource\RelationManagers;
  6. use App\Models\Setting\Currency;
  7. use App\Services\CurrencyService;
  8. use Wallo\FilamentSelectify\Components\ButtonGroup;
  9. use App\Models\Contact;
  10. use Filament\Forms;
  11. use Filament\Resources\Form;
  12. use Filament\Resources\Resource;
  13. use Filament\Resources\Table;
  14. use Filament\Tables;
  15. use Illuminate\Database\Eloquent\Builder;
  16. use Illuminate\Support\Facades\Auth;
  17. use Illuminate\Support\Facades\DB;
  18. class CustomerResource extends Resource
  19. {
  20. protected static ?string $model = Contact::class;
  21. protected static ?string $navigationIcon = 'heroicon-o-user-group';
  22. protected static ?string $navigationGroup = 'Sales';
  23. protected static ?string $navigationLabel = 'Customers';
  24. protected static ?string $modelLabel = 'customer';
  25. public static function getEloquentQuery(): Builder
  26. {
  27. return parent::getEloquentQuery()->customer();
  28. }
  29. public static function form(Form $form): Form
  30. {
  31. return $form
  32. ->schema([
  33. Forms\Components\Section::make('General')
  34. ->schema([
  35. Forms\Components\Grid::make(3)
  36. ->schema([
  37. ButtonGroup::make('entity')
  38. ->label('Entity')
  39. ->options([
  40. 'individual' => 'Individual',
  41. 'company' => 'Company',
  42. ])
  43. ->gridDirection('column')
  44. ->default('individual')
  45. ->columnSpan(1),
  46. Forms\Components\Grid::make()
  47. ->schema([
  48. Forms\Components\TextInput::make('name')
  49. ->label('Name')
  50. ->maxLength(100)
  51. ->required(),
  52. Forms\Components\TextInput::make('email')
  53. ->label('Email')
  54. ->email()
  55. ->nullable(),
  56. Forms\Components\TextInput::make('phone')
  57. ->label('Phone')
  58. ->tel()
  59. ->maxLength(20),
  60. Forms\Components\TextInput::make('website')
  61. ->label('Website')
  62. ->maxLength(100)
  63. ->url()
  64. ->nullable(),
  65. Forms\Components\TextInput::make('reference')
  66. ->label('Reference')
  67. ->maxLength(100)
  68. ->columnSpan(2)
  69. ->nullable(),
  70. ])->columnSpan(2),
  71. ]),
  72. ])->columns(),
  73. Forms\Components\Section::make('Billing')
  74. ->schema([
  75. Forms\Components\TextInput::make('tax_number')
  76. ->label('Tax Number')
  77. ->maxLength(100)
  78. ->nullable(),
  79. Forms\Components\Select::make('currency_code')
  80. ->label('Currency')
  81. ->relationship('currency', 'name')
  82. ->preload()
  83. ->default(Currency::getDefaultCurrency())
  84. ->searchable()
  85. ->reactive()
  86. ->required()
  87. ->createOptionForm([
  88. Forms\Components\Select::make('currency.code')
  89. ->label('Code')
  90. ->searchable()
  91. ->options(Currency::getCurrencyCodes())
  92. ->reactive()
  93. ->afterStateUpdated(static function (callable $set, $state) {
  94. if ($state === null) {
  95. return;
  96. }
  97. $code = $state;
  98. $currencyConfig = config("money.{$code}", []);
  99. $currencyService = app(CurrencyService::class);
  100. $defaultCurrency = Currency::getDefaultCurrency();
  101. $rate = 1;
  102. if ($defaultCurrency !== null) {
  103. $rate = $currencyService->getCachedExchangeRate($defaultCurrency, $code);
  104. }
  105. $set('currency.name', $currencyConfig['name'] ?? '');
  106. $set('currency.rate', $rate);
  107. })
  108. ->required(),
  109. Forms\Components\TextInput::make('currency.name')
  110. ->label('Name')
  111. ->maxLength(100)
  112. ->required(),
  113. Forms\Components\TextInput::make('currency.rate')
  114. ->label('Rate')
  115. ->numeric()
  116. ->required(),
  117. ])->createOptionAction(static function (Forms\Components\Actions\Action $action) {
  118. return $action
  119. ->label('Add Currency')
  120. ->modalHeading('Add Currency')
  121. ->modalButton('Add')
  122. ->action(static function (array $data) {
  123. return DB::transaction(static function () use ($data) {
  124. $code = $data['currency']['code'];
  125. $name = $data['currency']['name'];
  126. $rate = $data['currency']['rate'];
  127. return (new CreateCurrency())->create($code, $name, $rate);
  128. });
  129. });
  130. }),
  131. ])->columns(),
  132. Forms\Components\Section::make('Address')
  133. ->schema([
  134. Forms\Components\TextInput::make('address')
  135. ->label('Address')
  136. ->maxLength(100)
  137. ->columnSpanFull()
  138. ->nullable(),
  139. Forms\Components\Select::make('country')
  140. ->label('Country')
  141. ->searchable()
  142. ->reactive()
  143. ->options(Contact::getCountryOptions())
  144. ->nullable(),
  145. Forms\Components\Select::make('doesnt_exist') // TODO: Remove this when we have a better way to handle the searchable select when disabled
  146. ->label('Province/State')
  147. ->disabled()
  148. ->hidden(static fn (callable $get) => $get('country') !== null),
  149. Forms\Components\Select::make('state')
  150. ->label('Province/State')
  151. ->hidden(static fn (callable $get) => $get('country') === null)
  152. ->options(static function (callable $get) {
  153. $country = $get('country');
  154. if (! $country) {
  155. return [];
  156. }
  157. return Contact::getRegionOptions($country);
  158. })
  159. ->searchable()
  160. ->nullable(),
  161. Forms\Components\TextInput::make('city')
  162. ->label('Town/City')
  163. ->maxLength(100)
  164. ->nullable(),
  165. Forms\Components\TextInput::make('zip_code')
  166. ->label('Postal/Zip Code')
  167. ->maxLength(100)
  168. ->nullable(),
  169. ])->columns(),
  170. ]);
  171. }
  172. public static function table(Table $table): Table
  173. {
  174. return $table
  175. ->columns([
  176. Tables\Columns\TextColumn::make('name')
  177. ->label('Name')
  178. ->weight('semibold')
  179. ->description(static fn (Contact $record) => $record->tax_number ?: 'N/A')
  180. ->searchable()
  181. ->sortable(),
  182. Tables\Columns\TextColumn::make('email')
  183. ->label('Email')
  184. ->formatStateUsing(static fn (Contact $record) => $record->email ?: 'N/A')
  185. ->description(static fn (Contact $record) => $record->phone ?: 'N/A')
  186. ->searchable()
  187. ->sortable(),
  188. Tables\Columns\TextColumn::make('country')
  189. ->label('Country')
  190. ->searchable()
  191. ->formatStateUsing(static fn (Contact $record) => $record->country ?: 'N/A')
  192. ->description(static fn (Contact $record) => $record->currency->name ?: 'N/A')
  193. ->sortable(),
  194. ])
  195. ->filters([
  196. //
  197. ])
  198. ->actions([
  199. Tables\Actions\EditAction::make(),
  200. ])
  201. ->bulkActions([
  202. Tables\Actions\DeleteBulkAction::make(),
  203. ]);
  204. }
  205. public static function getRelations(): array
  206. {
  207. return [
  208. //
  209. ];
  210. }
  211. public static function getPages(): array
  212. {
  213. return [
  214. 'index' => Pages\ListCustomers::route('/'),
  215. 'create' => Pages\CreateCustomer::route('/create'),
  216. 'edit' => Pages\EditCustomer::route('/{record}/edit'),
  217. ];
  218. }
  219. }