Andrew Wallo před 5 měsíci
rodič
revize
d5e4f72569

+ 6
- 2
app/Filament/Company/Resources/Purchases/BillResource.php Zobrazit soubor

@@ -16,6 +16,7 @@ use App\Filament\Forms\Components\CreateAdjustmentSelect;
16 16
 use App\Filament\Forms\Components\CreateCurrencySelect;
17 17
 use App\Filament\Forms\Components\CreateOfferingSelect;
18 18
 use App\Filament\Forms\Components\CreateVendorSelect;
19
+use App\Filament\Forms\Components\CustomTableRepeater;
19 20
 use App\Filament\Forms\Components\DocumentTotals;
20 21
 use App\Filament\Tables\Actions\ReplicateBulkAction;
21 22
 use App\Filament\Tables\Columns;
@@ -29,7 +30,6 @@ use App\Models\Common\Vendor;
29 30
 use App\Utilities\Currency\CurrencyAccessor;
30 31
 use App\Utilities\Currency\CurrencyConverter;
31 32
 use App\Utilities\RateCalculator;
32
-use Awcodes\TableRepeater\Components\TableRepeater;
33 33
 use Awcodes\TableRepeater\Header;
34 34
 use Closure;
35 35
 use Filament\Forms;
@@ -178,10 +178,14 @@ class BillResource extends Resource
178 178
                                     ->live(),
179 179
                             ])->grow(true),
180 180
                         ])->from('md'),
181
-                        TableRepeater::make('lineItems')
181
+                        CustomTableRepeater::make('lineItems')
182
+                            ->hiddenLabel()
182 183
                             ->relationship()
183 184
                             ->saveRelationshipsUsing(null)
184 185
                             ->dehydrated(true)
186
+                            ->reorderable()
187
+                            ->reorderAtStart()
188
+                            ->cloneable()
185 189
                             ->headers(function (Forms\Get $get) use ($settings) {
186 190
                                 $hasDiscounts = DocumentDiscountMethod::parse($get('discount_method'))->isPerLineItem();
187 191
 

+ 2
- 2
app/Filament/Company/Resources/Sales/ClientResource.php Zobrazit soubor

@@ -252,7 +252,7 @@ class ClientResource extends Resource
252 252
                 Tables\Columns\TextColumn::make('name')
253 253
                     ->searchable()
254 254
                     ->sortable()
255
-                    ->description(static fn (Client $client) => $client->primaryContact->full_name),
255
+                    ->description(static fn (Client $client) => $client->primaryContact?->full_name),
256 256
                 Tables\Columns\TextColumn::make('primaryContact.email')
257 257
                     ->label('Email')
258 258
                     ->searchable()
@@ -260,7 +260,7 @@ class ClientResource extends Resource
260 260
                 Tables\Columns\TextColumn::make('primaryContact.phones')
261 261
                     ->label('Phone')
262 262
                     ->toggleable()
263
-                    ->state(static fn (Client $client) => $client->primaryContact->first_available_phone),
263
+                    ->state(static fn (Client $client) => $client->primaryContact?->first_available_phone),
264 264
                 Tables\Columns\TextColumn::make('billingAddress.address_string')
265 265
                     ->label('Billing address')
266 266
                     ->searchable()

+ 6
- 2
app/Filament/Company/Resources/Sales/EstimateResource.php Zobrazit soubor

@@ -16,6 +16,7 @@ use App\Filament\Forms\Components\CreateAdjustmentSelect;
16 16
 use App\Filament\Forms\Components\CreateClientSelect;
17 17
 use App\Filament\Forms\Components\CreateCurrencySelect;
18 18
 use App\Filament\Forms\Components\CreateOfferingSelect;
19
+use App\Filament\Forms\Components\CustomTableRepeater;
19 20
 use App\Filament\Forms\Components\DocumentFooterSection;
20 21
 use App\Filament\Forms\Components\DocumentHeaderSection;
21 22
 use App\Filament\Forms\Components\DocumentTotals;
@@ -30,7 +31,6 @@ use App\Models\Common\Offering;
30 31
 use App\Utilities\Currency\CurrencyAccessor;
31 32
 use App\Utilities\Currency\CurrencyConverter;
32 33
 use App\Utilities\RateCalculator;
33
-use Awcodes\TableRepeater\Components\TableRepeater;
34 34
 use Awcodes\TableRepeater\Header;
35 35
 use Filament\Forms;
36 36
 use Filament\Forms\Form;
@@ -176,10 +176,14 @@ class EstimateResource extends Resource
176 176
                                     ->live(),
177 177
                             ])->grow(true),
178 178
                         ])->from('md'),
179
-                        TableRepeater::make('lineItems')
179
+                        CustomTableRepeater::make('lineItems')
180
+                            ->hiddenLabel()
180 181
                             ->relationship()
181 182
                             ->saveRelationshipsUsing(null)
182 183
                             ->dehydrated(true)
184
+                            ->reorderable()
185
+                            ->reorderAtStart()
186
+                            ->cloneable()
183 187
                             ->headers(function (Forms\Get $get) use ($settings) {
184 188
                                 $hasDiscounts = DocumentDiscountMethod::parse($get('discount_method'))->isPerLineItem();
185 189
 

+ 98
- 80
app/Filament/Company/Resources/Sales/InvoiceResource.php Zobrazit soubor

@@ -18,6 +18,7 @@ use App\Filament\Forms\Components\CreateAdjustmentSelect;
18 18
 use App\Filament\Forms\Components\CreateClientSelect;
19 19
 use App\Filament\Forms\Components\CreateCurrencySelect;
20 20
 use App\Filament\Forms\Components\CreateOfferingSelect;
21
+use App\Filament\Forms\Components\CustomTableRepeater;
21 22
 use App\Filament\Forms\Components\DocumentFooterSection;
22 23
 use App\Filament\Forms\Components\DocumentHeaderSection;
23 24
 use App\Filament\Forms\Components\DocumentTotals;
@@ -33,7 +34,6 @@ use App\Models\Common\Offering;
33 34
 use App\Utilities\Currency\CurrencyAccessor;
34 35
 use App\Utilities\Currency\CurrencyConverter;
35 36
 use App\Utilities\RateCalculator;
36
-use Awcodes\TableRepeater\Components\TableRepeater;
37 37
 use Awcodes\TableRepeater\Header;
38 38
 use Closure;
39 39
 use Filament\Forms;
@@ -177,7 +177,7 @@ class InvoiceResource extends Resource
177 177
                                 Forms\Components\Select::make('discount_method')
178 178
                                     ->label('Discount method')
179 179
                                     ->options(DocumentDiscountMethod::class)
180
-                                    ->selectablePlaceholder(false)
180
+                                    ->softRequired()
181 181
                                     ->default($settings->discount_method)
182 182
                                     ->afterStateUpdated(function ($state, Forms\Set $set) {
183 183
                                         $discountMethod = DocumentDiscountMethod::parse($state);
@@ -189,28 +189,31 @@ class InvoiceResource extends Resource
189 189
                                     ->live(),
190 190
                             ])->grow(true),
191 191
                         ])->from('md'),
192
-                        TableRepeater::make('lineItems')
192
+                        CustomTableRepeater::make('lineItems')
193
+                            ->hiddenLabel()
193 194
                             ->relationship()
194 195
                             ->saveRelationshipsUsing(null)
195 196
                             ->dehydrated(true)
197
+                            ->reorderable()
198
+                            ->reorderAtStart()
199
+                            ->cloneable()
200
+                            ->addActionLabel('Add an item')
196 201
                             ->headers(function (Forms\Get $get) use ($settings) {
197 202
                                 $hasDiscounts = DocumentDiscountMethod::parse($get('discount_method'))->isPerLineItem();
198 203
 
199 204
                                 $headers = [
200 205
                                     Header::make($settings->resolveColumnLabel('item_name', 'Items'))
201
-                                        ->width($hasDiscounts ? '15%' : '20%'),
202
-                                    Header::make('Description')
203
-                                        ->width($hasDiscounts ? '15%' : '20%'),
206
+                                        ->width('30%'),
204 207
                                     Header::make($settings->resolveColumnLabel('unit_name', 'Quantity'))
205 208
                                         ->width('10%'),
206 209
                                     Header::make($settings->resolveColumnLabel('price_name', 'Price'))
207 210
                                         ->width('10%'),
208
-                                    Header::make('Taxes')
209
-                                        ->width($hasDiscounts ? '20%' : '30%'),
210 211
                                 ];
211 212
 
212 213
                                 if ($hasDiscounts) {
213
-                                    $headers[] = Header::make('Discounts')->width('20%');
214
+                                    $headers[] = Header::make('Adjustments')->width('30%');
215
+                                } else {
216
+                                    $headers[] = Header::make('Taxes')->width('30%');
214 217
                                 }
215 218
 
216 219
                                 $headers[] = Header::make($settings->resolveColumnLabel('amount_name', 'Amount'))
@@ -220,61 +223,68 @@ class InvoiceResource extends Resource
220 223
                                 return $headers;
221 224
                             })
222 225
                             ->schema([
223
-                                CreateOfferingSelect::make('offering_id')
224
-                                    ->label('Item')
225
-                                    ->required()
226
-                                    ->live()
227
-                                    ->sellable()
228
-                                    ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state, ?DocumentLineItem $record) {
229
-                                        $offeringId = $state;
230
-                                        $discountMethod = DocumentDiscountMethod::parse($get('../../discount_method'));
231
-                                        $isPerLineItem = $discountMethod->isPerLineItem();
226
+                                Forms\Components\Group::make([
227
+                                    CreateOfferingSelect::make('offering_id')
228
+                                        ->label('Item')
229
+                                        ->hiddenLabel()
230
+                                        ->placeholder('Select item')
231
+                                        ->required()
232
+                                        ->live()
233
+                                        ->inlineSuffix()
234
+                                        ->sellable()
235
+                                        ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state, ?DocumentLineItem $record) {
236
+                                            $offeringId = $state;
237
+                                            $discountMethod = DocumentDiscountMethod::parse($get('../../discount_method'));
238
+                                            $isPerLineItem = $discountMethod->isPerLineItem();
239
+
240
+                                            $existingTaxIds = [];
241
+                                            $existingDiscountIds = [];
242
+
243
+                                            if ($record) {
244
+                                                $existingTaxIds = $record->salesTaxes()->pluck('adjustments.id')->toArray();
245
+                                                if ($isPerLineItem) {
246
+                                                    $existingDiscountIds = $record->salesDiscounts()->pluck('adjustments.id')->toArray();
247
+                                                }
248
+                                            }
232 249
 
233
-                                        $existingTaxIds = [];
234
-                                        $existingDiscountIds = [];
250
+                                            $with = [
251
+                                                'salesTaxes' => static function ($query) use ($existingTaxIds) {
252
+                                                    $query->where(static function ($query) use ($existingTaxIds) {
253
+                                                        $query->where('status', AdjustmentStatus::Active)
254
+                                                            ->orWhereIn('adjustments.id', $existingTaxIds);
255
+                                                    });
256
+                                                },
257
+                                            ];
235 258
 
236
-                                        if ($record) {
237
-                                            $existingTaxIds = $record->salesTaxes()->pluck('adjustments.id')->toArray();
238 259
                                             if ($isPerLineItem) {
239
-                                                $existingDiscountIds = $record->salesDiscounts()->pluck('adjustments.id')->toArray();
260
+                                                $with['salesDiscounts'] = static function ($query) use ($existingDiscountIds) {
261
+                                                    $query->where(static function ($query) use ($existingDiscountIds) {
262
+                                                        $query->where('status', AdjustmentStatus::Active)
263
+                                                            ->orWhereIn('adjustments.id', $existingDiscountIds);
264
+                                                    });
265
+                                                };
240 266
                                             }
241
-                                        }
242 267
 
243
-                                        $with = [
244
-                                            'salesTaxes' => static function ($query) use ($existingTaxIds) {
245
-                                                $query->where(static function ($query) use ($existingTaxIds) {
246
-                                                    $query->where('status', AdjustmentStatus::Active)
247
-                                                        ->orWhereIn('adjustments.id', $existingTaxIds);
248
-                                                });
249
-                                            },
250
-                                        ];
251
-
252
-                                        if ($isPerLineItem) {
253
-                                            $with['salesDiscounts'] = static function ($query) use ($existingDiscountIds) {
254
-                                                $query->where(static function ($query) use ($existingDiscountIds) {
255
-                                                    $query->where('status', AdjustmentStatus::Active)
256
-                                                        ->orWhereIn('adjustments.id', $existingDiscountIds);
257
-                                                });
258
-                                            };
259
-                                        }
268
+                                            $offeringRecord = Offering::with($with)->find($offeringId);
260 269
 
261
-                                        $offeringRecord = Offering::with($with)->find($offeringId);
262
-
263
-                                        if (! $offeringRecord) {
264
-                                            return;
265
-                                        }
270
+                                            if (! $offeringRecord) {
271
+                                                return;
272
+                                            }
266 273
 
267
-                                        $unitPrice = CurrencyConverter::convertToFloat($offeringRecord->price, $get('../../currency_code') ?? CurrencyAccessor::getDefaultCurrency());
274
+                                            $unitPrice = CurrencyConverter::convertToFloat($offeringRecord->price, $get('../../currency_code') ?? CurrencyAccessor::getDefaultCurrency());
268 275
 
269
-                                        $set('description', $offeringRecord->description);
270
-                                        $set('unit_price', $unitPrice);
271
-                                        $set('salesTaxes', $offeringRecord->salesTaxes->pluck('id')->toArray());
276
+                                            $set('description', $offeringRecord->description);
277
+                                            $set('unit_price', $unitPrice);
278
+                                            $set('salesTaxes', $offeringRecord->salesTaxes->pluck('id')->toArray());
272 279
 
273
-                                        if ($isPerLineItem) {
274
-                                            $set('salesDiscounts', $offeringRecord->salesDiscounts->pluck('id')->toArray());
275
-                                        }
276
-                                    }),
277
-                                Forms\Components\TextInput::make('description'),
280
+                                            if ($isPerLineItem) {
281
+                                                $set('salesDiscounts', $offeringRecord->salesDiscounts->pluck('id')->toArray());
282
+                                            }
283
+                                        }),
284
+                                    Forms\Components\TextInput::make('description')
285
+                                        ->placeholder('Enter item description')
286
+                                        ->hiddenLabel(),
287
+                                ])->columnSpan(1),
278 288
                                 Forms\Components\TextInput::make('quantity')
279 289
                                     ->required()
280 290
                                     ->numeric()
@@ -287,32 +297,40 @@ class InvoiceResource extends Resource
287 297
                                     ->live()
288 298
                                     ->maxValue(9999999999.99)
289 299
                                     ->default(0),
290
-                                CreateAdjustmentSelect::make('salesTaxes')
291
-                                    ->label('Taxes')
292
-                                    ->category(AdjustmentCategory::Tax)
293
-                                    ->type(AdjustmentType::Sales)
294
-                                    ->adjustmentsRelationship('salesTaxes')
295
-                                    ->saveRelationshipsUsing(null)
296
-                                    ->dehydrated(true)
297
-                                    ->preload()
298
-                                    ->multiple()
299
-                                    ->live()
300
-                                    ->searchable(),
301
-                                CreateAdjustmentSelect::make('salesDiscounts')
302
-                                    ->label('Discounts')
303
-                                    ->category(AdjustmentCategory::Discount)
304
-                                    ->type(AdjustmentType::Sales)
305
-                                    ->adjustmentsRelationship('salesDiscounts')
306
-                                    ->saveRelationshipsUsing(null)
307
-                                    ->dehydrated(true)
308
-                                    ->multiple()
309
-                                    ->live()
310
-                                    ->hidden(function (Forms\Get $get) {
311
-                                        $discountMethod = DocumentDiscountMethod::parse($get('../../discount_method'));
300
+                                Forms\Components\Group::make([
301
+                                    CreateAdjustmentSelect::make('salesTaxes')
302
+                                        ->label('Taxes')
303
+                                        ->hiddenLabel()
304
+                                        ->placeholder('Select taxes')
305
+                                        ->category(AdjustmentCategory::Tax)
306
+                                        ->type(AdjustmentType::Sales)
307
+                                        ->adjustmentsRelationship('salesTaxes')
308
+                                        ->saveRelationshipsUsing(null)
309
+                                        ->dehydrated(true)
310
+                                        ->inlineSuffix()
311
+                                        ->preload()
312
+                                        ->multiple()
313
+                                        ->live()
314
+                                        ->searchable(),
315
+                                    CreateAdjustmentSelect::make('salesDiscounts')
316
+                                        ->label('Discounts')
317
+                                        ->hiddenLabel()
318
+                                        ->placeholder('Select discounts')
319
+                                        ->category(AdjustmentCategory::Discount)
320
+                                        ->type(AdjustmentType::Sales)
321
+                                        ->adjustmentsRelationship('salesDiscounts')
322
+                                        ->saveRelationshipsUsing(null)
323
+                                        ->dehydrated(true)
324
+                                        ->inlineSuffix()
325
+                                        ->multiple()
326
+                                        ->live()
327
+                                        ->hidden(function (Forms\Get $get) {
328
+                                            $discountMethod = DocumentDiscountMethod::parse($get('../../discount_method'));
312 329
 
313
-                                        return $discountMethod->isPerDocument();
314
-                                    })
315
-                                    ->searchable(),
330
+                                            return $discountMethod->isPerDocument();
331
+                                        })
332
+                                        ->searchable(),
333
+                                ])->columnSpan(1),
316 334
                                 Forms\Components\Placeholder::make('total')
317 335
                                     ->hiddenLabel()
318 336
                                     ->extraAttributes(['class' => 'text-left sm:text-right'])

+ 6
- 2
app/Filament/Company/Resources/Sales/RecurringInvoiceResource.php Zobrazit soubor

@@ -15,6 +15,7 @@ use App\Filament\Forms\Components\CreateAdjustmentSelect;
15 15
 use App\Filament\Forms\Components\CreateClientSelect;
16 16
 use App\Filament\Forms\Components\CreateCurrencySelect;
17 17
 use App\Filament\Forms\Components\CreateOfferingSelect;
18
+use App\Filament\Forms\Components\CustomTableRepeater;
18 19
 use App\Filament\Forms\Components\DocumentFooterSection;
19 20
 use App\Filament\Forms\Components\DocumentHeaderSection;
20 21
 use App\Filament\Forms\Components\DocumentTotals;
@@ -27,7 +28,6 @@ use App\Models\Common\Offering;
27 28
 use App\Utilities\Currency\CurrencyAccessor;
28 29
 use App\Utilities\Currency\CurrencyConverter;
29 30
 use App\Utilities\RateCalculator;
30
-use Awcodes\TableRepeater\Components\TableRepeater;
31 31
 use Awcodes\TableRepeater\Header;
32 32
 use Filament\Forms;
33 33
 use Filament\Forms\Form;
@@ -102,10 +102,14 @@ class RecurringInvoiceResource extends Resource
102 102
                                     ->live(),
103 103
                             ])->grow(true),
104 104
                         ])->from('md'),
105
-                        TableRepeater::make('lineItems')
105
+                        CustomTableRepeater::make('lineItems')
106
+                            ->hiddenLabel()
106 107
                             ->relationship()
107 108
                             ->saveRelationshipsUsing(null)
108 109
                             ->dehydrated(true)
110
+                            ->reorderable()
111
+                            ->reorderAtStart()
112
+                            ->cloneable()
109 113
                             ->headers(function (Forms\Get $get) use ($settings) {
110 114
                                 $hasDiscounts = DocumentDiscountMethod::parse($get('discount_method'))->isPerLineItem();
111 115
 

+ 30
- 2
app/Filament/Forms/Components/CustomTableRepeater.php Zobrazit soubor

@@ -4,10 +4,13 @@ namespace App\Filament\Forms\Components;
4 4
 
5 5
 use Awcodes\TableRepeater\Components\TableRepeater;
6 6
 use Closure;
7
+use Filament\Forms\Components\Actions\Action;
7 8
 
8 9
 class CustomTableRepeater extends TableRepeater
9 10
 {
10
-    protected bool | Closure | null $spreadsheet = null;
11
+    protected bool | Closure $spreadsheet = false;
12
+
13
+    protected bool | Closure $reorderAtStart = false;
11 14
 
12 15
     public function spreadsheet(bool | Closure $condition = true): static
13 16
     {
@@ -18,7 +21,19 @@ class CustomTableRepeater extends TableRepeater
18 21
 
19 22
     public function isSpreadsheet(): bool
20 23
     {
21
-        return $this->evaluate($this->spreadsheet) ?? false;
24
+        return (bool) $this->evaluate($this->spreadsheet);
25
+    }
26
+
27
+    public function reorderAtStart(bool | Closure $condition = true): static
28
+    {
29
+        $this->reorderAtStart = $condition;
30
+
31
+        return $this;
32
+    }
33
+
34
+    public function isReorderAtStart(): bool
35
+    {
36
+        return $this->evaluate($this->reorderAtStart) && $this->isReorderable();
22 37
     }
23 38
 
24 39
     protected function setUp(): void
@@ -34,5 +49,18 @@ class CustomTableRepeater extends TableRepeater
34 49
 
35 50
             return $attributes;
36 51
         });
52
+
53
+        $this->reorderAction(function (Action $action) {
54
+            if ($this->isReorderAtStart()) {
55
+                $action->icon('heroicon-m-bars-3');
56
+            }
57
+
58
+            return $action;
59
+        });
60
+    }
61
+
62
+    public function getView(): string
63
+    {
64
+        return 'filament.forms.components.custom-table-repeater';
37 65
     }
38 66
 }

+ 1
- 10
app/Providers/Filament/CompanyPanelProvider.php Zobrazit soubor

@@ -298,18 +298,9 @@ class CompanyPanelProvider extends PanelProvider
298 298
     protected function configureSelect(): void
299 299
     {
300 300
         Select::configureUsing(function (Select $select): void {
301
-            $isSelectable = fn (): bool => ! $this->hasRequiredRule($select);
302
-
303 301
             $select
304 302
                 ->native(false)
305
-                ->selectablePlaceholder($isSelectable);
303
+                ->selectablePlaceholder(fn (Select $component) => ! $component->isRequired());
306 304
         });
307 305
     }
308
-
309
-    protected function hasRequiredRule(Select $component): bool
310
-    {
311
-        $rules = $component->getValidationRules();
312
-
313
-        return in_array('required', $rules, true);
314
-    }
315 306
 }

+ 20
- 47
resources/css/filament/company/form-fields.css Zobrazit soubor

@@ -44,39 +44,43 @@
44 44
 /* Table Repeater Styles */
45 45
 :not(.is-spreadsheet) {
46 46
     .table-repeater-container {
47
-        @apply rounded-none ring-0;
48
-    }
49
-
50
-    .table-repeater-component {
51
-        @apply space-y-10;
52
-    }
53
-
54
-    .table-repeater-component ul {
55
-        @apply justify-start;
47
+        @apply border border-gray-300 dark:border-gray-600 rounded-sm;
56 48
     }
57 49
 
58 50
     .table-repeater-row {
59
-        @apply divide-x-0 !important;
51
+        @apply hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors;
60 52
     }
61 53
 
62 54
     .table-repeater-column {
63
-        @apply py-2 !important;
55
+        @apply p-3 text-sm text-gray-900 dark:text-gray-100;
64 56
     }
65 57
 
66 58
     .table-repeater-header {
67
-        @apply rounded-t-none !important;
59
+        @apply bg-gray-50 dark:bg-gray-700/50 border-b border-gray-300 dark:border-gray-600;
68 60
     }
69 61
 
70
-    .table-repeater-rows-wrapper {
71
-        @apply divide-gray-300 last:border-b last:border-gray-300 dark:divide-white/20 dark:last:border-white/20;
62
+    .table-repeater-row {
63
+        @apply divide-x-0 !important;
72 64
     }
73 65
 
74 66
     .table-repeater-header tr {
75
-        @apply divide-x-0 text-base sm:text-sm sm:leading-6 !important;
67
+        @apply divide-x-0 text-sm;
76 68
     }
77 69
 
78 70
     .table-repeater-header-column {
79
-        @apply ps-3 pe-3 font-semibold bg-gray-200 dark:bg-gray-800 rounded-none !important;
71
+        @apply p-3 font-semibold bg-gray-50 dark:bg-gray-700/50;
72
+    }
73
+
74
+    /* Chrome, Safari, Edge, Opera */
75
+    input::-webkit-outer-spin-button,
76
+    input::-webkit-inner-spin-button {
77
+        -webkit-appearance: none;
78
+        margin: 0;
79
+    }
80
+
81
+    /* Firefox */
82
+    input[type=number] {
83
+        -moz-appearance: textfield;
80 84
     }
81 85
 }
82 86
 
@@ -165,37 +169,6 @@
165 169
     }
166 170
 }
167 171
 
168
-/* Responsive behavior */
169
-@media (max-width: theme('screens.sm')) {
170
-    .table-repeater-component.break-point-sm .table-repeater-container {
171
-        overflow-x: visible;
172
-    }
173
-}
174
-
175
-@media (max-width: theme('screens.md')) {
176
-    .table-repeater-component.break-point-md .table-repeater-container {
177
-        overflow-x: visible;
178
-    }
179
-}
180
-
181
-@media (max-width: theme('screens.lg')) {
182
-    .table-repeater-component.break-point-lg .table-repeater-container {
183
-        overflow-x: visible;
184
-    }
185
-}
186
-
187
-@media (max-width: theme('screens.xl')) {
188
-    .table-repeater-component.break-point-xl .table-repeater-container {
189
-        overflow-x: visible;
190
-    }
191
-}
192
-
193
-@media (max-width: theme('screens.2xl')) {
194
-    .table-repeater-component.break-point-2xl .table-repeater-container {
195
-        overflow-x: visible;
196
-    }
197
-}
198
-
199 172
 .is-spreadsheet .table-repeater-column .fi-input-wrp-suffix {
200 173
     padding-right: 0 !important;
201 174
 }

+ 251
- 0
resources/views/filament/forms/components/custom-table-repeater.blade.php Zobrazit soubor

@@ -0,0 +1,251 @@
1
+@php
2
+    use Filament\Forms\Components\Actions\Action;
3
+    use Filament\Support\Enums\Alignment;
4
+    use Filament\Support\Enums\MaxWidth;
5
+
6
+    $containers = $getChildComponentContainers();
7
+
8
+    $addAction = $getAction($getAddActionName());
9
+    $cloneAction = $getAction($getCloneActionName());
10
+    $deleteAction = $getAction($getDeleteActionName());
11
+    $moveDownAction = $getAction($getMoveDownActionName());
12
+    $moveUpAction = $getAction($getMoveUpActionName());
13
+    $reorderAction = $getAction($getReorderActionName());
14
+    $isReorderableWithButtons = $isReorderableWithButtons();
15
+    $extraItemActions = $getExtraItemActions();
16
+    $extraActions = $getExtraActions();
17
+    $visibleExtraItemActions = [];
18
+    $visibleExtraActions = [];
19
+
20
+    $headers = $getHeaders();
21
+    $renderHeader = $shouldRenderHeader();
22
+    $stackAt = $getStackAt();
23
+    $hasContainers = count($containers) > 0;
24
+    $emptyLabel = $getEmptyLabel();
25
+    $streamlined = $isStreamlined();
26
+
27
+    $reorderAtStart = $isReorderAtStart();
28
+
29
+    $statePath = $getStatePath();
30
+
31
+    foreach ($extraActions as $extraAction) {
32
+        $visibleExtraActions = array_filter(
33
+            $extraActions,
34
+            fn (Action $action): bool => $action->isVisible(),
35
+        );
36
+    }
37
+
38
+    foreach ($extraItemActions as $extraItemAction) {
39
+        $visibleExtraItemActions = array_filter(
40
+            $extraItemActions,
41
+            fn (Action $action): bool => $action->isVisible(),
42
+        );
43
+    }
44
+
45
+    $hasActions = $reorderAction->isVisible()
46
+        || $cloneAction->isVisible()
47
+        || $deleteAction->isVisible()
48
+        || $moveUpAction->isVisible()
49
+        || $moveDownAction->isVisible()
50
+        || filled($visibleExtraItemActions);
51
+@endphp
52
+
53
+<x-dynamic-component :component="$getFieldWrapperView()" :field="$field">
54
+    <div
55
+        x-data="{}"
56
+        {{ $attributes->merge($getExtraAttributes())->class([
57
+            'table-repeater-component space-y-6 relative',
58
+            'streamlined' => $streamlined,
59
+            match ($stackAt) {
60
+                'sm', MaxWidth::Small => 'break-point-sm',
61
+                'lg', MaxWidth::Large => 'break-point-lg',
62
+                'xl', MaxWidth::ExtraLarge => 'break-point-xl',
63
+                '2xl', MaxWidth::TwoExtraLarge => 'break-point-2xl',
64
+                default => 'break-point-md',
65
+            }
66
+        ]) }}
67
+    >
68
+        @if (count($containers) || $emptyLabel !== false)
69
+            <div class="table-repeater-container rounded-xl relative ring-1 ring-gray-950/5 dark:ring-white/20">
70
+                <table class="w-full">
71
+                    <thead @class([
72
+                        'table-repeater-header-hidden sr-only' => ! $renderHeader,
73
+                        'table-repeater-header rounded-t-xl overflow-hidden border-b border-gray-950/5 dark:border-white/20' => $renderHeader,
74
+                    ])>
75
+                    <tr class="text-xs md:divide-x rtl:divide-x-reverse md:divide-gray-950/5 dark:md:divide-white/20">
76
+                        {{-- Move actions column to start if reorderAtStart is true --}}
77
+                        @if ($hasActions && count($containers) && $reorderAtStart)
78
+                            <th class="table-repeater-header-column w-px first:rounded-tl-xl rtl:first:rounded-tr-xl rtl:first:rounded-tl-none p-2 bg-gray-100 dark:bg-gray-900/60">
79
+                                <span class="sr-only">
80
+                                    {{ trans('table-repeater::components.repeater.row_actions.label') }}
81
+                                </span>
82
+                            </th>
83
+                        @endif
84
+
85
+                        @foreach ($headers as $key => $header)
86
+                            <th
87
+                                @class([
88
+                                    'table-repeater-header-column p-2 font-medium first:rounded-tl-xl rtl:first:rounded-tr-xl rtl:first:rounded-tl-none last:rounded-tr-xl bg-gray-100 dark:text-gray-300 dark:bg-gray-900/60',
89
+                                    match($header->getAlignment()) {
90
+                                      'center', Alignment::Center => 'text-center',
91
+                                      'right', 'end', Alignment::Right, Alignment::End => 'text-end',
92
+                                      default => 'text-start'
93
+                                    }
94
+                                ])
95
+                                style="width: {{ $header->getWidth() }}"
96
+                            >
97
+                                {{ $header->getLabel() }}
98
+                                @if ($header->isRequired())
99
+                                    <span class="whitespace-nowrap">
100
+                                        <sup class="font-medium text-danger-700 dark:text-danger-400">*</sup>
101
+                                    </span>
102
+                                @endif
103
+                            </th>
104
+                        @endforeach
105
+
106
+                        @if ($hasActions && count($containers))
107
+                            <th class="table-repeater-header-column w-px last:rounded-tr-xl rtl:last:rounded-tr-none rtl:last:rounded-tl-xl p-2 bg-gray-100 dark:bg-gray-900/60">
108
+                                <span class="sr-only">
109
+                                    {{ trans('table-repeater::components.repeater.row_actions.label') }}
110
+                                </span>
111
+                            </th>
112
+                        @endif
113
+                    </tr>
114
+                    </thead>
115
+                    <tbody
116
+                        x-sortable
117
+                        wire:end.stop="{{ 'mountFormComponentAction(\'' . $statePath . '\', \'reorder\', { items: $event.target.sortable.toArray() })' }}"
118
+                        class="table-repeater-rows-wrapper divide-y divide-gray-950/5 dark:divide-white/20"
119
+                    >
120
+                    @if (count($containers))
121
+                        @foreach ($containers as $uuid => $row)
122
+                            @php
123
+                                $visibleExtraItemActions = array_filter(
124
+                                    $extraItemActions,
125
+                                    fn (Action $action): bool => $action(['item' => $uuid])->isVisible(),
126
+                                );
127
+                            @endphp
128
+                            <tr
129
+                                wire:key="{{ $this->getId() }}.{{ $row->getStatePath() }}.{{ $field::class }}.item"
130
+                                x-sortable-item="{{ $uuid }}"
131
+                                class="table-repeater-row"
132
+                            >
133
+                                {{-- Add reorder action column at start if reorderAtStart is true --}}
134
+                                @if ($hasActions && $reorderAtStart && $reorderAction->isVisible())
135
+                                    <td class="table-repeater-column p-2 w-px align-top">
136
+                                        <ul class="flex items-center table-repeater-row-actions gap-x-3 px-2">
137
+                                            <li x-sortable-handle class="shrink-0">
138
+                                                {{ $reorderAction }}
139
+                                            </li>
140
+                                        </ul>
141
+                                    </td>
142
+                                @endif
143
+
144
+                                @php($counter = 0)
145
+                                @foreach($row->getComponents() as $cell)
146
+                                    @if($cell instanceof \Filament\Forms\Components\Hidden || $cell->isHidden())
147
+                                        {{ $cell }}
148
+                                    @else
149
+                                        <td
150
+                                            @class([
151
+                                                'table-repeater-column align-top',
152
+                                                'p-2' => ! $streamlined,
153
+                                                'has-hidden-label' => $cell->isLabelHidden(),
154
+                                                match($headers[$counter++]->getAlignment()) {
155
+                                                  'center', Alignment::Center => 'text-center',
156
+                                                  'right', 'end', Alignment::Right, Alignment::End => 'text-end',
157
+                                                  default => 'text-start'
158
+                                                }
159
+                                            ])
160
+                                            style="width: {{ $cell->getMaxWidth() ?? 'auto' }}"
161
+                                        >
162
+                                            {{ $cell }}
163
+                                        </td>
164
+                                    @endif
165
+                                @endforeach
166
+
167
+                                @if ($hasActions)
168
+                                    <td class="table-repeater-column p-2 w-px align-top">
169
+                                        <ul class="flex items-center table-repeater-row-actions gap-x-3 px-2">
170
+                                            @foreach ($visibleExtraItemActions as $extraItemAction)
171
+                                                <li>
172
+                                                    {{ $extraItemAction(['item' => $uuid]) }}
173
+                                                </li>
174
+                                            @endforeach
175
+
176
+                                            @if ($reorderAction->isVisible() && ! $reorderAtStart)
177
+                                                <li x-sortable-handle class="shrink-0">
178
+                                                    {{ $reorderAction }}
179
+                                                </li>
180
+                                            @endif
181
+
182
+                                            @if ($isReorderableWithButtons)
183
+                                                @if (! $loop->first)
184
+                                                    <li>
185
+                                                        {{ $moveUpAction(['item' => $uuid]) }}
186
+                                                    </li>
187
+                                                @endif
188
+
189
+                                                @if (! $loop->last)
190
+                                                    <li>
191
+                                                        {{ $moveDownAction(['item' => $uuid]) }}
192
+                                                    </li>
193
+                                                @endif
194
+                                            @endif
195
+
196
+                                            @if ($cloneAction->isVisible())
197
+                                                <li>
198
+                                                    {{ $cloneAction(['item' => $uuid]) }}
199
+                                                </li>
200
+                                            @endif
201
+
202
+                                            @if ($deleteAction->isVisible())
203
+                                                <li>
204
+                                                    {{ $deleteAction(['item' => $uuid]) }}
205
+                                                </li>
206
+                                            @endif
207
+                                        </ul>
208
+                                    </td>
209
+                                @endif
210
+                            </tr>
211
+                        @endforeach
212
+                    @else
213
+                        <tr class="table-repeater-row table-repeater-empty-row">
214
+                            <td colspan="{{ count($headers) + intval($hasActions) }}"
215
+                                class="table-repeater-column table-repeater-empty-column p-4 w-px text-center italic">
216
+                                {{ $emptyLabel ?: trans('table-repeater::components.repeater.empty.label') }}
217
+                            </td>
218
+                        </tr>
219
+                    @endif
220
+                    </tbody>
221
+                </table>
222
+            </div>
223
+        @endif
224
+
225
+        @if ($addAction->isVisible() || filled($visibleExtraActions))
226
+            <ul
227
+                @class([
228
+                    'relative flex gap-4',
229
+                    match ($getAddActionAlignment()) {
230
+                        Alignment::Start, Alignment::Left => 'justify-start',
231
+                        Alignment::End, Alignment::Right => 'justify-end',
232
+                        default =>  'justify-center',
233
+                    },
234
+                ])
235
+            >
236
+                @if ($addAction->isVisible())
237
+                    <li>
238
+                        {{ $addAction }}
239
+                    </li>
240
+                @endif
241
+                @if (filled($visibleExtraActions))
242
+                    @foreach ($visibleExtraActions as $extraAction)
243
+                        <li>
244
+                            {{ ($extraAction) }}
245
+                        </li>
246
+                    @endforeach
247
+                @endif
248
+            </ul>
249
+        @endif
250
+    </div>
251
+</x-dynamic-component>

Načítá se…
Zrušit
Uložit