Andrew Wallo 4 kuukautta sitten
vanhempi
commit
739432de16

+ 8
- 6
app/Filament/Company/Resources/Accounting/BudgetResource/RelationManagers/BudgetItemsRelationManager.php Näytä tiedosto

@@ -2,7 +2,7 @@
2 2
 
3 3
 namespace App\Filament\Company\Resources\Accounting\BudgetResource\RelationManagers;
4 4
 
5
-use App\Filament\Tables\Columns\DeferredTextInputColumn;
5
+use App\Filament\Tables\Columns\CustomTextInputColumn;
6 6
 use App\Models\Accounting\Budget;
7 7
 use App\Models\Accounting\BudgetAllocation;
8 8
 use App\Models\Accounting\BudgetItem;
@@ -144,7 +144,7 @@ class BudgetItemsRelationManager extends RelationManager
144 144
                     ->titlePrefixedWithLabel(false)
145 145
                     ->collapsible(),
146 146
             ])
147
-            ->recordClasses(['budget-items-relation-manager'])
147
+            ->recordClasses(['is-spreadsheet'])
148 148
             ->defaultGroup('account.category')
149 149
             ->headerActions([
150 150
                 Action::make('saveBatchChanges')
@@ -157,7 +157,7 @@ class BudgetItemsRelationManager extends RelationManager
157 157
                     ->label('Account')
158 158
                     ->limit(30)
159 159
                     ->searchable(),
160
-                DeferredTextInputColumn::make(self::TOTAL_COLUMN)
160
+                CustomTextInputColumn::make(self::TOTAL_COLUMN)
161 161
                     ->label('Total')
162 162
                     ->alignRight()
163 163
                     ->mask(RawJs::make('$money($input)'))
@@ -173,7 +173,8 @@ class BudgetItemsRelationManager extends RelationManager
173 173
 
174 174
                         return CurrencyConverter::convertCentsToFormatSimple($total);
175 175
                     })
176
-                    ->batchMode()
176
+                    ->deferred()
177
+                    ->navigable()
177 178
                     ->summarize(
178 179
                         Summarizer::make()
179 180
                             ->using(function (\Illuminate\Database\Query\Builder $query) {
@@ -258,10 +259,11 @@ class BudgetItemsRelationManager extends RelationManager
258 259
                 ...$allocationPeriods->map(function (BudgetAllocation $period) {
259 260
                     $alias = $period->start_date->format('Y_m_d');
260 261
 
261
-                    return DeferredTextInputColumn::make($alias)
262
+                    return CustomTextInputColumn::make($alias)
262 263
                         ->label($period->period)
263 264
                         ->alignRight()
264
-                        ->batchMode()
265
+                        ->deferred()
266
+                        ->navigable()
265 267
                         ->mask(RawJs::make('$money($input)'))
266 268
                         ->getStateUsing(function ($record) use ($alias) {
267 269
                             $key = "{$record->getKey()}.{$alias}";

+ 2
- 4
app/Filament/Company/Resources/Purchases/BillResource/Pages/ListBills.php Näytä tiedosto

@@ -21,10 +21,8 @@ class ListBills extends ListRecords
21 21
     {
22 22
         return [
23 23
             Actions\Action::make('payBills')
24
-                ->label('Pay Bills')
25
-                ->icon('heroicon-o-credit-card')
26
-                ->color('primary')
27
-                ->url(fn () => BillResource::getUrl('pay-bills')),
24
+                ->outlined()
25
+                ->url(BillResource::getUrl('pay-bills')),
28 26
             Actions\CreateAction::make(),
29 27
         ];
30 28
     }

+ 143
- 80
app/Filament/Company/Resources/Purchases/BillResource/Pages/PayBills.php Näytä tiedosto

@@ -5,9 +5,12 @@ namespace App\Filament\Company\Resources\Purchases\BillResource\Pages;
5 5
 use App\Enums\Accounting\BillStatus;
6 6
 use App\Enums\Accounting\PaymentMethod;
7 7
 use App\Filament\Company\Resources\Purchases\BillResource;
8
+use App\Filament\Tables\Columns\CustomTextInputColumn;
8 9
 use App\Models\Accounting\Bill;
9 10
 use App\Models\Accounting\Transaction;
10 11
 use App\Models\Banking\BankAccount;
12
+use App\Models\Common\Vendor;
13
+use App\Models\Setting\Currency;
11 14
 use App\Utilities\Currency\CurrencyAccessor;
12 15
 use App\Utilities\Currency\CurrencyConverter;
13 16
 use Filament\Actions;
@@ -17,12 +20,13 @@ use Filament\Notifications\Notification;
17 20
 use Filament\Resources\Pages\ListRecords;
18 21
 use Filament\Support\RawJs;
19 22
 use Filament\Tables;
23
+use Filament\Tables\Columns\Summarizers\Summarizer;
20 24
 use Filament\Tables\Columns\TextColumn;
21
-use Filament\Tables\Columns\TextInputColumn;
22
-use Filament\Tables\Enums\FiltersLayout;
23 25
 use Filament\Tables\Table;
24 26
 use Illuminate\Contracts\Support\Htmlable;
25 27
 use Illuminate\Database\Eloquent\Collection;
28
+use Illuminate\Database\Query\Builder;
29
+use Illuminate\Support\Str;
26 30
 use Livewire\Attributes\Computed;
27 31
 
28 32
 /**
@@ -52,11 +56,7 @@ class PayBills extends ListRecords
52 56
     {
53 57
         parent::mount();
54 58
 
55
-        $this->form->fill([
56
-            'bank_account_id' => BankAccount::where('enabled', true)->first()?->id,
57
-            'payment_date' => now(),
58
-            'payment_method' => PaymentMethod::Check,
59
-        ]);
59
+        $this->form->fill();
60 60
 
61 61
         $this->reset('tableFilters');
62 62
     }
@@ -64,17 +64,16 @@ class PayBills extends ListRecords
64 64
     protected function getHeaderActions(): array
65 65
     {
66 66
         return [
67
-            Actions\Action::make('paySelected')
68
-                ->label('Pay Selected Bills')
69
-                ->icon('heroicon-o-credit-card')
67
+            Actions\Action::make('processPayments')
70 68
                 ->color('primary')
71 69
                 ->action(function () {
72 70
                     $data = $this->data;
73
-                    $selectedRecords = $this->getTableRecords();
71
+                    $tableRecords = $this->getTableRecords();
74 72
                     $paidCount = 0;
75 73
                     $totalPaid = 0;
76 74
 
77
-                    foreach ($selectedRecords as $bill) {
75
+                    /** @var Bill $bill */
76
+                    foreach ($tableRecords as $bill) {
78 77
                         if (! $bill->canRecordPayment()) {
79 78
                             continue;
80 79
                         }
@@ -87,7 +86,7 @@ class PayBills extends ListRecords
87 86
                         }
88 87
 
89 88
                         $paymentData = [
90
-                            'posted_at' => $data['payment_date'],
89
+                            'posted_at' => $data['posted_at'],
91 90
                             'payment_method' => $data['payment_method'],
92 91
                             'bank_account_id' => $data['bank_account_id'],
93 92
                             'amount' => $paymentAmount,
@@ -98,18 +97,16 @@ class PayBills extends ListRecords
98 97
                         $totalPaid += $paymentAmount;
99 98
                     }
100 99
 
101
-                    $totalFormatted = CurrencyConverter::formatCentsToMoney($totalPaid);
100
+                    $currencyCode = $this->getTableFilterState('currency_code')['value'];
101
+                    $totalFormatted = CurrencyConverter::formatCentsToMoney($totalPaid, $currencyCode, true);
102 102
 
103 103
                     Notification::make()
104 104
                         ->title('Bills paid successfully')
105
-                        ->body("Paid {$paidCount} bill(s) for a total of {$totalFormatted}")
105
+                        ->body("Paid {$paidCount} " . Str::plural('bill', $paidCount) . " for a total of {$totalFormatted}")
106 106
                         ->success()
107 107
                         ->send();
108 108
 
109
-                    // Clear payment amounts after successful payment
110
-                    foreach ($selectedRecords as $bill) {
111
-                        $this->paymentAmounts[$bill->id] = 0;
112
-                    }
109
+                    $this->reset('paymentAmounts');
113 110
 
114 111
                     $this->resetTable();
115 112
                 }),
@@ -129,28 +126,29 @@ class PayBills extends ListRecords
129 126
     public function form(Form $form): Form
130 127
     {
131 128
         return $form
129
+            ->live()
132 130
             ->schema([
133 131
                 Forms\Components\Grid::make(3)
134 132
                     ->schema([
135 133
                         Forms\Components\Select::make('bank_account_id')
136
-                            ->label('Bank Account')
137
-                            ->options(function () {
134
+                            ->label('Account')
135
+                            ->options(static function () {
138 136
                                 return Transaction::getBankAccountOptionsFlat();
139 137
                             })
138
+                            ->default(fn () => BankAccount::where('enabled', true)->first()?->id)
140 139
                             ->selectablePlaceholder(false)
141 140
                             ->searchable()
142 141
                             ->softRequired(),
143
-                        Forms\Components\DatePicker::make('payment_date')
144
-                            ->label('Payment Date')
142
+                        Forms\Components\DatePicker::make('posted_at')
143
+                            ->label('Date')
145 144
                             ->default(now())
146 145
                             ->softRequired(),
147 146
                         Forms\Components\Select::make('payment_method')
148
-                            ->label('Payment Method')
147
+                            ->label('Payment method')
149 148
                             ->selectablePlaceholder(false)
150 149
                             ->options(PaymentMethod::class)
151
-                            ->default(PaymentMethod::Check)
152
-                            ->softRequired()
153
-                            ->live(),
150
+                            ->default(PaymentMethod::BankPayment)
151
+                            ->softRequired(),
154 152
                     ]),
155 153
             ])->statePath('data');
156 154
     }
@@ -163,29 +161,62 @@ class PayBills extends ListRecords
163 161
                     ->with(['vendor'])
164 162
                     ->unpaid()
165 163
             )
166
-            ->selectable()
164
+            ->recordClasses(['is-spreadsheet'])
165
+            ->defaultSort('due_date')
166
+            ->paginated(false)
167 167
             ->columns([
168 168
                 TextColumn::make('vendor.name')
169 169
                     ->label('Vendor')
170 170
                     ->sortable(),
171 171
                 TextColumn::make('bill_number')
172
-                    ->label('Bill #')
172
+                    ->label('Bill number')
173 173
                     ->sortable(),
174 174
                 TextColumn::make('due_date')
175
-                    ->label('Due Date')
176
-                    ->date('M j, Y')
175
+                    ->label('Due date')
176
+                    ->defaultDateFormat()
177
+                    ->sortable(),
178
+                Tables\Columns\TextColumn::make('status')
179
+                    ->badge()
177 180
                     ->sortable(),
178 181
                 TextColumn::make('amount_due')
179
-                    ->label('Amount Due')
182
+                    ->label('Amount due')
180 183
                     ->currency(static fn (Bill $record) => $record->currency_code)
181 184
                     ->alignEnd()
182
-                    ->sortable(),
183
-                TextInputColumn::make('payment_amount')
184
-                    ->label('Payment Amount')
185
+                    ->sortable()
186
+                    ->summarize([
187
+                        Summarizer::make()
188
+                            ->using(function (Builder $query) {
189
+                                $totalAmountDue = $query->sum('amount_due');
190
+                                $bankAccountCurrency = $this->getSelectedBankAccount()->account->currency_code;
191
+                                $activeCurrency = $this->getTableFilterState('currency_code')['value'] ?? $bankAccountCurrency;
192
+
193
+                                if ($activeCurrency !== $bankAccountCurrency) {
194
+                                    $totalAmountDue = CurrencyConverter::convertBalance($totalAmountDue, $activeCurrency, $bankAccountCurrency);
195
+                                }
196
+
197
+                                return CurrencyConverter::formatCentsToMoney($totalAmountDue, $bankAccountCurrency, true);
198
+                            }),
199
+                        Summarizer::make()
200
+                            ->using(function (Builder $query) {
201
+                                $totalAmountDue = $query->sum('amount_due');
202
+                                $currencyCode = $this->getTableFilterState('currency_code')['value'];
203
+
204
+                                return CurrencyConverter::formatCentsToMoney($totalAmountDue, $currencyCode, true);
205
+                            })
206
+                            ->visible(function () {
207
+                                $activeCurrency = $this->getTableFilterState('currency_code')['value'] ?? null;
208
+                                $bankAccountCurrency = $this->getSelectedBankAccount()->account->currency_code;
209
+
210
+                                return $activeCurrency && $activeCurrency !== $bankAccountCurrency;
211
+                            }),
212
+                    ]),
213
+                CustomTextInputColumn::make('payment_amount')
214
+                    ->label('Payment amount')
185 215
                     ->alignEnd()
216
+                    ->navigable()
186 217
                     ->mask(RawJs::make('$money($input)'))
187 218
                     ->updateStateUsing(function (Bill $record, $state) {
188
-                        if (empty($state) || $state === '0.00') {
219
+                        if (! CurrencyConverter::isValidAmount($state, 'USD')) {
189 220
                             $this->paymentAmounts[$record->id] = 0;
190 221
 
191 222
                             return '0.00';
@@ -193,18 +224,8 @@ class PayBills extends ListRecords
193 224
 
194 225
                         $paymentCents = CurrencyConverter::convertToCents($state, 'USD');
195 226
 
196
-                        // Validate payment doesn't exceed amount due
197 227
                         if ($paymentCents > $record->amount_due) {
198
-                            Notification::make()
199
-                                ->title('Invalid payment amount')
200
-                                ->body('Payment cannot exceed amount due')
201
-                                ->warning()
202
-                                ->send();
203
-
204
-                            $maxAmount = CurrencyConverter::convertCentsToFormatSimple($record->amount_due, 'USD');
205
-                            $this->paymentAmounts[$record->id] = $record->amount_due;
206
-
207
-                            return $maxAmount;
228
+                            $paymentCents = $record->amount_due;
208 229
                         }
209 230
 
210 231
                         $this->paymentAmounts[$record->id] = $paymentCents;
@@ -215,27 +236,33 @@ class PayBills extends ListRecords
215 236
                         $paymentAmount = $this->paymentAmounts[$record->id] ?? 0;
216 237
 
217 238
                         return CurrencyConverter::convertCentsToFormatSimple($paymentAmount, 'USD');
218
-                    }),
219
-            ])
220
-            ->actions([
221
-                Tables\Actions\Action::make('setFullAmount')
222
-                    ->label('Pay Full')
223
-                    ->icon('heroicon-o-banknotes')
224
-                    ->color('primary')
225
-                    ->action(function (Bill $record) {
226
-                        $this->paymentAmounts[$record->id] = $record->amount_due;
227
-                    }),
228
-                Tables\Actions\Action::make('clearAmount')
229
-                    ->label('Clear')
230
-                    ->icon('heroicon-o-x-mark')
231
-                    ->color('gray')
232
-                    ->action(function (Bill $record) {
233
-                        $this->paymentAmounts[$record->id] = 0;
234
-                    }),
239
+                    })
240
+                    ->summarize([
241
+                        Summarizer::make()
242
+                            ->using(function () {
243
+                                $total = array_sum($this->paymentAmounts);
244
+                                $defaultCurrency = CurrencyAccessor::getDefaultCurrency();
245
+                                $activeCurrency = $this->getTableFilterState('currency_code')['value'] ?? $defaultCurrency;
246
+
247
+                                if ($activeCurrency !== $defaultCurrency) {
248
+                                    $total = CurrencyConverter::convertBalance($total, $activeCurrency, $defaultCurrency);
249
+                                }
250
+
251
+                                return CurrencyConverter::formatCentsToMoney($total, $defaultCurrency, true);
252
+                            }),
253
+                        Summarizer::make()
254
+                            ->using(fn () => $this->totalPaymentAmount)
255
+                            ->visible(function () {
256
+                                $activeCurrency = $this->getTableFilterState('currency_code')['value'] ?? null;
257
+                                $defaultCurrency = CurrencyAccessor::getDefaultCurrency();
258
+
259
+                                return $activeCurrency && $activeCurrency !== $defaultCurrency;
260
+                            }),
261
+                    ]),
235 262
             ])
236 263
             ->bulkActions([
237 264
                 Tables\Actions\BulkAction::make('setFullAmounts')
238
-                    ->label('Set Full Amounts')
265
+                    ->label('Set full amounts')
239 266
                     ->icon('heroicon-o-banknotes')
240 267
                     ->color('primary')
241 268
                     ->deselectRecordsAfterCompletion()
@@ -245,7 +272,7 @@ class PayBills extends ListRecords
245 272
                         });
246 273
                     }),
247 274
                 Tables\Actions\BulkAction::make('clearAmounts')
248
-                    ->label('Clear Amounts')
275
+                    ->label('Clear amounts')
249 276
                     ->icon('heroicon-o-x-mark')
250 277
                     ->color('gray')
251 278
                     ->deselectRecordsAfterCompletion()
@@ -256,23 +283,40 @@ class PayBills extends ListRecords
256 283
                     }),
257 284
             ])
258 285
             ->filters([
259
-                Tables\Filters\SelectFilter::make('currency')
286
+                Tables\Filters\SelectFilter::make('currency_code')
287
+                    ->label('Currency')
260 288
                     ->selectablePlaceholder(false)
261 289
                     ->default(CurrencyAccessor::getDefaultCurrency())
262
-                    ->relationship('currency', 'name')
290
+                    ->options(Currency::query()->pluck('name', 'code')->toArray())
263 291
                     ->searchable()
264
-                    ->preload(),
265
-                Tables\Filters\SelectFilter::make('vendor')
266
-                    ->relationship('vendor', 'name')
267
-                    ->searchable()
268
-                    ->preload(),
292
+                    ->resetState([
293
+                        'value' => CurrencyAccessor::getDefaultCurrency(),
294
+                    ])
295
+                    ->indicateUsing(function (Tables\Filters\SelectFilter $filter, array $state) {
296
+                        if (blank($state['value'] ?? null)) {
297
+                            return [];
298
+                        }
299
+
300
+                        $label = collect($filter->getOptions())
301
+                            ->mapWithKeys(fn (string | array $label, string $value): array => is_array($label) ? $label : [$value => $label])
302
+                            ->get($state['value']);
303
+
304
+                        if (blank($label)) {
305
+                            return [];
306
+                        }
307
+
308
+                        $indicator = $filter->getLabel();
309
+
310
+                        return Tables\Filters\Indicator::make("{$indicator}: {$label}")->removable(false);
311
+                    }),
312
+                Tables\Filters\SelectFilter::make('vendor_id')
313
+                    ->label('Vendor')
314
+                    ->options(fn () => Vendor::query()->pluck('name', 'id')->toArray())
315
+                    ->searchable(),
269 316
                 Tables\Filters\SelectFilter::make('status')
270 317
                     ->multiple()
271 318
                     ->options(BillStatus::getUnpaidOptions()),
272
-            ], layout: FiltersLayout::AboveContent)
273
-            ->defaultSort('due_date')
274
-            ->striped()
275
-            ->paginated(false);
319
+            ]);
276 320
     }
277 321
 
278 322
     protected function getPaymentAmount(Bill $record): int
@@ -281,12 +325,31 @@ class PayBills extends ListRecords
281 325
     }
282 326
 
283 327
     #[Computed]
284
-    public function totalSelectedPaymentAmount(): string
328
+    public function totalPaymentAmount(): string
285 329
     {
286 330
         $total = array_sum($this->paymentAmounts);
287 331
 
288
-        $currencyCode = $this->getTableFilterState('currency')['value'];
332
+        $currencyCode = $this->getTableFilterState('currency_code')['value'];
333
+
334
+        return CurrencyConverter::formatCentsToMoney($total, $currencyCode, true);
335
+    }
336
+
337
+    public function getSelectedBankAccount(): BankAccount
338
+    {
339
+        $bankAccountId = $this->data['bank_account_id'];
340
+
341
+        $bankAccount = BankAccount::find($bankAccountId);
342
+
343
+        return $bankAccount ?: BankAccount::where('enabled', true)->first();
344
+    }
345
+
346
+    protected function handleTableFilterUpdates(): void
347
+    {
348
+        parent::handleTableFilterUpdates();
349
+
350
+        $visibleBillIds = $this->getTableRecords()->pluck('id')->toArray();
351
+        $visibleBillKeys = array_flip($visibleBillIds);
289 352
 
290
-        return CurrencyConverter::formatCentsToMoney($total, $currencyCode);
353
+        $this->paymentAmounts = array_intersect_key($this->paymentAmounts, $visibleBillKeys);
291 354
     }
292 355
 }

+ 39
- 0
app/Filament/Tables/Columns/CustomTextInputColumn.php Näytä tiedosto

@@ -0,0 +1,39 @@
1
+<?php
2
+
3
+namespace App\Filament\Tables\Columns;
4
+
5
+use Closure;
6
+use Filament\Tables\Columns\TextInputColumn;
7
+
8
+class CustomTextInputColumn extends TextInputColumn
9
+{
10
+    protected string $view = 'filament.tables.columns.custom-text-input-column';
11
+
12
+    protected bool | Closure $isDeferred = false;
13
+
14
+    protected bool | Closure $isNavigable = false;
15
+
16
+    public function deferred(bool | Closure $condition = true): static
17
+    {
18
+        $this->isDeferred = $condition;
19
+
20
+        return $this;
21
+    }
22
+
23
+    public function navigable(bool | Closure $condition = true): static
24
+    {
25
+        $this->isNavigable = $condition;
26
+
27
+        return $this;
28
+    }
29
+
30
+    public function isDeferred(): bool
31
+    {
32
+        return (bool) $this->evaluate($this->isDeferred);
33
+    }
34
+
35
+    public function isNavigable(): bool
36
+    {
37
+        return (bool) $this->evaluate($this->isNavigable);
38
+    }
39
+}

+ 0
- 25
app/Filament/Tables/Columns/DeferredTextInputColumn.php Näytä tiedosto

@@ -1,25 +0,0 @@
1
-<?php
2
-
3
-namespace App\Filament\Tables\Columns;
4
-
5
-use Closure;
6
-use Filament\Tables\Columns\TextInputColumn;
7
-
8
-class DeferredTextInputColumn extends TextInputColumn
9
-{
10
-    protected string $view = 'filament.tables.columns.deferred-text-input-column';
11
-
12
-    protected bool | Closure $batchMode = false;
13
-
14
-    public function batchMode(bool | Closure $condition = true): static
15
-    {
16
-        $this->batchMode = $condition;
17
-
18
-        return $this;
19
-    }
20
-
21
-    public function getBatchMode(): bool
22
-    {
23
-        return $this->evaluate($this->batchMode);
24
-    }
25
-}

+ 2
- 2
resources/css/filament/company/theme.css Näytä tiedosto

@@ -66,7 +66,7 @@
66 66
     z-index: -1;
67 67
 }
68 68
 
69
-.fi-ta-table:has(.budget-items-relation-manager) {
69
+.fi-ta-table:has(.is-spreadsheet) {
70 70
     .fi-ta-row {
71 71
         @apply divide-x divide-gray-200 dark:divide-gray-700;
72 72
     }
@@ -102,7 +102,7 @@
102 102
         }
103 103
     }
104 104
 
105
-    .fi-ta-cell:focus-within {
105
+    .fi-ta-cell:has(.fi-ta-text-input):focus-within {
106 106
         outline: 2px solid #2563eb;
107 107
         outline-offset: -2px;
108 108
         z-index: 1;

+ 18
- 7
resources/views/filament/company/resources/purchases/bill-resource/pages/pay-bills.blade.php Näytä tiedosto

@@ -1,17 +1,28 @@
1
-<x-filament-panels::page>
2
-    <div class="space-y-6">
3
-        <div class="bg-white rounded-lg border border-gray-200 p-6">
4
-            <div class="flex items-center justify-between mb-4">
1
+<x-filament-panels::page
2
+    @class([
3
+        'fi-resource-list-records-page',
4
+        'fi-resource-' . str_replace('/', '-', $this->getResource()::getSlug()),
5
+    ])
6
+>
7
+    <div class="flex flex-col gap-y-6">
8
+        <x-filament::section>
9
+            <div class="flex items-center justify-between">
5 10
                 <div>
6 11
                     {{ $this->form }}
7 12
                 </div>
8 13
                 <div class="text-right">
9
-                    <div class="text-sm text-gray-500">Total Payment Amount</div>
10
-                    <div class="text-xl font-semibold text-gray-900" id="total-amount">{{ $this->totalSelectedPaymentAmount }}</div>
14
+                    <div class="text-sm uppercase text-gray-500 dark:text-gray-400">Total Payment Amount</div>
15
+                    <div class="text-3xl font-semibold text-gray-900 dark:text-white">{{ $this->totalPaymentAmount }}</div>
11 16
                 </div>
12 17
             </div>
13
-        </div>
18
+        </x-filament::section>
19
+
20
+        <x-filament-panels::resources.tabs />
21
+
22
+        {{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::RESOURCE_PAGES_LIST_RECORDS_TABLE_BEFORE, scopes: $this->getRenderHookScopes()) }}
14 23
 
15 24
         {{ $this->table }}
25
+
26
+        {{ \Filament\Support\Facades\FilamentView::renderHook(\Filament\View\PanelsRenderHook::RESOURCE_PAGES_LIST_RECORDS_TABLE_AFTER, scopes: $this->getRenderHookScopes()) }}
16 27
     </div>
17 28
 </x-filament-panels::page>

resources/views/filament/tables/columns/deferred-text-input-column.blade.php → resources/views/filament/tables/columns/custom-text-input-column.blade.php Näytä tiedosto

@@ -4,7 +4,8 @@
4 4
     $isDisabled = $isDisabled();
5 5
     $state = $getState();
6 6
     $mask = $getMask();
7
-    $batchMode = $getBatchMode();
7
+    $isDeferred = $isDeferred();
8
+    $isNavigable = $isNavigable();
8 9
 
9 10
     $alignment = $getAlignment() ?? Alignment::Start;
10 11
 
@@ -32,6 +33,42 @@
32 33
         recordKey: @js($recordKey),
33 34
 
34 35
         state: @js($state),
36
+
37
+        navigateToRow(direction) {
38
+            const currentRow = $el.closest('tr');
39
+            const currentCell = $el.closest('td');
40
+            const currentColumnIndex = Array.from(currentRow.children).indexOf(currentCell);
41
+
42
+            const targetRow = direction === 'next'
43
+                ? currentRow.nextElementSibling
44
+                : currentRow.previousElementSibling;
45
+
46
+            if (targetRow && targetRow.children[currentColumnIndex]) {
47
+                const targetInput = targetRow.children[currentColumnIndex].querySelector('input[x-model=\'state\']');
48
+                if (targetInput) {
49
+                    targetInput.focus();
50
+                    targetInput.select();
51
+                }
52
+            }
53
+        },
54
+
55
+        navigateToColumn(direction) {
56
+            const currentCell = $el.closest('td');
57
+            const currentRow = $el.closest('tr');
58
+            const currentColumnIndex = Array.from(currentRow.children).indexOf(currentCell);
59
+
60
+            const targetCell = direction === 'next'
61
+                ? currentRow.children[currentColumnIndex + 1]
62
+                : currentRow.children[currentColumnIndex - 1];
63
+
64
+            if (targetCell) {
65
+                const targetInput = targetCell.querySelector('input[x-model=\'state\']');
66
+                if (targetInput) {
67
+                    targetInput.focus();
68
+                    targetInput.select();
69
+                }
70
+            }
71
+        }
35 72
     }"
36 73
     x-init="
37 74
         () => {
@@ -105,7 +142,7 @@
105 142
                 \Filament\Support\prepare_inherited_attributes(
106 143
                     $getExtraInputAttributeBag()
107 144
                         ->merge([
108
-                            'x-on:change' . ($type === 'number' ? '.debounce.1s' : null) => $batchMode ? '
145
+                            'x-on:change' . ($type === 'number' ? '.debounce.1s' : null) => $isDeferred ? '
109 146
                                 $wire.handleBatchColumnChanged({
110 147
                                     name: name,
111 148
                                     recordKey: recordKey,
@@ -128,7 +165,7 @@
128 165
 
129 166
                                 isLoading = false
130 167
                             ',
131
-                            'x-on:keydown.enter' => $batchMode ? '
168
+                            'x-on:keydown.enter' => $isDeferred ? '
132 169
                                 $wire.handleBatchColumnChanged({
133 170
                                     name: name,
134 171
                                     recordKey: recordKey,
@@ -138,7 +175,11 @@
138 175
                                 $nextTick(() => {
139 176
                                     $wire.saveBatchChanges();
140 177
                                 });
141
-                            ' : null,
178
+                            ' : ($isNavigable ? 'navigateToRow(\'next\')' : null),
179
+                            'x-on:keydown.arrow-down.prevent' => $isNavigable ? 'navigateToRow(\'next\')' : null,
180
+                            'x-on:keydown.arrow-up.prevent' => $isNavigable ? 'navigateToRow(\'prev\')' : null,
181
+                            'x-on:keydown.arrow-left.prevent' => $isNavigable ? 'navigateToColumn(\'prev\')' : null,
182
+                            'x-on:keydown.arrow-right.prevent' => $isNavigable ? 'navigateToColumn(\'next\')' : null,
142 183
                             'x-mask' . ($mask instanceof \Filament\Support\RawJs ? ':dynamic' : '') => filled($mask) ? $mask : null,
143 184
                         ])
144 185
                         ->class([

Loading…
Peruuta
Tallenna