浏览代码

update: ConnectedAccounts page

3.x
wallo 1年前
父节点
当前提交
7cc0f85d32
共有 32 个文件被更改,包括 407 次插入221 次删除
  1. 3
    1
      app/Events/PlaidSuccess.php
  2. 11
    11
      app/Events/StartTransactionImport.php
  3. 8
    8
      app/Filament/Company/Pages/Accounting/AccountChart.php
  4. 2
    2
      app/Filament/Company/Pages/Service/ConnectedAccount.php
  5. 0
    3
      app/Filament/Company/Resources/Accounting/TransactionResource.php
  6. 0
    1
      app/Filament/Company/Resources/Accounting/TransactionResource/Pages/CreateTransaction.php
  7. 3
    5
      app/Filament/Company/Resources/Banking/AccountResource.php
  8. 0
    5
      app/Filament/Company/Resources/Banking/AccountResource/Pages/CreateAccount.php
  9. 0
    3
      app/Filament/Company/Resources/Banking/AccountResource/Pages/EditAccount.php
  10. 0
    2
      app/Listeners/CreateConnectedAccount.php
  11. 1
    9
      app/Listeners/HandleTransactionImport.php
  12. 63
    45
      app/Livewire/Company/Service/ConnectedAccount/ListInstitutions.php
  13. 2
    2
      app/Livewire/UpdatePassword.php
  14. 2
    2
      app/Livewire/UpdateProfileInformation.php
  15. 1
    2
      app/Models/Accounting/AccountSubtype.php
  16. 1
    2
      app/Models/Banking/BankAccount.php
  17. 1
    1
      app/Models/Banking/ConnectedBankAccount.php
  18. 34
    34
      app/Observers/AccountObserver.php
  19. 1
    3
      app/Observers/BankAccountObserver.php
  20. 0
    1
      app/Providers/EventServiceProvider.php
  21. 9
    9
      app/Providers/MacroServiceProvider.php
  22. 1
    1
      app/Services/PlaidService.php
  23. 4
    2
      app/Utilities/Plaid/AccountTypeMapper.php
  24. 22
    22
      composer.lock
  25. 2
    2
      config/chart-of-accounts.php
  26. 159
    0
      config/livewire.php
  27. 1
    1
      config/services.php
  28. 0
    1
      database/factories/Accounting/AccountFactory.php
  29. 17
    14
      package-lock.json
  30. 6
    0
      resources/views/components/actions/delete-bank-connection-modal.blade.php
  31. 18
    0
      resources/views/components/actions/transaction-import-modal.blade.php
  32. 35
    27
      resources/views/livewire/company/service/connected-account/list-institutions.blade.php

+ 3
- 1
app/Events/PlaidSuccess.php 查看文件

@@ -8,9 +8,11 @@ use Illuminate\Queue\SerializesModels;
8 8
 
9 9
 class PlaidSuccess
10 10
 {
11
-    use Dispatchable, SerializesModels;
11
+    use Dispatchable;
12
+    use SerializesModels;
12 13
 
13 14
     public string $publicToken;
15
+
14 16
     public string $accessToken;
15 17
 
16 18
     public Company $company;

+ 11
- 11
app/Events/StartTransactionImport.php 查看文件

@@ -2,31 +2,31 @@
2 2
 
3 3
 namespace App\Events;
4 4
 
5
+use App\Models\Banking\ConnectedBankAccount;
5 6
 use App\Models\Company;
6
-use Illuminate\Broadcasting\Channel;
7
-use Illuminate\Broadcasting\InteractsWithSockets;
8
-use Illuminate\Broadcasting\PresenceChannel;
9
-use Illuminate\Broadcasting\PrivateChannel;
10
-use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
11 7
 use Illuminate\Foundation\Events\Dispatchable;
12 8
 use Illuminate\Queue\SerializesModels;
13 9
 
14 10
 class StartTransactionImport
15 11
 {
16
-    use Dispatchable, SerializesModels;
12
+    use Dispatchable;
13
+    use SerializesModels;
17 14
 
18 15
     public Company $company;
19
-    public $connectedBankAccountId;
20
-    public $selectedBankAccountId;
21
-    public $startDate;
16
+
17
+    public ConnectedBankAccount $connectedBankAccount;
18
+
19
+    public mixed $selectedBankAccountId;
20
+
21
+    public mixed $startDate;
22 22
 
23 23
     /**
24 24
      * Create a new event instance.
25 25
      */
26
-    public function __construct($company, $connectedBankAccountId, $selectedBankAccountId, $startDate)
26
+    public function __construct($company, $connectedBankAccount, $selectedBankAccountId, $startDate)
27 27
     {
28 28
         $this->company = $company;
29
-        $this->connectedBankAccountId = $connectedBankAccountId;
29
+        $this->connectedBankAccount = $connectedBankAccount;
30 30
         $this->selectedBankAccountId = $selectedBankAccountId;
31 31
         $this->startDate = $startDate;
32 32
     }

+ 8
- 8
app/Filament/Company/Pages/Accounting/AccountChart.php 查看文件

@@ -2,8 +2,8 @@
2 2
 
3 3
 namespace App\Filament\Company\Pages\Accounting;
4 4
 
5
-use App\Models\Accounting\Account as ChartModel;
6 5
 use App\Enums\Accounting\AccountCategory;
6
+use App\Models\Accounting\Account as ChartModel;
7 7
 use App\Models\Accounting\AccountSubtype;
8 8
 use App\Utilities\Accounting\AccountCode;
9 9
 use App\Utilities\Currency\CurrencyAccessor;
@@ -117,11 +117,11 @@ class AccountChart extends Page
117 117
                 ->disabled(static fn (string $operation, ?ChartModel $record) => $operation === 'edit' && $record?->default === true)
118 118
                 ->options($this->getChartSubtypeOptions($useActiveTab))
119 119
                 ->afterStateUpdated(static function (?string $state, Set $set): void {
120
-                   if ($state) {
121
-                       $companyId = auth()->user()->currentCompany->id;
122
-                       $generatedCode = AccountCode::generate($companyId, $state);
123
-                       $set('code', $generatedCode);
124
-                   }
120
+                    if ($state) {
121
+                        $companyId = auth()->user()->currentCompany->id;
122
+                        $generatedCode = AccountCode::generate($companyId, $state);
123
+                        $set('code', $generatedCode);
124
+                    }
125 125
                 }),
126 126
             TextInput::make('code')
127 127
                 ->label('Code')
@@ -154,8 +154,8 @@ class AccountChart extends Page
154 154
             AccountSubtype::where('category', $this->activeTab)->get() :
155 155
             AccountSubtype::all();
156 156
 
157
-        return $subtypes->groupBy(fn(AccountSubtype $subtype) => $subtype->type->getLabel())
158
-            ->map(fn(Collection $subtypes, string $type) => $subtypes->mapWithKeys(static fn (AccountSubtype $subtype) => [$subtype->id => $subtype->name]))
157
+        return $subtypes->groupBy(fn (AccountSubtype $subtype) => $subtype->type->getLabel())
158
+            ->map(fn (Collection $subtypes, string $type) => $subtypes->mapWithKeys(static fn (AccountSubtype $subtype) => [$subtype->id => $subtype->name]))
159 159
             ->toArray();
160 160
     }
161 161
 

+ 2
- 2
app/Filament/Company/Pages/Service/ConnectedAccount.php 查看文件

@@ -20,7 +20,7 @@ class ConnectedAccount extends Page
20 20
 
21 21
     protected static string $view = 'filament.company.pages.service.connected-account';
22 22
 
23
-    public function getTitle(): string|Htmlable
23
+    public function getTitle(): string | Htmlable
24 24
     {
25 25
         return translate(static::$title);
26 26
     }
@@ -48,7 +48,7 @@ class ConnectedAccount extends Page
48 48
         ];
49 49
     }
50 50
 
51
-    public function getMaxContentWidth(): MaxWidth|string|null
51
+    public function getMaxContentWidth(): MaxWidth | string | null
52 52
     {
53 53
         return MaxWidth::ScreenLarge;
54 54
     }

+ 0
- 3
app/Filament/Company/Resources/Accounting/TransactionResource.php 查看文件

@@ -4,7 +4,6 @@ namespace App\Filament\Company\Resources\Accounting;
4 4
 
5 5
 use App\Enums\DateFormat;
6 6
 use App\Filament\Company\Resources\Accounting\TransactionResource\Pages;
7
-use App\Filament\Company\Resources\Accounting\TransactionResource\RelationManagers;
8 7
 use App\Models\Accounting\Transaction;
9 8
 use App\Models\Banking\Account;
10 9
 use App\Models\Setting\Localization;
@@ -14,8 +13,6 @@ use Filament\Resources\Resource;
14 13
 use Filament\Support\Enums\FontWeight;
15 14
 use Filament\Tables;
16 15
 use Filament\Tables\Table;
17
-use Illuminate\Database\Eloquent\Builder;
18
-use Illuminate\Database\Eloquent\SoftDeletingScope;
19 16
 use Illuminate\Support\Carbon;
20 17
 
21 18
 class TransactionResource extends Resource

+ 0
- 1
app/Filament/Company/Resources/Accounting/TransactionResource/Pages/CreateTransaction.php 查看文件

@@ -3,7 +3,6 @@
3 3
 namespace App\Filament\Company\Resources\Accounting\TransactionResource\Pages;
4 4
 
5 5
 use App\Filament\Company\Resources\Accounting\TransactionResource;
6
-use Filament\Actions;
7 6
 use Filament\Resources\Pages\CreateRecord;
8 7
 
9 8
 class CreateTransaction extends CreateRecord

+ 3
- 5
app/Filament/Company/Resources/Banking/AccountResource.php 查看文件

@@ -4,7 +4,6 @@ namespace App\Filament\Company\Resources\Banking;
4 4
 
5 5
 use App\Actions\OptionAction\CreateCurrency;
6 6
 use App\Enums\Accounting\AccountCategory;
7
-use App\Enums\Accounting\AccountType;
8 7
 use App\Enums\BankAccountType;
9 8
 use App\Facades\Forex;
10 9
 use App\Filament\Company\Resources\Banking\AccountResource\Pages;
@@ -25,7 +24,6 @@ use Illuminate\Support\Collection;
25 24
 use Illuminate\Support\Facades\Auth;
26 25
 use Illuminate\Support\Facades\DB;
27 26
 use Illuminate\Validation\Rules\Unique;
28
-use Livewire\Component as Livewire;
29 27
 use Wallo\FilamentSelectify\Components\ToggleButton;
30 28
 
31 29
 class AccountResource extends Resource
@@ -198,7 +196,7 @@ class AccountResource extends Resource
198 196
                     ->icon(static fn (BankAccount $record) => $record->isEnabled() ? 'heroicon-o-lock-closed' : null)
199 197
                     ->tooltip(static fn (BankAccount $record) => $record->isEnabled() ? 'Default Account' : null)
200 198
                     ->iconPosition('after')
201
-                    ->description(static fn (BankAccount $record) => $record->number ?: 'N/A')
199
+                    ->description(static fn (BankAccount $record) => $record->mask ?: 'N/A')
202 200
                     ->sortable(),
203 201
                 Tables\Columns\TextColumn::make('account.starting_balance')
204 202
                     ->localizeLabel('Current Balance')
@@ -300,8 +298,8 @@ class AccountResource extends Resource
300 298
 
301 299
         $subtypes = AccountSubtype::where('category', $category)->get();
302 300
 
303
-        return $subtypes->groupBy(fn(AccountSubtype $subtype) => $subtype->type->getLabel())
304
-            ->map(fn(Collection $subtypes, string $type) => $subtypes->mapWithKeys(static fn (AccountSubtype $subtype) => [$subtype->id => $subtype->name]))
301
+        return $subtypes->groupBy(fn (AccountSubtype $subtype) => $subtype->type->getLabel())
302
+            ->map(fn (Collection $subtypes, string $type) => $subtypes->mapWithKeys(static fn (AccountSubtype $subtype) => [$subtype->id => $subtype->name]))
305 303
             ->toArray();
306 304
     }
307 305
 }

+ 0
- 5
app/Filament/Company/Resources/Banking/AccountResource/Pages/CreateAccount.php 查看文件

@@ -3,17 +3,12 @@
3 3
 namespace App\Filament\Company\Resources\Banking\AccountResource\Pages;
4 4
 
5 5
 use App\Filament\Company\Resources\Banking\AccountResource;
6
-use App\Models\Banking\BankAccount;
7
-use App\Traits\HandlesResourceRecordCreation;
8 6
 use Filament\Resources\Pages\CreateRecord;
9
-use Filament\Support\Exceptions\Halt;
10 7
 use Illuminate\Database\Eloquent\Model;
11
-use Illuminate\Support\Facades\Auth;
12 8
 use Illuminate\Support\Facades\Log;
13 9
 
14 10
 class CreateAccount extends CreateRecord
15 11
 {
16
-
17 12
     protected static string $resource = AccountResource::class;
18 13
 
19 14
     protected function getRedirectUrl(): string

+ 0
- 3
app/Filament/Company/Resources/Banking/AccountResource/Pages/EditAccount.php 查看文件

@@ -6,9 +6,6 @@ use App\Filament\Company\Resources\Banking\AccountResource;
6 6
 use App\Traits\HandlesResourceRecordUpdate;
7 7
 use Filament\Actions;
8 8
 use Filament\Resources\Pages\EditRecord;
9
-use Filament\Support\Exceptions\Halt;
10
-use Illuminate\Database\Eloquent\Model;
11
-use Illuminate\Support\Facades\Auth;
12 9
 
13 10
 class EditAccount extends EditRecord
14 11
 {

+ 0
- 2
app/Listeners/CreateConnectedAccount.php 查看文件

@@ -6,8 +6,6 @@ use App\Events\PlaidSuccess;
6 6
 use App\Models\Banking\Institution;
7 7
 use App\Models\Company;
8 8
 use App\Services\PlaidService;
9
-use Illuminate\Contracts\Queue\ShouldQueue;
10
-use Illuminate\Queue\InteractsWithQueue;
11 9
 use Illuminate\Support\Facades\DB;
12 10
 
13 11
 class CreateConnectedAccount

+ 1
- 9
app/Listeners/HandleTransactionImport.php 查看文件

@@ -10,8 +10,6 @@ use App\Models\Company;
10 10
 use App\Models\Setting\Currency;
11 11
 use App\Services\PlaidService;
12 12
 use App\Utilities\Currency\CurrencyAccessor;
13
-use Illuminate\Contracts\Queue\ShouldQueue;
14
-use Illuminate\Queue\InteractsWithQueue;
15 13
 use Illuminate\Support\Facades\DB;
16 14
 
17 15
 class HandleTransactionImport
@@ -39,16 +37,10 @@ class HandleTransactionImport
39 37
     public function processTransactionImport(StartTransactionImport $event): void
40 38
     {
41 39
         $company = $event->company;
42
-        $connectedBankAccountId = $event->connectedBankAccountId;
40
+        $connectedBankAccount = $event->connectedBankAccount;
43 41
         $selectedBankAccountId = $event->selectedBankAccountId;
44 42
         $startDate = $event->startDate;
45 43
 
46
-        $connectedBankAccount = ConnectedBankAccount::find($connectedBankAccountId);
47
-
48
-        if ($connectedBankAccount === null) {
49
-            return;
50
-        }
51
-
52 44
         $accessToken = $connectedBankAccount->access_token;
53 45
 
54 46
         if ($selectedBankAccountId === 'new') {

+ 63
- 45
app/Livewire/Company/Service/ConnectedAccount/ListInstitutions.php 查看文件

@@ -12,36 +12,33 @@ use App\Services\PlaidService;
12 12
 use Filament\Actions\Action;
13 13
 use Filament\Actions\Concerns\InteractsWithActions;
14 14
 use Filament\Actions\Contracts\HasActions;
15
+use Filament\Forms\Components\Checkbox;
15 16
 use Filament\Forms\Components\DatePicker;
17
+use Filament\Forms\Components\Placeholder;
16 18
 use Filament\Forms\Components\Select;
17 19
 use Filament\Forms\Concerns\InteractsWithForms;
18 20
 use Filament\Forms\Contracts\HasForms;
19
-use Filament\Forms\Form;
20
-use Filament\Forms\Get;
21 21
 use Filament\Notifications\Notification;
22
-use Filament\Support\Enums\MaxWidth;
22
+use Filament\Support\Enums\Alignment;
23 23
 use Illuminate\Contracts\View\View;
24
-use Illuminate\Database\Eloquent\Builder;
25 24
 use Illuminate\Database\Eloquent\Collection;
26
-use Illuminate\Database\Eloquent\Relations\HasMany;
27 25
 use Illuminate\Support\Facades\Auth;
28 26
 use Illuminate\Support\Facades\Log;
29
-use JsonException;
30 27
 use Livewire\Attributes\Computed;
31 28
 use Livewire\Attributes\On;
32 29
 use Livewire\Component;
33 30
 use RuntimeException;
34 31
 
35
-class ListInstitutions extends Component implements HasForms, HasActions
32
+class ListInstitutions extends Component implements HasActions, HasForms
36 33
 {
37
-    use InteractsWithForms;
38 34
     use InteractsWithActions;
35
+    use InteractsWithForms;
39 36
 
40 37
     protected PlaidService $plaidService;
41 38
 
42 39
     public User $user;
43 40
 
44
-    public ?ConnectedBankAccount $connectedBankAccount = null;
41
+    public string $modalWidth;
45 42
 
46 43
     public function boot(PlaidService $plaidService): void
47 44
     {
@@ -54,7 +51,7 @@ class ListInstitutions extends Component implements HasForms, HasActions
54 51
     }
55 52
 
56 53
     #[Computed]
57
-    public function connectedInstitutions(): Collection|array
54
+    public function connectedInstitutions(): Collection | array
58 55
     {
59 56
         return Institution::withWhereHas('connectedBankAccounts')
60 57
             ->get();
@@ -66,58 +63,60 @@ class ListInstitutions extends Component implements HasForms, HasActions
66 63
             ->link()
67 64
             ->icon('heroicon-o-cloud-arrow-down')
68 65
             ->label('Start Importing Transactions')
69
-            ->modalWidth(MaxWidth::TwoExtraLarge)
66
+            ->modalWidth(fn () => $this->modalWidth)
67
+            ->modalFooterActionsAlignment(fn () => $this->modalWidth === 'screen' ? Alignment::Center : Alignment::Start)
70 68
             ->stickyModalHeader()
71 69
             ->stickyModalFooter()
72
-            ->record($this->connectedBankAccount)
73
-            ->mountUsing(function (array $arguments, Form $form) {
74
-                $connectedAccountId = $arguments['connectedBankAccount'];
75
-
76
-                $this->connectedBankAccount = ConnectedBankAccount::find($connectedAccountId);
77
-
78
-                $form
79
-                    ->fill($this->connectedBankAccount->toArray())
80
-                    ->operation('edit')
81
-                    ->model($this->connectedBankAccount);
82
-            })
70
+            ->record(fn (array $arguments) => ConnectedBankAccount::find($arguments['connectedBankAccount']))
83 71
             ->form([
72
+                Placeholder::make('import_from')
73
+                    ->label('Import Transactions From')
74
+                    ->content(static fn (ConnectedBankAccount $connectedBankAccount): View => view(
75
+                        'components.actions.transaction-import-modal',
76
+                        compact('connectedBankAccount')
77
+                    )),
78
+                Placeholder::make('info')
79
+                    ->hiddenLabel()
80
+                    ->visible(static fn (ConnectedBankAccount $connectedBankAccount) => $connectedBankAccount->bank_account_id === null)
81
+                    ->content(static fn (ConnectedBankAccount $connectedBankAccount) => 'If ' . $connectedBankAccount->name . ' already has transactions for an existing account, select the account to import transactions into.'),
84 82
                 Select::make('bank_account_id')
85 83
                     ->label('Select Account')
86
-                    ->visible(static fn (?ConnectedBankAccount $connectedBankAccount) => $connectedBankAccount?->bank_account_id === null)
87
-                    ->options(fn () => $this->getBankAccountOptions())
88
-                    ->required()
89
-                    ->placeholder('Select an account to start importing transactions for.'),
84
+                    ->visible(static fn (ConnectedBankAccount $connectedBankAccount) => $connectedBankAccount->bank_account_id === null)
85
+                    ->options(fn (ConnectedBankAccount $connectedBankAccount) => $this->getBankAccountOptions($connectedBankAccount))
86
+                    ->required(),
90 87
                 DatePicker::make('start_date')
91 88
                     ->label('Start Date')
92 89
                     ->required()
93 90
                     ->placeholder('Select a start date for importing transactions.'),
94 91
             ])
95 92
             ->action(function (array $arguments, array $data, ConnectedBankAccount $connectedBankAccount) {
96
-                $connectedBankAccountId = $arguments['connectedBankAccount'];
97 93
                 $selectedBankAccountId = $data['bank_account_id'] ?? $connectedBankAccount->bank_account_id;
98 94
                 $startDate = $data['start_date'];
99 95
                 $company = $this->user->currentCompany;
100 96
 
101
-                StartTransactionImport::dispatch($company, $connectedBankAccountId, $selectedBankAccountId, $startDate);
97
+                StartTransactionImport::dispatch($company, $connectedBankAccount, $selectedBankAccountId, $startDate);
102 98
 
103 99
                 unset($this->connectedInstitutions);
104 100
             });
105 101
     }
106 102
 
107
-    public function getBankAccountOptions(): array
103
+    public function getBankAccountOptions(ConnectedBankAccount $connectedBankAccount): array
108 104
     {
109
-        $institutionId = $this->connectedBankAccount->institution_id ?? null;
110
-
111
-        $options = BankAccount::query()
112
-            ->where('company_id', $this->user->currentCompany->id)
113
-            ->when($institutionId, static fn($query) => $query->where('institution_id', $institutionId))
114
-            ->whereDoesntHave('connectedBankAccount')
115
-            ->with('account')
116
-            ->get()
117
-            ->pluck('account.name', 'id')
118
-            ->toArray();
119
-
120
-        return ['new' => 'New Account'] + $options;
105
+        $institutionId = $connectedBankAccount->institution_id ?? null;
106
+        $options = ['new' => 'New Account'];
107
+
108
+        if ($institutionId) {
109
+            $options += BankAccount::query()
110
+                ->where('company_id', $this->user->currentCompany->id)
111
+                ->where('institution_id', $institutionId)
112
+                ->whereDoesntHave('connectedBankAccount')
113
+                ->with('account')
114
+                ->get()
115
+                ->pluck('account.name', 'id')
116
+                ->toArray();
117
+        }
118
+
119
+        return $options;
121 120
     }
122 121
 
123 122
     public function stopImportingTransactions(): Action
@@ -137,7 +136,7 @@ class ListInstitutions extends Component implements HasForms, HasActions
137 136
 
138 137
                 if ($connectedBankAccount) {
139 138
                     $connectedBankAccount->update([
140
-                        'import_transactions' => !$connectedBankAccount->import_transactions,
139
+                        'import_transactions' => ! $connectedBankAccount->import_transactions,
141 140
                     ]);
142 141
                 }
143 142
 
@@ -150,9 +149,28 @@ class ListInstitutions extends Component implements HasForms, HasActions
150 149
         return Action::make('deleteBankConnection')
151 150
             ->iconButton()
152 151
             ->icon('heroicon-o-trash')
153
-            ->requiresConfirmation()
152
+            ->color('danger')
154 153
             ->modalHeading('Delete Bank Connection')
155
-            ->modalDescription('Deleting this bank connection will stop the import of transactions for all accounts associated with this bank. Existing transactions will remain unchanged.')
154
+            ->modalWidth(fn () => $this->modalWidth)
155
+            ->modalFooterActionsAlignment(fn () => $this->modalWidth === 'screen' ? Alignment::Center : Alignment::Start)
156
+            ->stickyModalHeader()
157
+            ->stickyModalFooter()
158
+            ->record(fn (array $arguments) => Institution::find($arguments['institution']))
159
+            ->form([
160
+                Placeholder::make('accounts')
161
+                    ->hiddenLabel()
162
+                    ->content(static fn (Institution $institution): View => view(
163
+                        'components.actions.delete-bank-connection-modal',
164
+                        compact('institution')
165
+                    )),
166
+                Placeholder::make('info')
167
+                    ->hiddenLabel()
168
+                    ->content('Deleting this bank connection will stop the import of transactions for all accounts associated with this bank. Existing transactions will remain unchanged.'),
169
+                Checkbox::make('confirm')
170
+                    ->label('Yes, I want to delete this bank connection.')
171
+                    ->markAsRequired(false)
172
+                    ->required(),
173
+            ])
156 174
             ->action(function (array $arguments) {
157 175
                 $institutionId = $arguments['institution'];
158 176
 
@@ -183,7 +201,7 @@ class ListInstitutions extends Component implements HasForms, HasActions
183 201
 
184 202
             $this->dispatch('initializeLink', $plaidLinkToken)->self();
185 203
         } catch (RuntimeException) {
186
-            Log::error("Error creating Plaid token.");
204
+            Log::error('Error creating Plaid token.');
187 205
 
188 206
             $this->sendErrorNotification("We're currently experiencing issues connecting your account. Please try again in a few moments.");
189 207
         }

+ 2
- 2
app/Livewire/UpdatePassword.php 查看文件

@@ -10,11 +10,11 @@ use Filament\Forms\Form;
10 10
 use Filament\Notifications\Notification;
11 11
 use Filament\Support\Exceptions\Halt;
12 12
 use Illuminate\Contracts\Auth\Authenticatable;
13
+use Illuminate\Contracts\View\View;
13 14
 use Illuminate\Database\Eloquent\Model;
14 15
 use Illuminate\Support\Facades\Hash;
15 16
 use Illuminate\Validation\Rules\Password;
16 17
 use Livewire\Component;
17
-use Illuminate\Contracts\View\View;
18 18
 use RuntimeException;
19 19
 
20 20
 /**
@@ -34,7 +34,7 @@ class UpdatePassword extends Component implements HasForms
34 34
         $this->fillForm();
35 35
     }
36 36
 
37
-    public function getUser(): Authenticatable|Model
37
+    public function getUser(): Authenticatable | Model
38 38
     {
39 39
         $user = Filament::auth()->user();
40 40
 

+ 2
- 2
app/Livewire/UpdateProfileInformation.php 查看文件

@@ -11,12 +11,12 @@ use Filament\Forms\Form;
11 11
 use Filament\Notifications\Notification;
12 12
 use Filament\Support\Exceptions\Halt;
13 13
 use Illuminate\Contracts\Auth\Authenticatable;
14
+use Illuminate\Contracts\View\View;
14 15
 use Illuminate\Database\Eloquent\Model;
15 16
 use Illuminate\Http\UploadedFile;
16 17
 use Illuminate\Support\Facades\Blade;
17 18
 use Illuminate\Support\HtmlString;
18 19
 use Livewire\Component;
19
-use Illuminate\Contracts\View\View;
20 20
 use RuntimeException;
21 21
 use Wallo\FilamentCompanies\Features;
22 22
 
@@ -37,7 +37,7 @@ class UpdateProfileInformation extends Component implements HasForms
37 37
         $this->fillForm();
38 38
     }
39 39
 
40
-    public function getUser(): Authenticatable|Model
40
+    public function getUser(): Authenticatable | Model
41 41
     {
42 42
         $user = Filament::auth()->user();
43 43
 

+ 1
- 2
app/Models/Accounting/AccountSubtype.php 查看文件

@@ -15,8 +15,8 @@ use Wallo\FilamentCompanies\FilamentCompanies;
15 15
 
16 16
 class AccountSubtype extends Model
17 17
 {
18
-    use HasFactory;
19 18
     use CompanyOwned;
19
+    use HasFactory;
20 20
 
21 21
     protected $table = 'account_subtypes';
22 22
 
@@ -35,7 +35,6 @@ class AccountSubtype extends Model
35 35
         'type' => AccountType::class,
36 36
     ];
37 37
 
38
-
39 38
     public function company(): BelongsTo
40 39
     {
41 40
         return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');

+ 1
- 2
app/Models/Banking/BankAccount.php 查看文件

@@ -2,7 +2,6 @@
2 2
 
3 3
 namespace App\Models\Banking;
4 4
 
5
-use App\Enums\BankAccountSubtype;
6 5
 use App\Enums\BankAccountType;
7 6
 use App\Models\Accounting\Account;
8 7
 use App\Traits\Blamable;
@@ -81,7 +80,7 @@ class BankAccount extends Model
81 80
     protected function mask(): Attribute
82 81
     {
83 82
         return Attribute::get(static function (mixed $value, array $attributes): ?string {
84
-            return $attributes['number'] ? '****' . substr($attributes['number'], -4) : null;
83
+            return $attributes['number'] ? '•••• ' . substr($attributes['number'], -4) : null;
85 84
         });
86 85
     }
87 86
 

+ 1
- 1
app/Models/Banking/ConnectedBankAccount.php 查看文件

@@ -62,7 +62,7 @@ class ConnectedBankAccount extends Model
62 62
     protected function maskedNumber(): Attribute
63 63
     {
64 64
         return Attribute::get(static function (mixed $value, array $attributes): ?string {
65
-            return $attributes['mask'] ? '****' . $attributes['mask'] : null;
65
+            return $attributes['mask'] ? '•••• ' . substr($attributes['mask'], -4) : null;
66 66
         });
67 67
     }
68 68
 

+ 34
- 34
app/Observers/AccountObserver.php 查看文件

@@ -55,19 +55,19 @@ class AccountObserver
55 55
     public function created(Account $account): void
56 56
     {
57 57
         //$account->histories()->create([
58
-           // 'company_id' => $account->company_id,
59
-          //  'account_id' => $account->id,
60
-           // 'type' => $account->type,
61
-          //  'name' => $account->name,
62
-          //  'number' => $account->number,
63
-           // 'currency_code' => $account->currency_code,
64
-         //   'opening_balance' => $account->opening_balance,
65
-         //   'balance' => $account->balance,
66
-         //   'exchange_rate' => $account->currency->rate,
67
-          //  'status' => AccountStatus::Open,
68
-          //  'actions' => ['account_created'],
69
-         //   'enabled' => $account->enabled,
70
-          //  'changed_by' => $account->created_by,
58
+        // 'company_id' => $account->company_id,
59
+        //  'account_id' => $account->id,
60
+        // 'type' => $account->type,
61
+        //  'name' => $account->name,
62
+        //  'number' => $account->number,
63
+        // 'currency_code' => $account->currency_code,
64
+        //   'opening_balance' => $account->opening_balance,
65
+        //   'balance' => $account->balance,
66
+        //   'exchange_rate' => $account->currency->rate,
67
+        //  'status' => AccountStatus::Open,
68
+        //  'actions' => ['account_created'],
69
+        //   'enabled' => $account->enabled,
70
+        //  'changed_by' => $account->created_by,
71 71
         //]);
72 72
     }
73 73
 
@@ -79,27 +79,27 @@ class AccountObserver
79 79
         //$actionsTaken = [];
80 80
 
81 81
         //foreach ($this->actions as $action => $attribute) {
82
-            //if ($account->isDirty($attribute)) {
83
-                //$actionsTaken[] = $action;
84
-           // }
82
+        //if ($account->isDirty($attribute)) {
83
+        //$actionsTaken[] = $action;
84
+        // }
85 85
         //}
86 86
 
87 87
         //if (count($actionsTaken) > 0) {
88
-            //$account->histories()->create([
89
-                //'company_id' => $account->company_id,
90
-               // 'account_id' => $account->id,
91
-               // 'type' => $account->getOriginal('type'),
92
-               // 'name' => $account->getOriginal('name'),
93
-               // 'number' => $account->getOriginal('number'),
94
-               // 'currency_code' => $account->getOriginal('currency_code'),
95
-               // 'opening_balance' => $account->getRawOriginal('opening_balance'),
96
-               // 'balance' => $account->getRawOriginal('balance'),
97
-               // 'exchange_rate' => $account->currency->getRawOriginal('rate'),
98
-               // 'status' => $account->getOriginal('status'),
99
-               // 'actions' => $actionsTaken,
100
-               // 'enabled' => $account->getOriginal('enabled'),
101
-               // 'changed_by' => $account->updated_by,
102
-            //]);
88
+        //$account->histories()->create([
89
+        //'company_id' => $account->company_id,
90
+        // 'account_id' => $account->id,
91
+        // 'type' => $account->getOriginal('type'),
92
+        // 'name' => $account->getOriginal('name'),
93
+        // 'number' => $account->getOriginal('number'),
94
+        // 'currency_code' => $account->getOriginal('currency_code'),
95
+        // 'opening_balance' => $account->getRawOriginal('opening_balance'),
96
+        // 'balance' => $account->getRawOriginal('balance'),
97
+        // 'exchange_rate' => $account->currency->getRawOriginal('rate'),
98
+        // 'status' => $account->getOriginal('status'),
99
+        // 'actions' => $actionsTaken,
100
+        // 'enabled' => $account->getOriginal('enabled'),
101
+        // 'changed_by' => $account->updated_by,
102
+        //]);
103 103
         //}
104 104
     }
105 105
 
@@ -133,8 +133,8 @@ class AccountObserver
133 133
         $defaultChartType = AccountType::CurrentAsset;
134 134
 
135 135
         //if ($account->type->isCreditCard()) {
136
-            //$defaultChartCategory = ChartCategory::Liability;
137
-            //$defaultChartType = ChartType::CurrentLiability;
136
+        //$defaultChartCategory = ChartCategory::Liability;
137
+        //$defaultChartType = ChartType::CurrentLiability;
138 138
         //}
139 139
 
140 140
         $subTypeId = $this->getSubTypeId($account->company_id, $defaultChartType);
@@ -170,7 +170,7 @@ class AccountObserver
170 170
             ->where('type', $type)
171 171
             ->first();
172 172
 
173
-        if (!$subType) {
173
+        if (! $subType) {
174 174
             $subType = AccountSubtype::where('company_id', $companyId)
175 175
                 ->where('type', $type)
176 176
                 ->first();

+ 1
- 3
app/Observers/BankAccountObserver.php 查看文件

@@ -2,12 +2,10 @@
2 2
 
3 3
 namespace App\Observers;
4 4
 
5
-use App\Enums\Accounting\AccountCategory;
6 5
 use App\Enums\Accounting\AccountType;
7 6
 use App\Models\Accounting\Account;
8 7
 use App\Models\Accounting\AccountSubtype;
9 8
 use App\Models\Banking\BankAccount;
10
-use Illuminate\Support\Facades\Log;
11 9
 
12 10
 class BankAccountObserver
13 11
 {
@@ -37,7 +35,7 @@ class BankAccountObserver
37 35
             ->where('type', $type)
38 36
             ->first();
39 37
 
40
-        if (!$subType) {
38
+        if (! $subType) {
41 39
             $subType = AccountSubtype::where('company_id', $companyId)
42 40
                 ->where('type', $type)
43 41
                 ->first();

+ 0
- 1
app/Providers/EventServiceProvider.php 查看文件

@@ -31,7 +31,6 @@ use App\Observers\CurrencyObserver;
31 31
 use Illuminate\Auth\Events\Registered;
32 32
 use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
33 33
 use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
34
-use Wallo\FilamentCompanies\Events\CompanyCreated;
35 34
 
36 35
 class EventServiceProvider extends ServiceProvider
37 36
 {

+ 9
- 9
app/Providers/MacroServiceProvider.php 查看文件

@@ -108,17 +108,17 @@ class MacroServiceProvider extends ServiceProvider
108 108
             $this
109 109
                 ->rules([
110 110
                     fn (Field $component): Closure => static function (string $attribute, $value, Closure $fail) use ($subtype, $component) {
111
-                    $subtype = $component->evaluate($subtype);
112
-                    $chartSubtype = AccountSubtype::find($subtype);
113
-                    $type = $chartSubtype->type;
111
+                        $subtype = $component->evaluate($subtype);
112
+                        $chartSubtype = AccountSubtype::find($subtype);
113
+                        $type = $chartSubtype->type;
114 114
 
115
-                    if (!AccountCode::isValidCode($value, $type)) {
116
-                        $message = AccountCode::getMessage($type);
115
+                        if (! AccountCode::isValidCode($value, $type)) {
116
+                            $message = AccountCode::getMessage($type);
117 117
 
118
-                        $fail($message);
119
-                    }
120
-                },
121
-            ]);
118
+                            $fail($message);
119
+                        }
120
+                    },
121
+                ]);
122 122
 
123 123
             return $this;
124 124
         });

+ 1
- 1
app/Services/PlaidService.php 查看文件

@@ -276,7 +276,7 @@ class PlaidService
276 276
         return $this->sendRequest('institutions/get_by_id', $data);
277 277
     }
278 278
 
279
-    public function syncTransactions(string $access_token, string|null $cursor = null, int $count = 100, array $options = []): object
279
+    public function syncTransactions(string $access_token, ?string $cursor = null, int $count = 100, array $options = []): object
280 280
     {
281 281
         $data = [
282 282
             'access_token' => $access_token,

+ 4
- 2
app/Utilities/Plaid/AccountTypeMapper.php 查看文件

@@ -7,7 +7,8 @@ use App\Enums\BankAccountType;
7 7
 
8 8
 class AccountTypeMapper
9 9
 {
10
-    public static function mapToEnums(string $plaidType, string $plaidSubtype): array {
10
+    public static function mapToEnums(string $plaidType, string $plaidSubtype): array
11
+    {
11 12
         $mappedType = self::mapType($plaidType);
12 13
         $mappedSubtype = self::mapSubtype($plaidType, $plaidSubtype);
13 14
 
@@ -17,7 +18,8 @@ class AccountTypeMapper
17 18
         ];
18 19
     }
19 20
 
20
-    private static function mapType(string $plaidType): BankAccountType {
21
+    private static function mapType(string $plaidType): BankAccountType
22
+    {
21 23
         return match ($plaidType) {
22 24
             'depository' => BankAccountType::Depository,
23 25
             'credit' => BankAccountType::Credit,

+ 22
- 22
composer.lock 查看文件

@@ -413,16 +413,16 @@
413 413
         },
414 414
         {
415 415
             "name": "aws/aws-sdk-php",
416
-            "version": "3.298.9",
416
+            "version": "3.299.1",
417 417
             "source": {
418 418
                 "type": "git",
419 419
                 "url": "https://github.com/aws/aws-sdk-php.git",
420
-                "reference": "db225c3a1c5dabfbb5071349cfb7e4c396c3d9ec"
420
+                "reference": "a0f87b8e8bfb9afd0ffd702fcda556b465eee457"
421 421
             },
422 422
             "dist": {
423 423
                 "type": "zip",
424
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/db225c3a1c5dabfbb5071349cfb7e4c396c3d9ec",
425
-                "reference": "db225c3a1c5dabfbb5071349cfb7e4c396c3d9ec",
424
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/a0f87b8e8bfb9afd0ffd702fcda556b465eee457",
425
+                "reference": "a0f87b8e8bfb9afd0ffd702fcda556b465eee457",
426 426
                 "shasum": ""
427 427
             },
428 428
             "require": {
@@ -502,9 +502,9 @@
502 502
             "support": {
503 503
                 "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
504 504
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
505
-                "source": "https://github.com/aws/aws-sdk-php/tree/3.298.9"
505
+                "source": "https://github.com/aws/aws-sdk-php/tree/3.299.1"
506 506
             },
507
-            "time": "2024-02-13T19:08:16+00:00"
507
+            "time": "2024-02-16T19:08:34+00:00"
508 508
         },
509 509
         {
510 510
             "name": "aws/aws-sdk-php-laravel",
@@ -1210,16 +1210,16 @@
1210 1210
         },
1211 1211
         {
1212 1212
             "name": "doctrine/dbal",
1213
-            "version": "3.8.1",
1213
+            "version": "3.8.2",
1214 1214
             "source": {
1215 1215
                 "type": "git",
1216 1216
                 "url": "https://github.com/doctrine/dbal.git",
1217
-                "reference": "c9ea252cdce4da324ede3d6c5913dd89f769afd2"
1217
+                "reference": "a19a1d05ca211f41089dffcc387733a6875196cb"
1218 1218
             },
1219 1219
             "dist": {
1220 1220
                 "type": "zip",
1221
-                "url": "https://api.github.com/repos/doctrine/dbal/zipball/c9ea252cdce4da324ede3d6c5913dd89f769afd2",
1222
-                "reference": "c9ea252cdce4da324ede3d6c5913dd89f769afd2",
1221
+                "url": "https://api.github.com/repos/doctrine/dbal/zipball/a19a1d05ca211f41089dffcc387733a6875196cb",
1222
+                "reference": "a19a1d05ca211f41089dffcc387733a6875196cb",
1223 1223
                 "shasum": ""
1224 1224
             },
1225 1225
             "require": {
@@ -1303,7 +1303,7 @@
1303 1303
             ],
1304 1304
             "support": {
1305 1305
                 "issues": "https://github.com/doctrine/dbal/issues",
1306
-                "source": "https://github.com/doctrine/dbal/tree/3.8.1"
1306
+                "source": "https://github.com/doctrine/dbal/tree/3.8.2"
1307 1307
             },
1308 1308
             "funding": [
1309 1309
                 {
@@ -1319,7 +1319,7 @@
1319 1319
                     "type": "tidelift"
1320 1320
                 }
1321 1321
             ],
1322
-            "time": "2024-02-03T17:33:49+00:00"
1322
+            "time": "2024-02-12T18:36:36+00:00"
1323 1323
         },
1324 1324
         {
1325 1325
             "name": "doctrine/deprecations",
@@ -3220,16 +3220,16 @@
3220 3220
         },
3221 3221
         {
3222 3222
             "name": "laravel/socialite",
3223
-            "version": "v5.12.0",
3223
+            "version": "v5.12.1",
3224 3224
             "source": {
3225 3225
                 "type": "git",
3226 3226
                 "url": "https://github.com/laravel/socialite.git",
3227
-                "reference": "ffeeb2cdf723b4c88b25479968e2d3a61a83dbe5"
3227
+                "reference": "7dae1b072573809f32ab6dcf4aebb57c8b3e8acf"
3228 3228
             },
3229 3229
             "dist": {
3230 3230
                 "type": "zip",
3231
-                "url": "https://api.github.com/repos/laravel/socialite/zipball/ffeeb2cdf723b4c88b25479968e2d3a61a83dbe5",
3232
-                "reference": "ffeeb2cdf723b4c88b25479968e2d3a61a83dbe5",
3231
+                "url": "https://api.github.com/repos/laravel/socialite/zipball/7dae1b072573809f32ab6dcf4aebb57c8b3e8acf",
3232
+                "reference": "7dae1b072573809f32ab6dcf4aebb57c8b3e8acf",
3233 3233
                 "shasum": ""
3234 3234
             },
3235 3235
             "require": {
@@ -3286,7 +3286,7 @@
3286 3286
                 "issues": "https://github.com/laravel/socialite/issues",
3287 3287
                 "source": "https://github.com/laravel/socialite"
3288 3288
             },
3289
-            "time": "2024-02-11T18:29:55+00:00"
3289
+            "time": "2024-02-16T08:58:20+00:00"
3290 3290
         },
3291 3291
         {
3292 3292
             "name": "laravel/tinker",
@@ -4231,16 +4231,16 @@
4231 4231
         },
4232 4232
         {
4233 4233
             "name": "matomo/device-detector",
4234
-            "version": "6.2.1",
4234
+            "version": "6.3.0",
4235 4235
             "source": {
4236 4236
                 "type": "git",
4237 4237
                 "url": "https://github.com/matomo-org/device-detector.git",
4238
-                "reference": "19138b0c4b9ddf4ffd8e423d6af3764b7317cb0b"
4238
+                "reference": "35efad75b31f2596701834d19f097497909572a4"
4239 4239
             },
4240 4240
             "dist": {
4241 4241
                 "type": "zip",
4242
-                "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/19138b0c4b9ddf4ffd8e423d6af3764b7317cb0b",
4243
-                "reference": "19138b0c4b9ddf4ffd8e423d6af3764b7317cb0b",
4242
+                "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/35efad75b31f2596701834d19f097497909572a4",
4243
+                "reference": "35efad75b31f2596701834d19f097497909572a4",
4244 4244
                 "shasum": ""
4245 4245
             },
4246 4246
             "require": {
@@ -4296,7 +4296,7 @@
4296 4296
                 "source": "https://github.com/matomo-org/matomo",
4297 4297
                 "wiki": "https://dev.matomo.org/"
4298 4298
             },
4299
-            "time": "2024-01-05T09:03:21+00:00"
4299
+            "time": "2024-02-16T16:26:57+00:00"
4300 4300
         },
4301 4301
         {
4302 4302
             "name": "monolog/monolog",

+ 2
- 2
config/chart-of-accounts.php 查看文件

@@ -382,7 +382,7 @@ return [
382 382
             ],
383 383
         ],
384 384
         'contra_expense' => [
385
-             'Contra Expenses' => [
385
+            'Contra Expenses' => [
386 386
                 'description' => 'Expenses that are deducted from gross expenses to arrive at net expenses. This includes purchase discounts, returns, and allowances.',
387 387
                 'multi_currency' => false,
388 388
                 'base_code' => '5900',
@@ -394,7 +394,7 @@ return [
394 394
                         'description' => 'The amount of money deducted from purchases due to discounts offered by suppliers for early payment or other reasons.',
395 395
                     ],
396 396
                 ],
397
-             ],
397
+            ],
398 398
         ],
399 399
         'uncategorized_expense' => [
400 400
             'Uncategorized Expense' => [

+ 159
- 0
config/livewire.php 查看文件

@@ -0,0 +1,159 @@
1
+<?php
2
+
3
+return [
4
+
5
+    /*
6
+    |---------------------------------------------------------------------------
7
+    | Class Namespace
8
+    |---------------------------------------------------------------------------
9
+    |
10
+    | This value sets the root class namespace for Livewire component classes in
11
+    | your application. This value will change where component auto-discovery
12
+    | finds components. It's also referenced by the file creation commands.
13
+    |
14
+    */
15
+
16
+    'class_namespace' => 'App\\Livewire',
17
+
18
+    /*
19
+    |---------------------------------------------------------------------------
20
+    | View Path
21
+    |---------------------------------------------------------------------------
22
+    |
23
+    | This value is used to specify where Livewire component Blade templates are
24
+    | stored when running file creation commands like `artisan make:livewire`.
25
+    | It is also used if you choose to omit a component's render() method.
26
+    |
27
+    */
28
+
29
+    'view_path' => resource_path('views/livewire'),
30
+
31
+    /*
32
+    |---------------------------------------------------------------------------
33
+    | Layout
34
+    |---------------------------------------------------------------------------
35
+    | The view that will be used as the layout when rendering a single component
36
+    | as an entire page via `Route::get('/post/create', CreatePost::class);`.
37
+    | In this case, the view returned by CreatePost will render into $slot.
38
+    |
39
+    */
40
+
41
+    'layout' => 'components.layouts.app',
42
+
43
+    /*
44
+    |---------------------------------------------------------------------------
45
+    | Lazy Loading Placeholder
46
+    |---------------------------------------------------------------------------
47
+    | Livewire allows you to lazy load components that would otherwise slow down
48
+    | the initial page load. Every component can have a custom placeholder or
49
+    | you can define the default placeholder view for all components below.
50
+    |
51
+    */
52
+
53
+    'lazy_placeholder' => null,
54
+
55
+    /*
56
+    |---------------------------------------------------------------------------
57
+    | Temporary File Uploads
58
+    |---------------------------------------------------------------------------
59
+    |
60
+    | Livewire handles file uploads by storing uploads in a temporary directory
61
+    | before the file is stored permanently. All file uploads are directed to
62
+    | a global endpoint for temporary storage. You may configure this below:
63
+    |
64
+    */
65
+
66
+    'temporary_file_upload' => [
67
+        'disk' => null,        // Example: 'local', 's3'              | Default: 'default'
68
+        'rules' => null,       // Example: ['file', 'mimes:png,jpg']  | Default: ['required', 'file', 'max:12288'] (12MB)
69
+        'directory' => null,   // Example: 'tmp'                      | Default: 'livewire-tmp'
70
+        'middleware' => null,  // Example: 'throttle:5,1'             | Default: 'throttle:60,1'
71
+        'preview_mimes' => [   // Supported file types for temporary pre-signed file URLs...
72
+            'png', 'gif', 'bmp', 'svg', 'wav', 'mp4',
73
+            'mov', 'avi', 'wmv', 'mp3', 'm4a',
74
+            'jpg', 'jpeg', 'mpga', 'webp', 'wma',
75
+        ],
76
+        'max_upload_time' => 5, // Max duration (in minutes) before an upload is invalidated...
77
+    ],
78
+
79
+    /*
80
+    |---------------------------------------------------------------------------
81
+    | Render On Redirect
82
+    |---------------------------------------------------------------------------
83
+    |
84
+    | This value determines if Livewire will run a component's `render()` method
85
+    | after a redirect has been triggered using something like `redirect(...)`
86
+    | Setting this to true will render the view once more before redirecting
87
+    |
88
+    */
89
+
90
+    'render_on_redirect' => false,
91
+
92
+    /*
93
+    |---------------------------------------------------------------------------
94
+    | Eloquent Model Binding
95
+    |---------------------------------------------------------------------------
96
+    |
97
+    | Previous versions of Livewire supported binding directly to eloquent model
98
+    | properties using wire:model by default. However, this behavior has been
99
+    | deemed too "magical" and has therefore been put under a feature flag.
100
+    |
101
+    */
102
+
103
+    'legacy_model_binding' => false,
104
+
105
+    /*
106
+    |---------------------------------------------------------------------------
107
+    | Auto-inject Frontend Assets
108
+    |---------------------------------------------------------------------------
109
+    |
110
+    | By default, Livewire automatically injects its JavaScript and CSS into the
111
+    | <head> and <body> of pages containing Livewire components. By disabling
112
+    | this behavior, you need to use @livewireStyles and @livewireScripts.
113
+    |
114
+    */
115
+
116
+    'inject_assets' => true,
117
+
118
+    /*
119
+    |---------------------------------------------------------------------------
120
+    | Navigate (SPA mode)
121
+    |---------------------------------------------------------------------------
122
+    |
123
+    | By adding `wire:navigate` to links in your Livewire application, Livewire
124
+    | will prevent the default link handling and instead request those pages
125
+    | via AJAX, creating an SPA-like effect. Configure this behavior here.
126
+    |
127
+    */
128
+
129
+    'navigate' => [
130
+        'show_progress_bar' => true,
131
+        'progress_bar_color' => '#2299dd',
132
+    ],
133
+
134
+    /*
135
+    |---------------------------------------------------------------------------
136
+    | HTML Morph Markers
137
+    |---------------------------------------------------------------------------
138
+    |
139
+    | Livewire intelligently "morphs" existing HTML into the newly rendered HTML
140
+    | after each update. To make this process more reliable, Livewire injects
141
+    | "markers" into the rendered Blade surrounding @if, @class & @foreach.
142
+    |
143
+    */
144
+
145
+    'inject_morph_markers' => true,
146
+
147
+    /*
148
+    |---------------------------------------------------------------------------
149
+    | Pagination Theme
150
+    |---------------------------------------------------------------------------
151
+    |
152
+    | When enabling Livewire's pagination feature by using the `WithPagination`
153
+    | trait, Livewire will use Tailwind templates to render pagination views
154
+    | on the page. If you want Bootstrap CSS, you can specify: "bootstrap"
155
+    |
156
+    */
157
+
158
+    'pagination_theme' => 'tailwind',
159
+];

+ 1
- 1
config/services.php 查看文件

@@ -46,5 +46,5 @@ return [
46 46
         'client_id' => env('PLAID_CLIENT_ID'),
47 47
         'client_secret' => env('PLAID_CLIENT_SECRET'),
48 48
         'environment' => env('PLAID_ENVIRONMENT', 'sandbox'),
49
-    ]
49
+    ],
50 50
 ];

+ 0
- 1
database/factories/Accounting/AccountFactory.php 查看文件

@@ -10,7 +10,6 @@ use Illuminate\Database\Eloquent\Factories\Factory;
10 10
  */
11 11
 class AccountFactory extends Factory
12 12
 {
13
-
14 13
     /**
15 14
      * The name of the factory's corresponding model.
16 15
      */

+ 17
- 14
package-lock.json 查看文件

@@ -411,9 +411,9 @@
411 411
             }
412 412
         },
413 413
         "node_modules/@jridgewell/resolve-uri": {
414
-            "version": "3.1.1",
415
-            "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
416
-            "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
414
+            "version": "3.1.2",
415
+            "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
416
+            "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
417 417
             "dev": true,
418 418
             "engines": {
419 419
                 "node": ">=6.0.0"
@@ -656,9 +656,9 @@
656 656
             }
657 657
         },
658 658
         "node_modules/browserslist": {
659
-            "version": "4.22.3",
660
-            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.3.tgz",
661
-            "integrity": "sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A==",
659
+            "version": "4.23.0",
660
+            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
661
+            "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
662 662
             "dev": true,
663 663
             "funding": [
664 664
                 {
@@ -675,8 +675,8 @@
675 675
                 }
676 676
             ],
677 677
             "dependencies": {
678
-                "caniuse-lite": "^1.0.30001580",
679
-                "electron-to-chromium": "^1.4.648",
678
+                "caniuse-lite": "^1.0.30001587",
679
+                "electron-to-chromium": "^1.4.668",
680 680
                 "node-releases": "^2.0.14",
681 681
                 "update-browserslist-db": "^1.0.13"
682 682
             },
@@ -845,9 +845,9 @@
845 845
             "dev": true
846 846
         },
847 847
         "node_modules/electron-to-chromium": {
848
-            "version": "1.4.668",
849
-            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.668.tgz",
850
-            "integrity": "sha512-ZOBocMYCehr9W31+GpMclR+KBaDZOoAEabLdhpZ8oU1JFDwIaFY0UDbpXVEUFc0BIP2O2Qn3rkfCjQmMR4T/bQ==",
848
+            "version": "1.4.673",
849
+            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.673.tgz",
850
+            "integrity": "sha512-zjqzx4N7xGdl5468G+vcgzDhaHkaYgVcf9MqgexcTqsl2UHSCmOj/Bi3HAprg4BZCpC7HyD8a6nZl6QAZf72gw==",
851 851
             "dev": true
852 852
         },
853 853
         "node_modules/emoji-regex": {
@@ -1551,12 +1551,15 @@
1551 1551
             }
1552 1552
         },
1553 1553
         "node_modules/postcss-load-config/node_modules/lilconfig": {
1554
-            "version": "3.0.0",
1555
-            "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz",
1556
-            "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==",
1554
+            "version": "3.1.0",
1555
+            "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.0.tgz",
1556
+            "integrity": "sha512-p3cz0JV5vw/XeouBU3Ldnp+ZkBjE+n8ydJ4mcwBrOiXXPqNlrzGBqWs9X4MWF7f+iKUBu794Y8Hh8yawiJbCjw==",
1557 1557
             "dev": true,
1558 1558
             "engines": {
1559 1559
                 "node": ">=14"
1560
+            },
1561
+            "funding": {
1562
+                "url": "https://github.com/sponsors/antonk52"
1560 1563
             }
1561 1564
         },
1562 1565
         "node_modules/postcss-nested": {

+ 6
- 0
resources/views/components/actions/delete-bank-connection-modal.blade.php 查看文件

@@ -0,0 +1,6 @@
1
+<span>Are you sure you want to delete your {{ $institution->name }} connection? Deleting this bank connection will remove the following connected accounts:</span>
2
+<ul class="list-disc list-inside p-2">
3
+    @foreach($institution->connectedBankAccounts as $connectedBankAccount)
4
+        <li>{{ $connectedBankAccount->name }}</li>
5
+    @endforeach
6
+</ul>

+ 18
- 0
resources/views/components/actions/transaction-import-modal.blade.php 查看文件

@@ -0,0 +1,18 @@
1
+<div class="rounded-lg bg-primary-300/10 shadow-sm ring-1 ring-gray-950/10 dark:ring-white/20 overflow-hidden">
2
+    <div class="flex items-center p-4 gap-x-2">
3
+        @if($connectedBankAccount->institution->logo_url)
4
+            <img src="{{ $connectedBankAccount->institution->logo_url }}" alt="{{ $connectedBankAccount->institution->name }}" class="h-10">
5
+        @else
6
+            <div class="flex-shrink-0 bg-platinum p-2 rounded-full dark:bg-gray-500/20">
7
+                <x-filament::icon
8
+                    icon="heroicon-o-building-library"
9
+                    class="h-6 w-6 text-gray-500 dark:text-gray-400"
10
+                />
11
+            </div>
12
+        @endif
13
+        <div>
14
+            <p class="text-sm font-medium leading-6 text-gray-900 dark:text-white">{{ $connectedBankAccount->institution->name }}</p>
15
+            <p class="text-sm leading-6 text-gray-600 dark:text-gray-200">{{ ucwords($connectedBankAccount->subtype) }} {{ $connectedBankAccount->masked_number }}</p>
16
+        </div>
17
+    </div>
18
+</div>

+ 35
- 27
resources/views/livewire/company/service/connected-account/list-institutions.blade.php 查看文件

@@ -2,7 +2,7 @@
2 2
     <div class="grid grid-cols-1 gap-4">
3 3
         @forelse($this->connectedInstitutions as $institution) {{-- Group connected accounts by institution --}}
4 4
             <section class="connected-account-section overflow-hidden rounded-xl bg-white shadow-sm ring-1 ring-gray-950/5 dark:bg-gray-900 dark:ring-white/10">
5
-                <header class="connected-account-header bg-primary-300/10 flex flex-col gap-3 overflow-hidden sm:flex-row sm:items-center px-6 py-4">
5
+                <header class="connected-account-header bg-primary-300/10 px-6 py-4 flex flex-col sm:flex-row sm:items-center gap-3">
6 6
                     @if($institution->logo_url === null)
7 7
                         <div class="flex-shrink-0 bg-platinum p-2 rounded-full dark:bg-gray-500/20">
8 8
                             <x-filament::icon
@@ -11,16 +11,16 @@
11 11
                             />
12 12
                         </div>
13 13
                     @else
14
-                        <img src="{{ $institution->logo_url }}" alt="{{ $institution->name }}" class="h-10">
14
+                        <img src="{{ $institution->logo_url }}" alt="{{ $institution->name }}" class="h-10 object-contain object-left">
15 15
                     @endif
16 16
 
17
-                    <div class="grid flex-1 gap-y-1">
18
-                        <h3 class="connected-account-section-header-heading text-lg font-semibold leading-[1.4] text-gray-950 dark:text-white">
17
+                    <div class="flex-auto">
18
+                        <h3 class="connected-account-section-header-heading text-lg font-semibold leading-6 text-gray-950 dark:text-white">
19 19
                             {{ $institution->name }}
20 20
                         </h3>
21 21
 
22 22
                         {{-- Eventually we will need to assert last updated time based on when the last time one of the accounts for the institution last has transactions imported --}}
23
-                        <p class="connected-account-section-header-description text-sm text-gray-500 dark:text-gray-400">
23
+                        <p class="connected-account-section-header-description text-sm leading-6 text-gray-500 dark:text-gray-400">
24 24
                             {{ __('Last Updated') }} {{ $institution->updated_at->diffForHumans() }}
25 25
                         </p>
26 26
                     </div>
@@ -29,30 +29,28 @@
29 29
                 </header>
30 30
 
31 31
                 @foreach($institution->connectedBankAccounts as $connectedBankAccount)
32
-                    <div class="border-t-2 border-gray-200 dark:border-white/10">
33
-                        <div class="p-6">
34
-                            <div class="flex justify-between items-start">
35
-                                <div class="flex flex-col space-y-2">
36
-                                    <span class="account-name text-base font-medium text-gray-900 dark:text-white">{{ $connectedBankAccount->name }}</span>
37
-                                    <span class="account-type text-sm text-gray-600 dark:text-gray-200">{{  ucwords($connectedBankAccount->subtype) }} {{ $connectedBankAccount->masked_number }}</span>
38
-                                </div>
39
-
40
-                                @if($connectedBankAccount->bankAccount?->account)
41
-                                    <div class="account-balance flex justify-between text-base text-gray-700 dark:text-gray-200 space-x-1">
42
-                                        <strong>@money($connectedBankAccount->bankAccount->account->ending_balance, $connectedBankAccount->bankAccount->account->currency_code, true)</strong>
43
-                                        <p>{{ $connectedBankAccount->bankAccount->account->currency_code }}</p>
44
-                                    </div>
45
-                                @endif
32
+                    <div class="border-t-2 border-gray-200 dark:border-white/10 px-6 py-4">
33
+                        <div class="flex flex-col sm:flex-row items-start gap-y-2">
34
+                            <div class="grid flex-auto gap-y-2">
35
+                                <span class="account-name text-base font-medium leading-6 text-gray-900 dark:text-white">{{ $connectedBankAccount->name }}</span>
36
+                                <span class="account-type text-sm leading-6 text-gray-600 dark:text-gray-200">{{  ucwords($connectedBankAccount->subtype) }} {{ $connectedBankAccount->masked_number }}</span>
46 37
                             </div>
47 38
 
48
-                            {{-- Add the toggle button to import transactions or not --}}
49
-                            <div class="mt-4 flex items-center space-x-2">
50
-                                @if($connectedBankAccount->import_transactions)
51
-                                    {{ ($this->stopImportingTransactions)(['connectedBankAccount' => $connectedBankAccount->id]) }}
52
-                                @else
53
-                                    {{ ($this->startImportingTransactions)(['connectedBankAccount' => $connectedBankAccount->id]) }}
54
-                                @endif
55
-                            </div>
39
+                            @if($connectedBankAccount->bankAccount?->account)
40
+                                <div class="account-balance flex text-base leading-6 text-gray-700 dark:text-gray-200 space-x-1">
41
+                                    <strong>@money($connectedBankAccount->bankAccount->account->ending_balance, $connectedBankAccount->bankAccount->account->currency_code, true)</strong>
42
+                                    <p>{{ $connectedBankAccount->bankAccount->account->currency_code }}</p>
43
+                                </div>
44
+                            @endif
45
+                        </div>
46
+
47
+                        {{-- Add the toggle button to import transactions or not --}}
48
+                        <div class="mt-4">
49
+                            @if($connectedBankAccount->import_transactions)
50
+                                {{ ($this->stopImportingTransactions)(['connectedBankAccount' => $connectedBankAccount->id]) }}
51
+                            @else
52
+                                {{ ($this->startImportingTransactions)(['connectedBankAccount' => $connectedBankAccount->id]) }}
53
+                            @endif
56 54
                         </div>
57 55
                     </div>
58 56
                 @endforeach
@@ -96,6 +94,16 @@
96 94
     {{-- Initialize Plaid Link --}}
97 95
     @script
98 96
     <script>
97
+        let data = Alpine.reactive({ windowWidth: 'max-w-2xl' });
98
+
99
+        Alpine.effect(() => {
100
+            $wire.$set('modalWidth', data.windowWidth);
101
+        });
102
+
103
+        window.addEventListener('resize', () => {
104
+            data.windowWidth = window.innerWidth <= 480 ? 'screen' : 'max-w-2xl';
105
+        });
106
+
99 107
         $wire.on('initializeLink', token => {
100 108
             const handler = Plaid.create({
101 109
                 token: token,

正在加载...
取消
保存