Andrew Wallo 7 kuukautta sitten
vanhempi
commit
368181011f
31 muutettua tiedostoa jossa 1393 lisäystä ja 515 poistoa
  1. 3
    1
      app/DTO/DocumentDTO.php
  2. 1
    2
      app/DTO/DocumentPreviewDTO.php
  3. 1
    11
      app/Enums/Setting/DocumentType.php
  4. 1
    1
      app/Filament/Company/Clusters/Settings/Pages/Invoice.php
  5. 273
    0
      app/Filament/Company/Clusters/Settings/Resources/DocumentDefaultResource.php
  6. 11
    0
      app/Filament/Company/Clusters/Settings/Resources/DocumentDefaultResource/Pages/CreateDocumentDefault.php
  7. 19
    0
      app/Filament/Company/Clusters/Settings/Resources/DocumentDefaultResource/Pages/EditDocumentDefault.php
  8. 25
    0
      app/Filament/Company/Clusters/Settings/Resources/DocumentDefaultResource/Pages/ListDocumentDefaults.php
  9. 11
    0
      app/Filament/Infolists/Components/DocumentPreview.php
  10. 9
    1
      app/Http/Middleware/Authenticate.php
  11. 24
    0
      app/Http/Responses/LoginRedirectResponse.php
  12. 0
    15
      app/Http/Responses/LoginResponse.php
  13. 13
    2
      app/Listeners/CreateEmployeeContact.php
  14. 18
    1
      app/Models/Company.php
  15. 10
    4
      app/Models/Setting/DocumentDefault.php
  16. 7
    0
      app/Models/User.php
  17. 2
    1
      app/Providers/AppServiceProvider.php
  18. 1
    0
      app/Providers/Filament/CompanyPanelProvider.php
  19. 12
    1
      app/Providers/Filament/UserPanelProvider.php
  20. 0
    1
      bootstrap/providers.php
  21. 215
    216
      composer.lock
  22. 1
    1
      database/factories/Setting/DocumentDefaultFactory.php
  23. 269
    99
      package-lock.json
  24. 3
    2
      resources/data/lang/en.json
  25. 4
    1
      resources/views/components/company/invoice/container.blade.php
  26. 19
    0
      resources/views/components/icons/document-header-decoration.blade.php
  27. 5
    10
      resources/views/filament/company/components/invoice-layouts/classic.blade.php
  28. 5
    145
      resources/views/filament/infolists/components/document-preview.blade.php
  29. 148
    0
      resources/views/filament/infolists/components/document-templates/classic.blade.php
  30. 138
    0
      resources/views/filament/infolists/components/document-templates/default.blade.php
  31. 145
    0
      resources/views/filament/infolists/components/document-templates/modern.blade.php

+ 3
- 1
app/DTO/DocumentDTO.php Näytä tiedosto

@@ -44,7 +44,9 @@ readonly class DocumentDTO
44 44
     public static function fromModel(Document $document): self
45 45
     {
46 46
         /** @var DocumentDefault $settings */
47
-        $settings = $document->company->defaultInvoice;
47
+        $settings = $document->company->documentDefaults()
48
+            ->type($document::documentType())
49
+            ->first();
48 50
 
49 51
         $currencyCode = $document->currency_code ?? CurrencyAccessor::getDefaultCurrency();
50 52
 

+ 1
- 2
app/DTO/DocumentPreviewDTO.php Näytä tiedosto

@@ -2,7 +2,6 @@
2 2
 
3 3
 namespace App\DTO;
4 4
 
5
-use App\Enums\Accounting\DocumentType;
6 5
 use App\Enums\Setting\Font;
7 6
 use App\Enums\Setting\PaymentTerms;
8 7
 use App\Models\Setting\DocumentDefault;
@@ -35,7 +34,7 @@ readonly class DocumentPreviewDTO extends DocumentDTO
35 34
             company: CompanyDTO::fromModel($company),
36 35
             client: ClientPreviewDTO::fake(),
37 36
             lineItems: LineItemPreviewDTO::fakeItems(),
38
-            label: DocumentType::Invoice->getLabels(),
37
+            label: $settings->type->getLabels(),
39 38
             columnLabel: self::generateColumnLabels($settings, $data),
40 39
             accentColor: $data['accent_color'] ?? $settings->accent_color ?? '#000000',
41 40
             showLogo: $data['show_logo'] ?? $settings->show_logo ?? true,

+ 1
- 11
app/Enums/Setting/DocumentType.php Näytä tiedosto

@@ -2,10 +2,9 @@
2 2
 
3 3
 namespace App\Enums\Setting;
4 4
 
5
-use Filament\Support\Contracts\HasIcon;
6 5
 use Filament\Support\Contracts\HasLabel;
7 6
 
8
-enum DocumentType: string implements HasIcon, HasLabel
7
+enum DocumentType: string implements HasLabel
9 8
 {
10 9
     case Invoice = 'invoice';
11 10
     case Bill = 'bill';
@@ -17,13 +16,4 @@ enum DocumentType: string implements HasIcon, HasLabel
17 16
     {
18 17
         return $this->name;
19 18
     }
20
-
21
-    public function getIcon(): ?string
22
-    {
23
-        return match ($this->value) {
24
-            self::Invoice->value => 'heroicon-o-document-duplicate',
25
-            self::Bill->value => 'heroicon-o-clipboard-document-list',
26
-            self::Estimate->value => 'heroicon-o-currency-dollar',
27
-        };
28
-    }
29 19
 }

+ 1
- 1
app/Filament/Company/Clusters/Settings/Pages/Invoice.php Näytä tiedosto

@@ -2,7 +2,7 @@
2 2
 
3 3
 namespace App\Filament\Company\Clusters\Settings\Pages;
4 4
 
5
-use App\Enums\Setting\DocumentType;
5
+use App\Enums\Accounting\DocumentType;
6 6
 use App\Enums\Setting\Font;
7 7
 use App\Enums\Setting\PaymentTerms;
8 8
 use App\Enums\Setting\Template;

+ 273
- 0
app/Filament/Company/Clusters/Settings/Resources/DocumentDefaultResource.php Näytä tiedosto

@@ -0,0 +1,273 @@
1
+<?php
2
+
3
+namespace App\Filament\Company\Clusters\Settings\Resources;
4
+
5
+use App\Enums\Setting\Font;
6
+use App\Enums\Setting\PaymentTerms;
7
+use App\Enums\Setting\Template;
8
+use App\Filament\Company\Clusters\Settings;
9
+use App\Filament\Company\Clusters\Settings\Resources\DocumentDefaultResource\Pages;
10
+use App\Models\Setting\DocumentDefault;
11
+use App\Models\Setting\DocumentDefault as InvoiceModel;
12
+use Filament\Forms;
13
+use Filament\Forms\Components\Component;
14
+use Filament\Forms\Form;
15
+use Filament\Forms\Get;
16
+use Filament\Forms\Set;
17
+use Filament\Resources\Resource;
18
+use Filament\Tables;
19
+use Filament\Tables\Table;
20
+use Illuminate\Support\Facades\Auth;
21
+use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
22
+
23
+class DocumentDefaultResource extends Resource
24
+{
25
+    protected static ?string $model = DocumentDefault::class;
26
+
27
+    protected static ?string $cluster = Settings::class;
28
+
29
+    public static function form(Form $form): Form
30
+    {
31
+        return $form
32
+            ->live()
33
+            ->schema([
34
+                self::getGeneralSection(),
35
+                self::getContentSection(),
36
+                self::getTemplateSection(),
37
+            ]);
38
+    }
39
+
40
+    public static function getGeneralSection(): Forms\Components\Component
41
+    {
42
+        return Forms\Components\Section::make('General')
43
+            ->schema([
44
+                Forms\Components\TextInput::make('number_prefix')
45
+                    ->localizeLabel()
46
+                    ->nullable(),
47
+                Forms\Components\Select::make('number_digits')
48
+                    ->softRequired()
49
+                    ->localizeLabel()
50
+                    ->options(InvoiceModel::availableNumberDigits()),
51
+                Forms\Components\TextInput::make('number_next')
52
+                    ->softRequired()
53
+                    ->localizeLabel()
54
+                    ->mask(static function (Get $get) {
55
+                        return str_repeat('9', $get('number_digits'));
56
+                    })
57
+                    ->hint(function (Get $get, $state, DocumentDefault $record) {
58
+                        $number_prefix = $get('number_prefix');
59
+                        $number_digits = $get('number_digits');
60
+                        $number_next = $state;
61
+
62
+                        return $record->getNumberNext(true, true, $number_prefix, $number_digits, $number_next);
63
+                    }),
64
+                Forms\Components\Select::make('payment_terms')
65
+                    ->softRequired()
66
+                    ->localizeLabel()
67
+                    ->options(PaymentTerms::class),
68
+            ])->columns();
69
+    }
70
+
71
+    public static function getContentSection(): Forms\Components\Component
72
+    {
73
+        return Forms\Components\Section::make('Content')
74
+            ->schema([
75
+                Forms\Components\TextInput::make('header')
76
+                    ->localizeLabel()
77
+                    ->nullable(),
78
+                Forms\Components\TextInput::make('subheader')
79
+                    ->localizeLabel()
80
+                    ->nullable(),
81
+                Forms\Components\Textarea::make('terms')
82
+                    ->localizeLabel()
83
+                    ->nullable(),
84
+                Forms\Components\Textarea::make('footer')
85
+                    ->localizeLabel('Footer')
86
+                    ->nullable(),
87
+            ])->columns();
88
+    }
89
+
90
+    public static function getTemplateSection(): Component
91
+    {
92
+        return Forms\Components\Section::make('Template')
93
+            ->description('Choose the template and edit the column names.')
94
+            ->schema([
95
+                Forms\Components\Grid::make(1)
96
+                    ->schema([
97
+                        Forms\Components\FileUpload::make('logo')
98
+                            ->openable()
99
+                            ->maxSize(1024)
100
+                            ->localizeLabel()
101
+                            ->visibility('public')
102
+                            ->disk('public')
103
+                            ->directory('logos/document')
104
+                            ->imageResizeMode('contain')
105
+                            ->imageCropAspectRatio('3:2')
106
+                            ->panelAspectRatio('3:2')
107
+                            ->panelLayout('integrated')
108
+                            ->removeUploadedFileButtonPosition('center bottom')
109
+                            ->uploadButtonPosition('center bottom')
110
+                            ->uploadProgressIndicatorPosition('center bottom')
111
+                            ->getUploadedFileNameForStorageUsing(
112
+                                static fn (TemporaryUploadedFile $file): string => (string) str($file->getClientOriginalName())
113
+                                    ->prepend(Auth::user()->currentCompany->id . '_'),
114
+                            )
115
+                            ->extraAttributes([
116
+                                'class' => 'aspect-[3/2] w-[9.375rem] max-w-full',
117
+                            ])
118
+                            ->acceptedFileTypes(['image/png', 'image/jpeg', 'image/gif']),
119
+                        Forms\Components\Checkbox::make('show_logo')
120
+                            ->localizeLabel(),
121
+                        Forms\Components\ColorPicker::make('accent_color')
122
+                            ->localizeLabel(),
123
+                        Forms\Components\Select::make('font')
124
+                            ->softRequired()
125
+                            ->localizeLabel()
126
+                            ->allowHtml()
127
+                            ->options(
128
+                                collect(Font::cases())
129
+                                    ->mapWithKeys(static fn ($case) => [
130
+                                        $case->value => "<span style='font-family:{$case->getLabel()}'>{$case->getLabel()}</span>",
131
+                                    ]),
132
+                            ),
133
+                        Forms\Components\Select::make('template')
134
+                            ->softRequired()
135
+                            ->localizeLabel()
136
+                            ->options(Template::class),
137
+                        Forms\Components\Select::make('item_name.option')
138
+                            ->softRequired()
139
+                            ->localizeLabel('Item name')
140
+                            ->options(InvoiceModel::getAvailableItemNameOptions())
141
+                            ->afterStateUpdated(static function (Get $get, Set $set, $state, $old) {
142
+                                if ($state !== 'other' && $old === 'other' && filled($get('item_name.custom'))) {
143
+                                    $set('item_name.old_custom', $get('item_name.custom'));
144
+                                    $set('item_name.custom', null);
145
+                                }
146
+
147
+                                if ($state === 'other' && $old !== 'other') {
148
+                                    $set('item_name.custom', $get('item_name.old_custom'));
149
+                                }
150
+                            }),
151
+                        Forms\Components\TextInput::make('item_name.custom')
152
+                            ->hiddenLabel()
153
+                            ->disabled(static fn (callable $get) => $get('item_name.option') !== 'other')
154
+                            ->nullable(),
155
+                        Forms\Components\Select::make('unit_name.option')
156
+                            ->softRequired()
157
+                            ->localizeLabel('Unit name')
158
+                            ->options(InvoiceModel::getAvailableUnitNameOptions())
159
+                            ->afterStateUpdated(static function (Get $get, Set $set, $state, $old) {
160
+                                if ($state !== 'other' && $old === 'other' && filled($get('unit_name.custom'))) {
161
+                                    $set('unit_name.old_custom', $get('unit_name.custom'));
162
+                                    $set('unit_name.custom', null);
163
+                                }
164
+
165
+                                if ($state === 'other' && $old !== 'other') {
166
+                                    $set('unit_name.custom', $get('unit_name.old_custom'));
167
+                                }
168
+                            }),
169
+                        Forms\Components\TextInput::make('unit_name.custom')
170
+                            ->hiddenLabel()
171
+                            ->disabled(static fn (callable $get) => $get('unit_name.option') !== 'other')
172
+                            ->nullable(),
173
+                        Forms\Components\Select::make('price_name.option')
174
+                            ->softRequired()
175
+                            ->localizeLabel('Price name')
176
+                            ->options(InvoiceModel::getAvailablePriceNameOptions())
177
+                            ->afterStateUpdated(static function (Get $get, Set $set, $state, $old) {
178
+                                if ($state !== 'other' && $old === 'other' && filled($get('price_name.custom'))) {
179
+                                    $set('price_name.old_custom', $get('price_name.custom'));
180
+                                    $set('price_name.custom', null);
181
+                                }
182
+
183
+                                if ($state === 'other' && $old !== 'other') {
184
+                                    $set('price_name.custom', $get('price_name.old_custom'));
185
+                                }
186
+                            }),
187
+                        Forms\Components\TextInput::make('price_name.custom')
188
+                            ->hiddenLabel()
189
+                            ->disabled(static fn (callable $get) => $get('price_name.option') !== 'other')
190
+                            ->nullable(),
191
+                        Forms\Components\Select::make('amount_name.option')
192
+                            ->softRequired()
193
+                            ->localizeLabel('Amount name')
194
+                            ->options(InvoiceModel::getAvailableAmountNameOptions())
195
+                            ->afterStateUpdated(static function (Get $get, Set $set, $state, $old) {
196
+                                if ($state !== 'other' && $old === 'other' && filled($get('amount_name.custom'))) {
197
+                                    $set('amount_name.old_custom', $get('amount_name.custom'));
198
+                                    $set('amount_name.custom', null);
199
+                                }
200
+
201
+                                if ($state === 'other' && $old !== 'other') {
202
+                                    $set('amount_name.custom', $get('amount_name.old_custom'));
203
+                                }
204
+                            }),
205
+                        Forms\Components\TextInput::make('amount_name.custom')
206
+                            ->hiddenLabel()
207
+                            ->disabled(static fn (callable $get) => $get('amount_name.option') !== 'other')
208
+                            ->nullable(),
209
+                    ])->columnSpan(1),
210
+                Forms\Components\Grid::make()
211
+                    ->schema([
212
+                        Forms\Components\ViewField::make('preview.default')
213
+                            ->columnSpan(2)
214
+                            ->hiddenLabel()
215
+                            ->visible(static fn (Get $get) => $get('template') === 'default')
216
+                            ->view('filament.company.components.invoice-layouts.default'),
217
+                        Forms\Components\ViewField::make('preview.modern')
218
+                            ->columnSpan(2)
219
+                            ->hiddenLabel()
220
+                            ->visible(static fn (Get $get) => $get('template') === 'modern')
221
+                            ->view('filament.company.components.invoice-layouts.modern'),
222
+                        Forms\Components\ViewField::make('preview.classic')
223
+                            ->columnSpan(2)
224
+                            ->hiddenLabel()
225
+                            ->visible(static fn (Get $get) => $get('template') === 'classic')
226
+                            ->view('filament.company.components.invoice-layouts.classic'),
227
+                    ])->columnSpan(2),
228
+            ])->columns(3);
229
+    }
230
+
231
+    public static function table(Table $table): Table
232
+    {
233
+        return $table
234
+            ->columns([
235
+                Tables\Columns\TextColumn::make('type')
236
+                    ->badge(),
237
+                Tables\Columns\TextColumn::make('number_prefix'),
238
+                Tables\Columns\TextColumn::make('number_next')
239
+                    ->label('Next Number'),
240
+                Tables\Columns\TextColumn::make('template')
241
+                    ->badge(),
242
+                Tables\Columns\IconColumn::make('show_logo')
243
+                    ->boolean(),
244
+            ])
245
+            ->filters([
246
+                //
247
+            ])
248
+            ->actions([
249
+                Tables\Actions\EditAction::make(),
250
+            ])
251
+            ->bulkActions([
252
+                Tables\Actions\BulkActionGroup::make([
253
+                    Tables\Actions\DeleteBulkAction::make(),
254
+                ]),
255
+            ]);
256
+    }
257
+
258
+    public static function getRelations(): array
259
+    {
260
+        return [
261
+            //
262
+        ];
263
+    }
264
+
265
+    public static function getPages(): array
266
+    {
267
+        return [
268
+            'index' => Pages\ListDocumentDefaults::route('/'),
269
+            'create' => Pages\CreateDocumentDefault::route('/create'),
270
+            'edit' => Pages\EditDocumentDefault::route('/{record}/edit'),
271
+        ];
272
+    }
273
+}

+ 11
- 0
app/Filament/Company/Clusters/Settings/Resources/DocumentDefaultResource/Pages/CreateDocumentDefault.php Näytä tiedosto

@@ -0,0 +1,11 @@
1
+<?php
2
+
3
+namespace App\Filament\Company\Clusters\Settings\Resources\DocumentDefaultResource\Pages;
4
+
5
+use App\Filament\Company\Clusters\Settings\Resources\DocumentDefaultResource;
6
+use Filament\Resources\Pages\CreateRecord;
7
+
8
+class CreateDocumentDefault extends CreateRecord
9
+{
10
+    protected static string $resource = DocumentDefaultResource::class;
11
+}

+ 19
- 0
app/Filament/Company/Clusters/Settings/Resources/DocumentDefaultResource/Pages/EditDocumentDefault.php Näytä tiedosto

@@ -0,0 +1,19 @@
1
+<?php
2
+
3
+namespace App\Filament\Company\Clusters\Settings\Resources\DocumentDefaultResource\Pages;
4
+
5
+use App\Filament\Company\Clusters\Settings\Resources\DocumentDefaultResource;
6
+use Filament\Actions;
7
+use Filament\Resources\Pages\EditRecord;
8
+
9
+class EditDocumentDefault extends EditRecord
10
+{
11
+    protected static string $resource = DocumentDefaultResource::class;
12
+
13
+    protected function getHeaderActions(): array
14
+    {
15
+        return [
16
+            Actions\DeleteAction::make(),
17
+        ];
18
+    }
19
+}

+ 25
- 0
app/Filament/Company/Clusters/Settings/Resources/DocumentDefaultResource/Pages/ListDocumentDefaults.php Näytä tiedosto

@@ -0,0 +1,25 @@
1
+<?php
2
+
3
+namespace App\Filament\Company\Clusters\Settings\Resources\DocumentDefaultResource\Pages;
4
+
5
+use App\Filament\Company\Clusters\Settings\Resources\DocumentDefaultResource;
6
+use Filament\Actions;
7
+use Filament\Resources\Pages\ListRecords;
8
+use Filament\Support\Enums\MaxWidth;
9
+
10
+class ListDocumentDefaults extends ListRecords
11
+{
12
+    protected static string $resource = DocumentDefaultResource::class;
13
+
14
+    protected function getHeaderActions(): array
15
+    {
16
+        return [
17
+            Actions\CreateAction::make(),
18
+        ];
19
+    }
20
+
21
+    public function getMaxContentWidth(): MaxWidth | string | null
22
+    {
23
+        return MaxWidth::ScreenTwoExtraLarge;
24
+    }
25
+}

+ 11
- 0
app/Filament/Infolists/Components/DocumentPreview.php Näytä tiedosto

@@ -3,6 +3,8 @@
3 3
 namespace App\Filament\Infolists\Components;
4 4
 
5 5
 use App\Enums\Accounting\DocumentType;
6
+use App\Enums\Setting\Template;
7
+use App\Models\Setting\DocumentDefault;
6 8
 use Filament\Infolists\Components\Grid;
7 9
 
8 10
 class DocumentPreview extends Grid
@@ -33,4 +35,13 @@ class DocumentPreview extends Grid
33 35
     {
34 36
         return $this->documentType;
35 37
     }
38
+
39
+    public function getTemplate(): Template
40
+    {
41
+        $defaults = DocumentDefault::query()
42
+            ->type($this->documentType)
43
+            ->first();
44
+
45
+        return $defaults?->template ?? Template::Default;
46
+    }
36 47
 }

+ 9
- 1
app/Http/Middleware/Authenticate.php Näytä tiedosto

@@ -2,8 +2,10 @@
2 2
 
3 3
 namespace App\Http\Middleware;
4 4
 
5
+use Filament\Exceptions\NoDefaultPanelSetException;
5 6
 use Filament\Facades\Filament;
6 7
 use Filament\Http\Middleware\Authenticate as Middleware;
8
+use Wallo\FilamentCompanies\FilamentCompanies;
7 9
 
8 10
 class Authenticate extends Middleware
9 11
 {
@@ -12,6 +14,12 @@ class Authenticate extends Middleware
12 14
      */
13 15
     protected function redirectTo($request): ?string
14 16
     {
15
-        return Filament::getDefaultPanel()->getLoginUrl();
17
+        try {
18
+            $defaultPanelLoginUrl = Filament::getDefaultPanel()->getLoginUrl();
19
+        } catch (NoDefaultPanelSetException) {
20
+            $defaultPanelLoginUrl = Filament::getPanel(FilamentCompanies::getCompanyPanel())->getLoginUrl();
21
+        }
22
+
23
+        return $defaultPanelLoginUrl;
16 24
     }
17 25
 }

+ 24
- 0
app/Http/Responses/LoginRedirectResponse.php Näytä tiedosto

@@ -0,0 +1,24 @@
1
+<?php
2
+
3
+namespace App\Http\Responses;
4
+
5
+use Filament\Exceptions\NoDefaultPanelSetException;
6
+use Filament\Facades\Filament;
7
+use Filament\Http\Responses\Auth\LoginResponse;
8
+use Illuminate\Http\RedirectResponse;
9
+use Livewire\Features\SupportRedirects\Redirector;
10
+use Wallo\FilamentCompanies\FilamentCompanies;
11
+
12
+class LoginRedirectResponse extends LoginResponse
13
+{
14
+    public function toResponse($request): RedirectResponse | Redirector
15
+    {
16
+        try {
17
+            $defaultPanelUrl = Filament::getDefaultPanel()->getUrl();
18
+        } catch (NoDefaultPanelSetException) {
19
+            $defaultPanelUrl = Filament::getPanel(FilamentCompanies::getCompanyPanel())->getUrl();
20
+        }
21
+
22
+        return redirect()->intended($defaultPanelUrl);
23
+    }
24
+}

+ 0
- 15
app/Http/Responses/LoginResponse.php Näytä tiedosto

@@ -1,15 +0,0 @@
1
-<?php
2
-
3
-namespace App\Http\Responses;
4
-
5
-use Filament\Facades\Filament;
6
-use Illuminate\Http\RedirectResponse;
7
-use Livewire\Features\SupportRedirects\Redirector;
8
-
9
-class LoginResponse extends \Filament\Http\Responses\Auth\LoginResponse
10
-{
11
-    public function toResponse($request): RedirectResponse | Redirector
12
-    {
13
-        return redirect()->to(Filament::getDefaultPanel()->getUrl());
14
-    }
15
-}

+ 13
- 2
app/Listeners/CreateEmployeeContact.php Näytä tiedosto

@@ -3,6 +3,8 @@
3 3
 namespace App\Listeners;
4 4
 
5 5
 use App\Enums\Common\ContactType;
6
+use App\Models\Company;
7
+use App\Models\User;
6 8
 use Wallo\FilamentCompanies\Events\CompanyEmployeeAdded;
7 9
 
8 10
 class CreateEmployeeContact
@@ -20,12 +22,21 @@ class CreateEmployeeContact
20 22
      */
21 23
     public function handle(CompanyEmployeeAdded $event): void
22 24
     {
25
+        /** @var Company $company */
23 26
         $company = $event->company;
27
+
28
+        /** @var User $employee */
24 29
         $employee = $event->user;
25 30
 
26
-        $company->contacts()->create([
31
+        $nameParts = explode(' ', $employee->name, 2);
32
+        $firstName = $nameParts[0];
33
+        $lastName = $nameParts[1] ?? '';
34
+
35
+        $employee->contacts()->create([
36
+            'company_id' => $company->id,
27 37
             'type' => ContactType::Employee,
28
-            'name' => $employee->name,
38
+            'first_name' => $firstName,
39
+            'last_name' => $lastName,
29 40
             'email' => $employee->email,
30 41
             'created_by' => $company->owner->id,
31 42
             'updated_by' => $company->owner->id,

+ 18
- 1
app/Models/Company.php Näytä tiedosto

@@ -2,7 +2,7 @@
2 2
 
3 3
 namespace App\Models;
4 4
 
5
-use App\Enums\Setting\DocumentType;
5
+use App\Enums\Accounting\DocumentType;
6 6
 use App\Models\Accounting\AccountSubtype;
7 7
 use App\Models\Banking\BankAccount;
8 8
 use App\Models\Banking\ConnectedBankAccount;
@@ -125,18 +125,35 @@ class Company extends FilamentCompaniesCompany implements HasAvatar
125 125
         return $this->hasOne(CompanyDefault::class, 'company_id');
126 126
     }
127 127
 
128
+    public function documentDefaults(): HasMany
129
+    {
130
+        return $this->hasMany(DocumentDefault::class, 'company_id');
131
+    }
132
+
128 133
     public function defaultBill(): HasOne
129 134
     {
130 135
         return $this->hasOne(DocumentDefault::class, 'company_id')
131 136
             ->where('type', DocumentType::Bill);
132 137
     }
133 138
 
139
+    public function defaultEstimate(): HasOne
140
+    {
141
+        return $this->hasOne(DocumentDefault::class, 'company_id')
142
+            ->where('type', DocumentType::Estimate);
143
+    }
144
+
134 145
     public function defaultInvoice(): HasOne
135 146
     {
136 147
         return $this->hasOne(DocumentDefault::class, 'company_id')
137 148
             ->where('type', DocumentType::Invoice);
138 149
     }
139 150
 
151
+    public function defaultRecurringInvoice(): HasOne
152
+    {
153
+        return $this->hasOne(DocumentDefault::class, 'company_id')
154
+            ->where('type', DocumentType::RecurringInvoice);
155
+    }
156
+
140 157
     public function departments(): HasMany
141 158
     {
142 159
         return $this->hasMany(Department::class, 'company_id');

+ 10
- 4
app/Models/Setting/DocumentDefault.php Näytä tiedosto

@@ -5,7 +5,7 @@ namespace App\Models\Setting;
5 5
 use App\Casts\TrimLeadingZeroCast;
6 6
 use App\Concerns\Blamable;
7 7
 use App\Concerns\CompanyOwned;
8
-use App\Enums\Setting\DocumentType;
8
+use App\Enums\Accounting\DocumentType;
9 9
 use App\Enums\Setting\Font;
10 10
 use App\Enums\Setting\PaymentTerms;
11 11
 use App\Enums\Setting\Template;
@@ -51,6 +51,7 @@ class DocumentDefault extends Model
51 51
     ];
52 52
 
53 53
     protected $casts = [
54
+        'type' => DocumentType::class,
54 55
         'show_logo' => 'boolean',
55 56
         'number_next' => TrimLeadingZeroCast::class,
56 57
         'payment_terms' => PaymentTerms::class,
@@ -80,17 +81,22 @@ class DocumentDefault extends Model
80 81
 
81 82
     public function scopeInvoice(Builder $query): Builder
82 83
     {
83
-        return $query->scopes(['type' => [DocumentType::Invoice]]);
84
+        return $query->type(DocumentType::Invoice);
85
+    }
86
+
87
+    public function scopeRecurringInvoice(Builder $query): Builder
88
+    {
89
+        return $query->type(DocumentType::RecurringInvoice);
84 90
     }
85 91
 
86 92
     public function scopeBill(Builder $query): Builder
87 93
     {
88
-        return $query->scopes(['type' => [DocumentType::Bill]]);
94
+        return $query->type(DocumentType::Bill);
89 95
     }
90 96
 
91 97
     public function scopeEstimate(Builder $query): Builder
92 98
     {
93
-        return $query->scopes(['type' => [DocumentType::Estimate]]);
99
+        return $query->type(DocumentType::Estimate);
94 100
     }
95 101
 
96 102
     public static function availableNumberDigits(): array

+ 7
- 0
app/Models/User.php Näytä tiedosto

@@ -2,6 +2,7 @@
2 2
 
3 3
 namespace App\Models;
4 4
 
5
+use App\Models\Common\Contact;
5 6
 use App\Models\Core\Department;
6 7
 use Filament\Models\Contracts\FilamentUser;
7 8
 use Filament\Models\Contracts\HasAvatar;
@@ -11,6 +12,7 @@ use Filament\Panel;
11 12
 use Illuminate\Database\Eloquent\Factories\HasFactory;
12 13
 use Illuminate\Database\Eloquent\Model;
13 14
 use Illuminate\Database\Eloquent\Relations\HasMany;
15
+use Illuminate\Database\Eloquent\Relations\MorphMany;
14 16
 use Illuminate\Foundation\Auth\User as Authenticatable;
15 17
 use Illuminate\Notifications\Notifiable;
16 18
 use Illuminate\Support\Collection;
@@ -92,6 +94,11 @@ class User extends Authenticatable implements FilamentUser, HasAvatar, HasDefaul
92 94
         return $this->profile_photo_url;
93 95
     }
94 96
 
97
+    public function contacts(): MorphMany
98
+    {
99
+        return $this->morphMany(Contact::class, 'contactable');
100
+    }
101
+
95 102
     public function managerOf(): HasMany
96 103
     {
97 104
         return $this->hasMany(Department::class, 'manager_id');

+ 2
- 1
app/Providers/AppServiceProvider.php Näytä tiedosto

@@ -2,6 +2,7 @@
2 2
 
3 3
 namespace App\Providers;
4 4
 
5
+use App\Http\Responses\LoginRedirectResponse;
5 6
 use App\Services\DateRangeService;
6 7
 use Filament\Http\Responses\Auth\Contracts\LoginResponse;
7 8
 use Filament\Notifications\Livewire\Notifications;
@@ -18,7 +19,7 @@ class AppServiceProvider extends ServiceProvider
18 19
     public function register(): void
19 20
     {
20 21
         $this->app->singleton(DateRangeService::class);
21
-        $this->app->singleton(LoginResponse::class, \App\Http\Responses\LoginResponse::class);
22
+        $this->app->singleton(LoginResponse::class, LoginRedirectResponse::class);
22 23
     }
23 24
 
24 25
     /**

+ 1
- 0
app/Providers/Filament/CompanyPanelProvider.php Näytä tiedosto

@@ -99,6 +99,7 @@ class CompanyPanelProvider extends PanelProvider
99 99
                     ->profilePhotos()
100 100
                     ->api()
101 101
                     ->companies(invitations: true)
102
+                    ->autoAcceptInvitations()
102 103
                     ->termsAndPrivacyPolicy()
103 104
                     ->notifications()
104 105
                     ->modals()

+ 12
- 1
app/Providers/Filament/UserPanelProvider.php Näytä tiedosto

@@ -6,6 +6,7 @@ use App\Filament\Components\PanelShiftDropdown;
6 6
 use App\Filament\User\Clusters\Account;
7 7
 use App\Http\Middleware\Authenticate;
8 8
 use Exception;
9
+use Filament\Facades\Filament;
9 10
 use Filament\Http\Middleware\DisableBladeIconComponents;
10 11
 use Filament\Http\Middleware\DispatchServingFilamentEvent;
11 12
 use Filament\Navigation\NavigationBuilder;
@@ -20,7 +21,9 @@ use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
20 21
 use Illuminate\Routing\Middleware\SubstituteBindings;
21 22
 use Illuminate\Session\Middleware\AuthenticateSession;
22 23
 use Illuminate\Session\Middleware\StartSession;
24
+use Illuminate\Support\Facades\Auth;
23 25
 use Illuminate\View\Middleware\ShareErrorsFromSession;
26
+use Wallo\FilamentCompanies\FilamentCompanies;
24 27
 use Wallo\FilamentCompanies\Pages\User\PersonalAccessTokens;
25 28
 use Wallo\FilamentCompanies\Pages\User\Profile;
26 29
 
@@ -45,7 +48,15 @@ class UserPanelProvider extends PanelProvider
45 48
                                 NavigationItem::make('company')
46 49
                                     ->label('Company Dashboard')
47 50
                                     ->icon('heroicon-s-building-office-2')
48
-                                    ->url(static fn (): string => Pages\Dashboard::getUrl(panel: 'company', tenant: auth()->user()->personalCompany())),
51
+                                    ->url(static function (): ?string {
52
+                                        $user = Auth::user();
53
+
54
+                                        if ($company = $user?->primaryCompany()) {
55
+                                            return Pages\Dashboard::getUrl(panel: FilamentCompanies::getCompanyPanel(), tenant: $company);
56
+                                        }
57
+
58
+                                        return Filament::getPanel(FilamentCompanies::getCompanyPanel())->getTenantRegistrationUrl();
59
+                                    }),
49 60
                             ]);
50 61
                     }),
51 62
             )

+ 0
- 1
bootstrap/providers.php Näytä tiedosto

@@ -3,7 +3,6 @@
3 3
 return [
4 4
     App\Providers\AppServiceProvider::class,
5 5
     App\Providers\AuthServiceProvider::class,
6
-    App\Providers\Filament\AdminPanelProvider::class,
7 6
     App\Providers\Filament\CompanyPanelProvider::class,
8 7
     App\Providers\Filament\UserPanelProvider::class,
9 8
     App\Providers\Faker\FakerServiceProvider::class,

+ 215
- 216
composer.lock
File diff suppressed because it is too large
Näytä tiedosto


+ 1
- 1
database/factories/Setting/DocumentDefaultFactory.php Näytä tiedosto

@@ -2,7 +2,7 @@
2 2
 
3 3
 namespace Database\Factories\Setting;
4 4
 
5
-use App\Enums\Setting\DocumentType;
5
+use App\Enums\Accounting\DocumentType;
6 6
 use App\Models\Setting\DocumentDefault;
7 7
 use Illuminate\Database\Eloquent\Factories\Factory;
8 8
 

+ 269
- 99
package-lock.json Näytä tiedosto

@@ -575,9 +575,9 @@
575 575
             }
576 576
         },
577 577
         "node_modules/@rollup/rollup-android-arm-eabi": {
578
-            "version": "4.34.1",
579
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.1.tgz",
580
-            "integrity": "sha512-kwctwVlswSEsr4ljpmxKrRKp1eG1v2NAhlzFzDf1x1OdYaMjBYjDCbHkzWm57ZXzTwqn8stMXgROrnMw8dJK3w==",
578
+            "version": "4.34.7",
579
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.7.tgz",
580
+            "integrity": "sha512-l6CtzHYo8D2TQ3J7qJNpp3Q1Iye56ssIAtqbM2H8axxCEEwvN7o8Ze9PuIapbxFL3OHrJU2JBX6FIIVnP/rYyw==",
581 581
             "cpu": [
582 582
                 "arm"
583 583
             ],
@@ -589,9 +589,9 @@
589 589
             ]
590 590
         },
591 591
         "node_modules/@rollup/rollup-android-arm64": {
592
-            "version": "4.34.1",
593
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.1.tgz",
594
-            "integrity": "sha512-4H5ZtZitBPlbPsTv6HBB8zh1g5d0T8TzCmpndQdqq20Ugle/nroOyDMf9p7f88Gsu8vBLU78/cuh8FYHZqdXxw==",
592
+            "version": "4.34.7",
593
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.7.tgz",
594
+            "integrity": "sha512-KvyJpFUueUnSp53zhAa293QBYqwm94TgYTIfXyOTtidhm5V0LbLCJQRGkQClYiX3FXDQGSvPxOTD/6rPStMMDg==",
595 595
             "cpu": [
596 596
                 "arm64"
597 597
             ],
@@ -603,9 +603,9 @@
603 603
             ]
604 604
         },
605 605
         "node_modules/@rollup/rollup-darwin-arm64": {
606
-            "version": "4.34.1",
607
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.1.tgz",
608
-            "integrity": "sha512-f2AJ7Qwx9z25hikXvg+asco8Sfuc5NCLg8rmqQBIOUoWys5sb/ZX9RkMZDPdnnDevXAMJA5AWLnRBmgdXGEUiA==",
606
+            "version": "4.34.7",
607
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.7.tgz",
608
+            "integrity": "sha512-jq87CjmgL9YIKvs8ybtIC98s/M3HdbqXhllcy9EdLV0yMg1DpxES2gr65nNy7ObNo/vZ/MrOTxt0bE5LinL6mA==",
609 609
             "cpu": [
610 610
                 "arm64"
611 611
             ],
@@ -617,9 +617,9 @@
617 617
             ]
618 618
         },
619 619
         "node_modules/@rollup/rollup-darwin-x64": {
620
-            "version": "4.34.1",
621
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.1.tgz",
622
-            "integrity": "sha512-+/2JBrRfISCsWE4aEFXxd+7k9nWGXA8+wh7ZUHn/u8UDXOU9LN+QYKKhd57sIn6WRcorOnlqPMYFIwie/OHXWw==",
620
+            "version": "4.34.7",
621
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.7.tgz",
622
+            "integrity": "sha512-rSI/m8OxBjsdnMMg0WEetu/w+LhLAcCDEiL66lmMX4R3oaml3eXz3Dxfvrxs1FbzPbJMaItQiksyMfv1hoIxnA==",
623 623
             "cpu": [
624 624
                 "x64"
625 625
             ],
@@ -631,9 +631,9 @@
631 631
             ]
632 632
         },
633 633
         "node_modules/@rollup/rollup-freebsd-arm64": {
634
-            "version": "4.34.1",
635
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.1.tgz",
636
-            "integrity": "sha512-SUeB0pYjIXwT2vfAMQ7E4ERPq9VGRrPR7Z+S4AMssah5EHIilYqjWQoTn5dkDtuIJUSTs8H+C9dwoEcg3b0sCA==",
634
+            "version": "4.34.7",
635
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.7.tgz",
636
+            "integrity": "sha512-oIoJRy3ZrdsXpFuWDtzsOOa/E/RbRWXVokpVrNnkS7npz8GEG++E1gYbzhYxhxHbO2om1T26BZjVmdIoyN2WtA==",
637 637
             "cpu": [
638 638
                 "arm64"
639 639
             ],
@@ -645,9 +645,9 @@
645 645
             ]
646 646
         },
647 647
         "node_modules/@rollup/rollup-freebsd-x64": {
648
-            "version": "4.34.1",
649
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.1.tgz",
650
-            "integrity": "sha512-L3T66wAZiB/ooiPbxz0s6JEX6Sr2+HfgPSK+LMuZkaGZFAFCQAHiP3dbyqovYdNaiUXcl9TlgnIbcsIicAnOZg==",
648
+            "version": "4.34.7",
649
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.7.tgz",
650
+            "integrity": "sha512-X++QSLm4NZfZ3VXGVwyHdRf58IBbCu9ammgJxuWZYLX0du6kZvdNqPwrjvDfwmi6wFdvfZ/s6K7ia0E5kI7m8Q==",
651 651
             "cpu": [
652 652
                 "x64"
653 653
             ],
@@ -659,9 +659,9 @@
659 659
             ]
660 660
         },
661 661
         "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
662
-            "version": "4.34.1",
663
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.1.tgz",
664
-            "integrity": "sha512-UBXdQ4+ATARuFgsFrQ+tAsKvBi/Hly99aSVdeCUiHV9dRTTpMU7OrM3WXGys1l40wKVNiOl0QYY6cZQJ2xhKlQ==",
662
+            "version": "4.34.7",
663
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.7.tgz",
664
+            "integrity": "sha512-Z0TzhrsNqukTz3ISzrvyshQpFnFRfLunYiXxlCRvcrb3nvC5rVKI+ZXPFG/Aa4jhQa1gHgH3A0exHaRRN4VmdQ==",
665 665
             "cpu": [
666 666
                 "arm"
667 667
             ],
@@ -673,9 +673,9 @@
673 673
             ]
674 674
         },
675 675
         "node_modules/@rollup/rollup-linux-arm-musleabihf": {
676
-            "version": "4.34.1",
677
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.1.tgz",
678
-            "integrity": "sha512-m/yfZ25HGdcCSwmopEJm00GP7xAUyVcBPjttGLRAqZ60X/bB4Qn6gP7XTwCIU6bITeKmIhhwZ4AMh2XLro+4+w==",
676
+            "version": "4.34.7",
677
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.7.tgz",
678
+            "integrity": "sha512-nkznpyXekFAbvFBKBy4nNppSgneB1wwG1yx/hujN3wRnhnkrYVugMTCBXED4+Ni6thoWfQuHNYbFjgGH0MBXtw==",
679 679
             "cpu": [
680 680
                 "arm"
681 681
             ],
@@ -687,9 +687,9 @@
687 687
             ]
688 688
         },
689 689
         "node_modules/@rollup/rollup-linux-arm64-gnu": {
690
-            "version": "4.34.1",
691
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.1.tgz",
692
-            "integrity": "sha512-Wy+cUmFuvziNL9qWRRzboNprqSQ/n38orbjRvd6byYWridp5TJ3CD+0+HUsbcWVSNz9bxkDUkyASGP0zS7GAvg==",
690
+            "version": "4.34.7",
691
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.7.tgz",
692
+            "integrity": "sha512-KCjlUkcKs6PjOcxolqrXglBDcfCuUCTVlX5BgzgoJHw+1rWH1MCkETLkLe5iLLS9dP5gKC7mp3y6x8c1oGBUtA==",
693 693
             "cpu": [
694 694
                 "arm64"
695 695
             ],
@@ -701,9 +701,9 @@
701 701
             ]
702 702
         },
703 703
         "node_modules/@rollup/rollup-linux-arm64-musl": {
704
-            "version": "4.34.1",
705
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.1.tgz",
706
-            "integrity": "sha512-CQ3MAGgiFmQW5XJX5W3wnxOBxKwFlUAgSXFA2SwgVRjrIiVt5LHfcQLeNSHKq5OEZwv+VCBwlD1+YKCjDG8cpg==",
704
+            "version": "4.34.7",
705
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.7.tgz",
706
+            "integrity": "sha512-uFLJFz6+utmpbR313TTx+NpPuAXbPz4BhTQzgaP0tozlLnGnQ6rCo6tLwaSa6b7l6gRErjLicXQ1iPiXzYotjw==",
707 707
             "cpu": [
708 708
                 "arm64"
709 709
             ],
@@ -715,9 +715,9 @@
715 715
             ]
716 716
         },
717 717
         "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
718
-            "version": "4.34.1",
719
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.1.tgz",
720
-            "integrity": "sha512-rSzb1TsY4lSwH811cYC3OC2O2mzNMhM13vcnA7/0T6Mtreqr3/qs6WMDriMRs8yvHDI54qxHgOk8EV5YRAHFbw==",
718
+            "version": "4.34.7",
719
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.7.tgz",
720
+            "integrity": "sha512-ws8pc68UcJJqCpneDFepnwlsMUFoWvPbWXT/XUrJ7rWUL9vLoIN3GAasgG+nCvq8xrE3pIrd+qLX/jotcLy0Qw==",
721 721
             "cpu": [
722 722
                 "loong64"
723 723
             ],
@@ -729,9 +729,9 @@
729 729
             ]
730 730
         },
731 731
         "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
732
-            "version": "4.34.1",
733
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.1.tgz",
734
-            "integrity": "sha512-fwr0n6NS0pG3QxxlqVYpfiY64Fd1Dqd8Cecje4ILAV01ROMp4aEdCj5ssHjRY3UwU7RJmeWd5fi89DBqMaTawg==",
732
+            "version": "4.34.7",
733
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.7.tgz",
734
+            "integrity": "sha512-vrDk9JDa/BFkxcS2PbWpr0C/LiiSLxFbNOBgfbW6P8TBe9PPHx9Wqbvx2xgNi1TOAyQHQJ7RZFqBiEohm79r0w==",
735 735
             "cpu": [
736 736
                 "ppc64"
737 737
             ],
@@ -743,9 +743,9 @@
743 743
             ]
744 744
         },
745 745
         "node_modules/@rollup/rollup-linux-riscv64-gnu": {
746
-            "version": "4.34.1",
747
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.1.tgz",
748
-            "integrity": "sha512-4uJb9qz7+Z/yUp5RPxDGGGUcoh0PnKF33QyWgEZ3X/GocpWb6Mb+skDh59FEt5d8+Skxqs9mng6Swa6B2AmQZg==",
746
+            "version": "4.34.7",
747
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.7.tgz",
748
+            "integrity": "sha512-rB+ejFyjtmSo+g/a4eovDD1lHWHVqizN8P0Hm0RElkINpS0XOdpaXloqM4FBkF9ZWEzg6bezymbpLmeMldfLTw==",
749 749
             "cpu": [
750 750
                 "riscv64"
751 751
             ],
@@ -757,9 +757,9 @@
757 757
             ]
758 758
         },
759 759
         "node_modules/@rollup/rollup-linux-s390x-gnu": {
760
-            "version": "4.34.1",
761
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.1.tgz",
762
-            "integrity": "sha512-QlIo8ndocWBEnfmkYqj8vVtIUpIqJjfqKggjy7IdUncnt8BGixte1wDON7NJEvLg3Kzvqxtbo8tk+U1acYEBlw==",
760
+            "version": "4.34.7",
761
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.7.tgz",
762
+            "integrity": "sha512-nNXNjo4As6dNqRn7OrsnHzwTgtypfRA3u3AKr0B3sOOo+HkedIbn8ZtFnB+4XyKJojIfqDKmbIzO1QydQ8c+Pw==",
763 763
             "cpu": [
764 764
                 "s390x"
765 765
             ],
@@ -771,9 +771,9 @@
771 771
             ]
772 772
         },
773 773
         "node_modules/@rollup/rollup-linux-x64-gnu": {
774
-            "version": "4.34.1",
775
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.1.tgz",
776
-            "integrity": "sha512-hzpleiKtq14GWjz3ahWvJXgU1DQC9DteiwcsY4HgqUJUGxZThlL66MotdUEK9zEo0PK/2ADeZGM9LIondE302A==",
774
+            "version": "4.34.7",
775
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.7.tgz",
776
+            "integrity": "sha512-9kPVf9ahnpOMSGlCxXGv980wXD0zRR3wyk8+33/MXQIpQEOpaNe7dEHm5LMfyRZRNt9lMEQuH0jUKj15MkM7QA==",
777 777
             "cpu": [
778 778
                 "x64"
779 779
             ],
@@ -785,9 +785,9 @@
785 785
             ]
786 786
         },
787 787
         "node_modules/@rollup/rollup-linux-x64-musl": {
788
-            "version": "4.34.1",
789
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.1.tgz",
790
-            "integrity": "sha512-jqtKrO715hDlvUcEsPn55tZt2TEiBvBtCMkUuU0R6fO/WPT7lO9AONjPbd8II7/asSiNVQHCMn4OLGigSuxVQA==",
788
+            "version": "4.34.7",
789
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.7.tgz",
790
+            "integrity": "sha512-7wJPXRWTTPtTFDFezA8sle/1sdgxDjuMoRXEKtx97ViRxGGkVQYovem+Q8Pr/2HxiHp74SSRG+o6R0Yq0shPwQ==",
791 791
             "cpu": [
792 792
                 "x64"
793 793
             ],
@@ -799,9 +799,9 @@
799 799
             ]
800 800
         },
801 801
         "node_modules/@rollup/rollup-win32-arm64-msvc": {
802
-            "version": "4.34.1",
803
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.1.tgz",
804
-            "integrity": "sha512-RnHy7yFf2Wz8Jj1+h8klB93N0NHNHXFhNwAmiy9zJdpY7DE01VbEVtPdrK1kkILeIbHGRJjvfBDBhnxBr8kD4g==",
802
+            "version": "4.34.7",
803
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.7.tgz",
804
+            "integrity": "sha512-MN7aaBC7mAjsiMEZcsJvwNsQVNZShgES/9SzWp1HC9Yjqb5OpexYnRjF7RmE4itbeesHMYYQiAtUAQaSKs2Rfw==",
805 805
             "cpu": [
806 806
                 "arm64"
807 807
             ],
@@ -813,9 +813,9 @@
813 813
             ]
814 814
         },
815 815
         "node_modules/@rollup/rollup-win32-ia32-msvc": {
816
-            "version": "4.34.1",
817
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.1.tgz",
818
-            "integrity": "sha512-i7aT5HdiZIcd7quhzvwQ2oAuX7zPYrYfkrd1QFfs28Po/i0q6kas/oRrzGlDhAEyug+1UfUtkWdmoVlLJj5x9Q==",
816
+            "version": "4.34.7",
817
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.7.tgz",
818
+            "integrity": "sha512-aeawEKYswsFu1LhDM9RIgToobquzdtSc4jSVqHV8uApz4FVvhFl/mKh92wc8WpFc6aYCothV/03UjY6y7yLgbg==",
819 819
             "cpu": [
820 820
                 "ia32"
821 821
             ],
@@ -827,9 +827,9 @@
827 827
             ]
828 828
         },
829 829
         "node_modules/@rollup/rollup-win32-x64-msvc": {
830
-            "version": "4.34.1",
831
-            "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.1.tgz",
832
-            "integrity": "sha512-k3MVFD9Oq+laHkw2N2v7ILgoa9017ZMF/inTtHzyTVZjYs9cSH18sdyAf6spBAJIGwJ5UaC7et2ZH1WCdlhkMw==",
830
+            "version": "4.34.7",
831
+            "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.7.tgz",
832
+            "integrity": "sha512-4ZedScpxxIrVO7otcZ8kCX1mZArtH2Wfj3uFCxRJ9NO80gg1XV0U/b2f/MKaGwj2X3QopHfoWiDQ917FRpwY3w==",
833 833
             "cpu": [
834 834
                 "x64"
835 835
             ],
@@ -1063,6 +1063,20 @@
1063 1063
                 "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
1064 1064
             }
1065 1065
         },
1066
+        "node_modules/call-bind-apply-helpers": {
1067
+            "version": "1.0.2",
1068
+            "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
1069
+            "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
1070
+            "dev": true,
1071
+            "license": "MIT",
1072
+            "dependencies": {
1073
+                "es-errors": "^1.3.0",
1074
+                "function-bind": "^1.1.2"
1075
+            },
1076
+            "engines": {
1077
+                "node": ">= 0.4"
1078
+            }
1079
+        },
1066 1080
         "node_modules/camelcase-css": {
1067 1081
             "version": "2.0.1",
1068 1082
             "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
@@ -1074,9 +1088,9 @@
1074 1088
             }
1075 1089
         },
1076 1090
         "node_modules/caniuse-lite": {
1077
-            "version": "1.0.30001696",
1078
-            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001696.tgz",
1079
-            "integrity": "sha512-pDCPkvzfa39ehJtJ+OwGT/2yvT2SbjfHhiIW2LWOAcMQ7BzwxT/XuyUp4OTOd0XFWA6BKw0JalnBHgSi5DGJBQ==",
1091
+            "version": "1.0.30001700",
1092
+            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz",
1093
+            "integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==",
1080 1094
             "dev": true,
1081 1095
             "funding": [
1082 1096
                 {
@@ -1227,6 +1241,21 @@
1227 1241
             "dev": true,
1228 1242
             "license": "MIT"
1229 1243
         },
1244
+        "node_modules/dunder-proto": {
1245
+            "version": "1.0.1",
1246
+            "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
1247
+            "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
1248
+            "dev": true,
1249
+            "license": "MIT",
1250
+            "dependencies": {
1251
+                "call-bind-apply-helpers": "^1.0.1",
1252
+                "es-errors": "^1.3.0",
1253
+                "gopd": "^1.2.0"
1254
+            },
1255
+            "engines": {
1256
+                "node": ">= 0.4"
1257
+            }
1258
+        },
1230 1259
         "node_modules/eastasianwidth": {
1231 1260
             "version": "0.2.0",
1232 1261
             "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
@@ -1235,9 +1264,9 @@
1235 1264
             "license": "MIT"
1236 1265
         },
1237 1266
         "node_modules/electron-to-chromium": {
1238
-            "version": "1.5.91",
1239
-            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.91.tgz",
1240
-            "integrity": "sha512-sNSHHyq048PFmZY4S90ax61q+gLCs0X0YmcOII9wG9S2XwbVr+h4VW2wWhnbp/Eys3cCwTxVF292W3qPaxIapQ==",
1267
+            "version": "1.5.101",
1268
+            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.101.tgz",
1269
+            "integrity": "sha512-L0ISiQrP/56Acgu4/i/kfPwWSgrzYZUnQrC0+QPFuhqlLP1Ir7qzPPDVS9BcKIyWTRU8+o6CC8dKw38tSWhYIA==",
1241 1270
             "dev": true,
1242 1271
             "license": "ISC"
1243 1272
         },
@@ -1248,6 +1277,55 @@
1248 1277
             "dev": true,
1249 1278
             "license": "MIT"
1250 1279
         },
1280
+        "node_modules/es-define-property": {
1281
+            "version": "1.0.1",
1282
+            "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
1283
+            "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
1284
+            "dev": true,
1285
+            "license": "MIT",
1286
+            "engines": {
1287
+                "node": ">= 0.4"
1288
+            }
1289
+        },
1290
+        "node_modules/es-errors": {
1291
+            "version": "1.3.0",
1292
+            "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
1293
+            "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
1294
+            "dev": true,
1295
+            "license": "MIT",
1296
+            "engines": {
1297
+                "node": ">= 0.4"
1298
+            }
1299
+        },
1300
+        "node_modules/es-object-atoms": {
1301
+            "version": "1.1.1",
1302
+            "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
1303
+            "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
1304
+            "dev": true,
1305
+            "license": "MIT",
1306
+            "dependencies": {
1307
+                "es-errors": "^1.3.0"
1308
+            },
1309
+            "engines": {
1310
+                "node": ">= 0.4"
1311
+            }
1312
+        },
1313
+        "node_modules/es-set-tostringtag": {
1314
+            "version": "2.1.0",
1315
+            "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
1316
+            "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
1317
+            "dev": true,
1318
+            "license": "MIT",
1319
+            "dependencies": {
1320
+                "es-errors": "^1.3.0",
1321
+                "get-intrinsic": "^1.2.6",
1322
+                "has-tostringtag": "^1.0.2",
1323
+                "hasown": "^2.0.2"
1324
+            },
1325
+            "engines": {
1326
+                "node": ">= 0.4"
1327
+            }
1328
+        },
1251 1329
         "node_modules/esbuild": {
1252 1330
             "version": "0.24.2",
1253 1331
             "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz",
@@ -1391,14 +1469,15 @@
1391 1469
             }
1392 1470
         },
1393 1471
         "node_modules/form-data": {
1394
-            "version": "4.0.1",
1395
-            "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
1396
-            "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
1472
+            "version": "4.0.2",
1473
+            "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
1474
+            "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
1397 1475
             "dev": true,
1398 1476
             "license": "MIT",
1399 1477
             "dependencies": {
1400 1478
                 "asynckit": "^0.4.0",
1401 1479
                 "combined-stream": "^1.0.8",
1480
+                "es-set-tostringtag": "^2.1.0",
1402 1481
                 "mime-types": "^2.1.12"
1403 1482
             },
1404 1483
             "engines": {
@@ -1444,6 +1523,45 @@
1444 1523
                 "url": "https://github.com/sponsors/ljharb"
1445 1524
             }
1446 1525
         },
1526
+        "node_modules/get-intrinsic": {
1527
+            "version": "1.2.7",
1528
+            "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz",
1529
+            "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==",
1530
+            "dev": true,
1531
+            "license": "MIT",
1532
+            "dependencies": {
1533
+                "call-bind-apply-helpers": "^1.0.1",
1534
+                "es-define-property": "^1.0.1",
1535
+                "es-errors": "^1.3.0",
1536
+                "es-object-atoms": "^1.0.0",
1537
+                "function-bind": "^1.1.2",
1538
+                "get-proto": "^1.0.0",
1539
+                "gopd": "^1.2.0",
1540
+                "has-symbols": "^1.1.0",
1541
+                "hasown": "^2.0.2",
1542
+                "math-intrinsics": "^1.1.0"
1543
+            },
1544
+            "engines": {
1545
+                "node": ">= 0.4"
1546
+            },
1547
+            "funding": {
1548
+                "url": "https://github.com/sponsors/ljharb"
1549
+            }
1550
+        },
1551
+        "node_modules/get-proto": {
1552
+            "version": "1.0.1",
1553
+            "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
1554
+            "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
1555
+            "dev": true,
1556
+            "license": "MIT",
1557
+            "dependencies": {
1558
+                "dunder-proto": "^1.0.1",
1559
+                "es-object-atoms": "^1.0.0"
1560
+            },
1561
+            "engines": {
1562
+                "node": ">= 0.4"
1563
+            }
1564
+        },
1447 1565
         "node_modules/glob": {
1448 1566
             "version": "10.4.5",
1449 1567
             "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
@@ -1478,6 +1596,48 @@
1478 1596
                 "node": ">=10.13.0"
1479 1597
             }
1480 1598
         },
1599
+        "node_modules/gopd": {
1600
+            "version": "1.2.0",
1601
+            "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
1602
+            "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
1603
+            "dev": true,
1604
+            "license": "MIT",
1605
+            "engines": {
1606
+                "node": ">= 0.4"
1607
+            },
1608
+            "funding": {
1609
+                "url": "https://github.com/sponsors/ljharb"
1610
+            }
1611
+        },
1612
+        "node_modules/has-symbols": {
1613
+            "version": "1.1.0",
1614
+            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
1615
+            "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
1616
+            "dev": true,
1617
+            "license": "MIT",
1618
+            "engines": {
1619
+                "node": ">= 0.4"
1620
+            },
1621
+            "funding": {
1622
+                "url": "https://github.com/sponsors/ljharb"
1623
+            }
1624
+        },
1625
+        "node_modules/has-tostringtag": {
1626
+            "version": "1.0.2",
1627
+            "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
1628
+            "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
1629
+            "dev": true,
1630
+            "license": "MIT",
1631
+            "dependencies": {
1632
+                "has-symbols": "^1.0.3"
1633
+            },
1634
+            "engines": {
1635
+                "node": ">= 0.4"
1636
+            },
1637
+            "funding": {
1638
+                "url": "https://github.com/sponsors/ljharb"
1639
+            }
1640
+        },
1481 1641
         "node_modules/hasown": {
1482 1642
             "version": "2.0.2",
1483 1643
             "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
@@ -1664,6 +1824,16 @@
1664 1824
             "dev": true,
1665 1825
             "license": "ISC"
1666 1826
         },
1827
+        "node_modules/math-intrinsics": {
1828
+            "version": "1.1.0",
1829
+            "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
1830
+            "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
1831
+            "dev": true,
1832
+            "license": "MIT",
1833
+            "engines": {
1834
+                "node": ">= 0.4"
1835
+            }
1836
+        },
1667 1837
         "node_modules/merge2": {
1668 1838
             "version": "1.4.1",
1669 1839
             "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -1907,9 +2077,9 @@
1907 2077
             }
1908 2078
         },
1909 2079
         "node_modules/postcss": {
1910
-            "version": "8.5.1",
1911
-            "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz",
1912
-            "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==",
2080
+            "version": "8.5.2",
2081
+            "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz",
2082
+            "integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==",
1913 2083
             "dev": true,
1914 2084
             "funding": [
1915 2085
                 {
@@ -2124,9 +2294,9 @@
2124 2294
             }
2125 2295
         },
2126 2296
         "node_modules/postcss-nesting/node_modules/postcss-selector-parser": {
2127
-            "version": "7.0.0",
2128
-            "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz",
2129
-            "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==",
2297
+            "version": "7.1.0",
2298
+            "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz",
2299
+            "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==",
2130 2300
             "dev": true,
2131 2301
             "license": "MIT",
2132 2302
             "dependencies": {
@@ -2242,9 +2412,9 @@
2242 2412
             }
2243 2413
         },
2244 2414
         "node_modules/rollup": {
2245
-            "version": "4.34.1",
2246
-            "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.1.tgz",
2247
-            "integrity": "sha512-iYZ/+PcdLYSGfH3S+dGahlW/RWmsqDhLgj1BT9DH/xXJ0ggZN7xkdP9wipPNjjNLczI+fmMLmTB9pye+d2r4GQ==",
2415
+            "version": "4.34.7",
2416
+            "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.7.tgz",
2417
+            "integrity": "sha512-8qhyN0oZ4x0H6wmBgfKxJtxM7qS98YJ0k0kNh5ECVtuchIJ7z9IVVvzpmtQyT10PXKMtBxYr1wQ5Apg8RS8kXQ==",
2248 2418
             "dev": true,
2249 2419
             "license": "MIT",
2250 2420
             "dependencies": {
@@ -2258,25 +2428,25 @@
2258 2428
                 "npm": ">=8.0.0"
2259 2429
             },
2260 2430
             "optionalDependencies": {
2261
-                "@rollup/rollup-android-arm-eabi": "4.34.1",
2262
-                "@rollup/rollup-android-arm64": "4.34.1",
2263
-                "@rollup/rollup-darwin-arm64": "4.34.1",
2264
-                "@rollup/rollup-darwin-x64": "4.34.1",
2265
-                "@rollup/rollup-freebsd-arm64": "4.34.1",
2266
-                "@rollup/rollup-freebsd-x64": "4.34.1",
2267
-                "@rollup/rollup-linux-arm-gnueabihf": "4.34.1",
2268
-                "@rollup/rollup-linux-arm-musleabihf": "4.34.1",
2269
-                "@rollup/rollup-linux-arm64-gnu": "4.34.1",
2270
-                "@rollup/rollup-linux-arm64-musl": "4.34.1",
2271
-                "@rollup/rollup-linux-loongarch64-gnu": "4.34.1",
2272
-                "@rollup/rollup-linux-powerpc64le-gnu": "4.34.1",
2273
-                "@rollup/rollup-linux-riscv64-gnu": "4.34.1",
2274
-                "@rollup/rollup-linux-s390x-gnu": "4.34.1",
2275
-                "@rollup/rollup-linux-x64-gnu": "4.34.1",
2276
-                "@rollup/rollup-linux-x64-musl": "4.34.1",
2277
-                "@rollup/rollup-win32-arm64-msvc": "4.34.1",
2278
-                "@rollup/rollup-win32-ia32-msvc": "4.34.1",
2279
-                "@rollup/rollup-win32-x64-msvc": "4.34.1",
2431
+                "@rollup/rollup-android-arm-eabi": "4.34.7",
2432
+                "@rollup/rollup-android-arm64": "4.34.7",
2433
+                "@rollup/rollup-darwin-arm64": "4.34.7",
2434
+                "@rollup/rollup-darwin-x64": "4.34.7",
2435
+                "@rollup/rollup-freebsd-arm64": "4.34.7",
2436
+                "@rollup/rollup-freebsd-x64": "4.34.7",
2437
+                "@rollup/rollup-linux-arm-gnueabihf": "4.34.7",
2438
+                "@rollup/rollup-linux-arm-musleabihf": "4.34.7",
2439
+                "@rollup/rollup-linux-arm64-gnu": "4.34.7",
2440
+                "@rollup/rollup-linux-arm64-musl": "4.34.7",
2441
+                "@rollup/rollup-linux-loongarch64-gnu": "4.34.7",
2442
+                "@rollup/rollup-linux-powerpc64le-gnu": "4.34.7",
2443
+                "@rollup/rollup-linux-riscv64-gnu": "4.34.7",
2444
+                "@rollup/rollup-linux-s390x-gnu": "4.34.7",
2445
+                "@rollup/rollup-linux-x64-gnu": "4.34.7",
2446
+                "@rollup/rollup-linux-x64-musl": "4.34.7",
2447
+                "@rollup/rollup-win32-arm64-msvc": "4.34.7",
2448
+                "@rollup/rollup-win32-ia32-msvc": "4.34.7",
2449
+                "@rollup/rollup-win32-x64-msvc": "4.34.7",
2280 2450
                 "fsevents": "~2.3.2"
2281 2451
             }
2282 2452
         },
@@ -2624,15 +2794,15 @@
2624 2794
             "license": "MIT"
2625 2795
         },
2626 2796
         "node_modules/vite": {
2627
-            "version": "6.0.11",
2628
-            "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.11.tgz",
2629
-            "integrity": "sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==",
2797
+            "version": "6.1.0",
2798
+            "resolved": "https://registry.npmjs.org/vite/-/vite-6.1.0.tgz",
2799
+            "integrity": "sha512-RjjMipCKVoR4hVfPY6GQTgveinjNuyLw+qruksLDvA5ktI1150VmcMBKmQaEWJhg/j6Uaf6dNCNA0AfdzUb/hQ==",
2630 2800
             "dev": true,
2631 2801
             "license": "MIT",
2632 2802
             "dependencies": {
2633 2803
                 "esbuild": "^0.24.2",
2634
-                "postcss": "^8.4.49",
2635
-                "rollup": "^4.23.0"
2804
+                "postcss": "^8.5.1",
2805
+                "rollup": "^4.30.1"
2636 2806
             },
2637 2807
             "bin": {
2638 2808
                 "vite": "bin/vite.js"

+ 3
- 2
resources/data/lang/en.json Näytä tiedosto

@@ -195,5 +195,6 @@
195 195
     "Frequency": "Frequency",
196 196
     "Dates & Time": "Dates & Time",
197 197
     "account": "account",
198
-    "currency": "currency"
199
-}
198
+    "currency": "currency",
199
+    "Estimate": "Estimate"
200
+}

+ 4
- 1
resources/views/components/company/invoice/container.blade.php Näytä tiedosto

@@ -6,9 +6,12 @@
6 6
     <div
7 7
         @class([
8 8
             'inv-paper bg-[#ffffff] dark:bg-gray-800 rounded-sm shadow-xl',
9
-            'w-full max-w-[820px] min-h-[1024px]' => $preview === false,
9
+            'w-full max-w-[820px] max-h-[1024px] overflow-y-auto' => $preview === false,
10 10
             'w-[38.25rem] h-[49.5rem] overflow-hidden' => $preview === true,
11 11
         ])
12
+        @style([
13
+            'scrollbar-width: thin;' => $preview === false,
14
+        ])
12 15
     >
13 16
         {{ $slot }}
14 17
     </div>

+ 19
- 0
resources/views/components/icons/document-header-decoration.blade.php Näytä tiedosto

@@ -0,0 +1,19 @@
1
+@props([
2
+   'color' => 'currentColor',
3
+   'text' => '',
4
+])
5
+
6
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 72" aria-hidden="true" fill="none" {{ $attributes }}>
7
+   <g stroke="{{ $color }}" stroke-width="2">
8
+       <path d="M20 57.038v-42.076c.33.025.664.038 1 .038 7.18 0 13-5.82 13-13 0-.336-.013-.67-.038-1h172.076c-.025.33-.038.664-.038 1 0 7.18 5.82 13 13 13 .336 0 .67-.013 1-.038v42.076c-.33-.025-.664-.038-1-.038-7.18 0-13 5.82-13 13 0 .336.013.67.038 1h-172.076c.025-.33.038-.664.038-1 0-7.18-5.82-13-13-13-.336 0-.67.013-1 .038z" />
9
+       <path d="M26 51.503v-31.007c.33.024.664.037 1 .037 7.18 0 13-5.626 13-12.567 0-.325.013-.648-.038-.967h160.076c-.025.319-.038.641-.038.967 0 6.94 5.82 12.567 13 12.567.336 0 .67-.012 1-.037v31.007c-.33-.024-.664-.037-1-.037-7.18 0-13 5.626-13 12.567 0 .325.013.648.038.967h-160.076c.025-.319.038-.641.038-.967 0-6.94-5.82-12.567-13-12.567-.336 0-.67.012-1 .037z" />
10
+   </g>
11
+   <text
12
+       x="50%"
13
+       y="50%"
14
+       text-anchor="middle"
15
+       dominant-baseline="middle"
16
+       class="text-3xl font-light tracking-tight"
17
+       fill="{{ $color }}"
18
+   >{{ $text }}</text>
19
+</svg>

+ 5
- 10
resources/views/filament/company/components/invoice-layouts/classic.blade.php Näytä tiedosto

@@ -33,16 +33,11 @@
33 33
     <x-company.invoice.metadata class="classic-template-metadata">
34 34
         <div class="items-center flex">
35 35
             <hr class="grow-[2] py-0.5 border-solid border-y-2" style="border-color: {{ $document->accentColor }};">
36
-            <div class="items-center flex mx-5">
37
-                <x-icons.decor-border-left color="{{ $document->accentColor }}"/>
38
-                <div class="px-2.5 border-solid border-y-2 py-1 -mx-3" style="border-color: {{ $document->accentColor }};">
39
-                    <div class="px-2.5 border-solid border-y-2 py-3" style="border-color: {{ $document->accentColor }};">
40
-                        <div class="inline text-2xl font-semibold"
41
-                             style="color: {{ $document->accentColor }};">{{ $document->header }}</div>
42
-                    </div>
43
-                </div>
44
-                <x-icons.decor-border-right color="{{ $document->accentColor }}"/>
45
-            </div>
36
+            <x-icons.document-header-decoration
37
+                color="{{ $document->accentColor }}"
38
+                text="{{ $document->header }}"
39
+                class="w-48"
40
+            />
46 41
             <hr class="grow-[2] py-0.5 border-solid border-y-2" style="border-color: {{ $document->accentColor }};">
47 42
         </div>
48 43
         <div class="mt-2 text-sm text-center text-gray-600 dark:text-gray-400">{{ $document->subheader }}</div>

+ 5
- 145
resources/views/filament/infolists/components/document-preview.blade.php Näytä tiedosto

@@ -1,5 +1,6 @@
1 1
 @php
2 2
     $document = \App\DTO\DocumentDTO::fromModel($getRecord());
3
+    $template = $getTemplate();
3 4
 @endphp
4 5
 
5 6
 {!! $document->getFontHtml() !!}
@@ -11,149 +12,8 @@
11 12
 </style>
12 13
 
13 14
 <div {{ $attributes }}>
14
-    <x-company.invoice.container class="modern-template-container">
15
-        <!-- Colored Header with Logo -->
16
-        <x-company.invoice.header class="bg-gray-800 h-24">
17
-            <!-- Logo -->
18
-            <div class="w-2/3">
19
-                @if($document->logo && $document->showLogo)
20
-                    <x-company.invoice.logo class="ml-8" :src="$document->logo"/>
21
-                @endif
22
-            </div>
23
-
24
-            <!-- Ribbon Container -->
25
-            <div class="w-1/3 absolute right-0 top-0 p-3 h-32 flex flex-col justify-end rounded-bl-sm"
26
-                 style="background: {{ $document->accentColor }};">
27
-                @if($document->header)
28
-                    <h1 class="text-4xl font-bold text-white text-center uppercase">{{ $document->header }}</h1>
29
-                @endif
30
-            </div>
31
-        </x-company.invoice.header>
32
-
33
-        <!-- Company Details -->
34
-        <x-company.invoice.metadata class="modern-template-metadata space-y-8">
35
-            <div class="text-sm">
36
-                <h2 class="text-lg font-semibold">{{ $document->company->name }}</h2>
37
-                @if($formattedAddress = $document->company->getFormattedAddressHtml())
38
-                    {!! $formattedAddress !!}
39
-                @endif
40
-            </div>
41
-
42
-            <div class="flex justify-between items-end">
43
-                <!-- Billing Details -->
44
-                <div class="text-sm tracking-tight">
45
-                    <h3 class="text-gray-600 dark:text-gray-400 font-medium tracking-tight mb-1">BILL TO</h3>
46
-                    <p class="text-base font-bold"
47
-                       style="color: {{ $document->accentColor }}">{{ $document->client->name }}</p>
48
-
49
-                    @if($formattedAddress = $document->client->getFormattedAddressHtml())
50
-                        {!! $formattedAddress !!}
51
-                    @endif
52
-                </div>
53
-
54
-                <div class="text-sm tracking-tight">
55
-                    <table class="min-w-full">
56
-                        <tbody>
57
-                        <tr>
58
-                            <td class="font-semibold text-right pr-2">{{ $document->label->number }}:</td>
59
-                            <td class="text-left pl-2">{{ $document->number }}</td>
60
-                        </tr>
61
-                        @if($document->referenceNumber)
62
-                            <tr>
63
-                                <td class="font-semibold text-right pr-2">{{ $document->label->referenceNumber }}:</td>
64
-                                <td class="text-left pl-2">{{ $document->referenceNumber }}</td>
65
-                            </tr>
66
-                        @endif
67
-                        <tr>
68
-                            <td class="font-semibold text-right pr-2">{{ $document->label->date }}:</td>
69
-                            <td class="text-left pl-2">{{ $document->date }}</td>
70
-                        </tr>
71
-                        <tr>
72
-                            <td class="font-semibold text-right pr-2">{{ $document->label->dueDate }}:</td>
73
-                            <td class="text-left pl-2">{{ $document->dueDate }}</td>
74
-                        </tr>
75
-                        </tbody>
76
-                    </table>
77
-                </div>
78
-            </div>
79
-        </x-company.invoice.metadata>
80
-
81
-        <!-- Line Items Table -->
82
-        <x-company.invoice.line-items class="modern-template-line-items">
83
-            <table class="w-full text-left table-fixed">
84
-                <thead class="text-sm leading-relaxed">
85
-                <tr class="text-gray-600 dark:text-gray-400">
86
-                    <th class="text-left pl-6 w-[50%] py-4">{{ $document->columnLabel->items }}</th>
87
-                    <th class="text-center w-[10%] py-4">{{ $document->columnLabel->units }}</th>
88
-                    <th class="text-right w-[20%] py-4">{{ $document->columnLabel->price }}</th>
89
-                    <th class="text-right pr-6 w-[20%] py-4">{{ $document->columnLabel->amount }}</th>
90
-                </tr>
91
-                </thead>
92
-                <tbody class="text-sm tracking-tight border-y-2">
93
-                @foreach($document->lineItems as $index => $item)
94
-                    <tr @class(['bg-gray-100 dark:bg-gray-800' => $index % 2 === 0])>
95
-                        <td class="text-left pl-6 font-semibold py-3">
96
-                            {{ $item->name }}
97
-                            @if($item->description)
98
-                                <div class="text-gray-600 font-normal line-clamp-2 mt-1">{{ $item->description }}</div>
99
-                            @endif
100
-                        </td>
101
-                        <td class="text-center py-3">{{ $item->quantity }}</td>
102
-                        <td class="text-right py-3">{{ $item->unitPrice }}</td>
103
-                        <td class="text-right pr-6 py-3">{{ $item->subtotal }}</td>
104
-                    </tr>
105
-                @endforeach
106
-                </tbody>
107
-                <tfoot class="text-sm tracking-tight">
108
-                <tr>
109
-                    <td class="pl-6 py-2" colspan="2"></td>
110
-                    <td class="text-right font-semibold py-2">Subtotal:</td>
111
-                    <td class="text-right pr-6 py-2">{{ $document->subtotal }}</td>
112
-                </tr>
113
-                @if($document->discount)
114
-                    <tr class="text-success-800 dark:text-success-600">
115
-                        <td class="pl-6 py-2" colspan="2"></td>
116
-                        <td class="text-right py-2">Discount:</td>
117
-                        <td class="text-right pr-6 py-2">
118
-                            ({{ $document->discount }})
119
-                        </td>
120
-                    </tr>
121
-                @endif
122
-                @if($document->tax)
123
-                    <tr>
124
-                        <td class="pl-6 py-2" colspan="2"></td>
125
-                        <td class="text-right py-2">Tax:</td>
126
-                        <td class="text-right pr-6 py-2">{{ $document->tax }}</td>
127
-                    </tr>
128
-                @endif
129
-                <tr>
130
-                    <td class="pl-6 py-2" colspan="2"></td>
131
-                    <td class="text-right font-semibold border-t py-2">Total:</td>
132
-                    <td class="text-right border-t pr-6 py-2">{{ $document->total }}</td>
133
-                </tr>
134
-                @if($document->amountDue)
135
-                    <tr>
136
-                        <td class="pl-6 py-2" colspan="2"></td>
137
-                        <td class="text-right font-semibold border-t-4 border-double py-2">{{ $document->label->amountDue }}
138
-                            ({{ $document->currencyCode }}):
139
-                        </td>
140
-                        <td class="text-right border-t-4 border-double pr-6 py-2">{{ $document->amountDue }}</td>
141
-                    </tr>
142
-                @endif
143
-                </tfoot>
144
-            </table>
145
-        </x-company.invoice.line-items>
146
-
147
-        <!-- Footer Notes -->
148
-        <x-company.invoice.footer class="modern-template-footer tracking-tight">
149
-            <h4 class="font-semibold px-6 text-sm" style="color: {{ $document->accentColor }}">
150
-                Terms & Conditions
151
-            </h4>
152
-            <span class="border-t-2 my-2 border-gray-300 block w-full"></span>
153
-            <div class="flex justify-between space-x-4 px-6 text-sm">
154
-                <p class="w-1/2 break-words line-clamp-4">{{ $document->terms }}</p>
155
-                <p class="w-1/2 break-words line-clamp-4">{{ $document->footer }}</p>
156
-            </div>
157
-        </x-company.invoice.footer>
158
-    </x-company.invoice.container>
15
+    @include("filament.infolists.components.document-templates.{$template->value}", [
16
+        'document' => $document,
17
+        'preview' => false,
18
+    ])
159 19
 </div>

+ 148
- 0
resources/views/filament/infolists/components/document-templates/classic.blade.php Näytä tiedosto

@@ -0,0 +1,148 @@
1
+<x-company.invoice.container class="classic-template-container">
2
+    <!-- Header Section -->
3
+    <x-company.invoice.header class="default-template-header">
4
+        <div class="w-2/3 text-left ml-6">
5
+            <div class="text-sm tracking-tight">
6
+                <h2 class="text-lg font-semibold">{{ $document->company->name }}</h2>
7
+                @if($formattedAddress = $document->company->getFormattedAddressHtml())
8
+                    {!! $formattedAddress !!}
9
+                @endif
10
+            </div>
11
+        </div>
12
+
13
+        <div class="w-1/3 flex justify-end mr-6">
14
+            @if($document->logo && $document->showLogo)
15
+                <x-company.invoice.logo :src="$document->logo"/>
16
+            @endif
17
+        </div>
18
+    </x-company.invoice.header>
19
+
20
+    <x-company.invoice.metadata class="classic-template-metadata space-y-8">
21
+        <div class="items-center flex">
22
+            <hr class="grow-[2] py-0.5 border-solid border-y-2" style="border-color: {{ $document->accentColor }};">
23
+            <x-icons.document-header-decoration
24
+                color="{{ $document->accentColor }}"
25
+                text="{{ $document->header }}"
26
+                class="w-60"
27
+            />
28
+            <hr class="grow-[2] py-0.5 border-solid border-y-2" style="border-color: {{ $document->accentColor }};">
29
+        </div>
30
+        <div class="mt-2 text-base text-center text-gray-600 dark:text-gray-400">{{ $document->subheader }}</div>
31
+
32
+        <div class="flex justify-between items-end">
33
+            <!-- Billing Details -->
34
+            <div class="text-sm tracking-tight">
35
+                <h3 class="text-gray-600 dark:text-gray-400 font-medium tracking-tight mb-1">BILL TO</h3>
36
+                <p class="text-base font-bold">{{ $document->client->name }}</p>
37
+                @if($formattedAddress = $document->client->getFormattedAddressHtml())
38
+                    {!! $formattedAddress !!}
39
+                @endif
40
+            </div>
41
+
42
+            <div class="text-sm tracking-tight">
43
+                <table class="min-w-full">
44
+                    <tbody>
45
+                    <tr>
46
+                        <td class="font-semibold text-right pr-2">{{ $document->label->number }}:</td>
47
+                        <td class="text-left pl-2">{{ $document->number }}</td>
48
+                    </tr>
49
+                    <tr>
50
+                        <td class="font-semibold text-right pr-2">{{ $document->label->referenceNumber }}:</td>
51
+                        <td class="text-left pl-2">{{ $document->referenceNumber }}</td>
52
+                    </tr>
53
+                    <tr>
54
+                        <td class="font-semibold text-right pr-2">{{ $document->label->date }}:</td>
55
+                        <td class="text-left pl-2">{{ $document->date }}</td>
56
+                    </tr>
57
+                    <tr>
58
+                        <td class="font-semibold text-right pr-2">{{ $document->label->dueDate }}:</td>
59
+                        <td class="text-left pl-2">{{ $document->dueDate }}</td>
60
+                    </tr>
61
+                    </tbody>
62
+                </table>
63
+            </div>
64
+        </div>
65
+    </x-company.invoice.metadata>
66
+
67
+    <!-- Line Items -->
68
+    <x-company.invoice.line-items class="classic-template-line-items">
69
+        <table class="w-full text-left table-fixed">
70
+            <thead class="text-sm leading-relaxed">
71
+            <tr>
72
+                <th class="text-left pl-6 w-[50%] py-4">{{ $document->columnLabel->items }}</th>
73
+                <th class="text-center w-[10%] py-4">{{ $document->columnLabel->units }}</th>
74
+                <th class="text-right w-[20%] py-4">{{ $document->columnLabel->price }}</th>
75
+                <th class="text-right pr-6 w-[20%] py-4">{{ $document->columnLabel->amount }}</th>
76
+            </tr>
77
+            </thead>
78
+            <tbody class="text-sm tracking-tight border-y-2 border-dotted border-gray-300">
79
+            @foreach($document->lineItems as $item)
80
+                <tr>
81
+                    <td class="text-left pl-6 font-semibold py-3">
82
+                        {{ $item->name }}
83
+                        @if($item->description)
84
+                            <div class="text-gray-600 font-normal line-clamp-2 mt-1">{{ $item->description }}</div>
85
+                        @endif
86
+                    </td>
87
+                    <td class="text-center py-3">{{ $item->quantity }}</td>
88
+                    <td class="text-right py-3">{{ $item->unitPrice }}</td>
89
+                    <td class="text-right pr-6 py-3">{{ $item->subtotal }}</td>
90
+                </tr>
91
+            @endforeach
92
+            </tbody>
93
+        </table>
94
+
95
+        <!-- Financial Details and Notes -->
96
+        <div class="flex justify-between text-sm tracking-tight space-x-1">
97
+            <!-- Notes Section -->
98
+            <div class="w-[60%] border border-dashed border-gray-300 p-2 mt-4">
99
+                <h4 class="font-semibold mb-2">Notes</h4>
100
+                <p>{{ $document->footer }}</p>
101
+            </div>
102
+
103
+            <!-- Financial Summary -->
104
+            <div class="w-[40%] mt-2">
105
+                <table class="w-full table-fixed">
106
+                    <tbody class="text-sm tracking-tight">
107
+                    <tr>
108
+                        <td class="text-right font-semibold py-2">Subtotal:</td>
109
+                        <td class="text-right pr-6 py-2">{{ $document->subtotal }}</td>
110
+                    </tr>
111
+                    @if($document->discount)
112
+                        <tr class="text-success-800 dark:text-success-600">
113
+                            <td class="text-right py-2">Discount:</td>
114
+                            <td class="text-right pr-6 py-2">
115
+                                ({{ $document->discount }})
116
+                            </td>
117
+                        </tr>
118
+                    @endif
119
+                    @if($document->tax)
120
+                        <tr>
121
+                            <td class="text-right py-2">Tax:</td>
122
+                            <td class="text-right pr-6 py-2">{{ $document->tax }}</td>
123
+                        </tr>
124
+                    @endif
125
+                    <tr>
126
+                        <td class="text-right font-semibold border-t py-2">Total:</td>
127
+                        <td class="text-right border-t pr-6 py-2">{{ $document->total }}</td>
128
+                    </tr>
129
+                    @if($document->amountDue)
130
+                        <tr>
131
+                            <td class="text-right font-semibold border-t-4 border-double py-2">{{ $document->label->amountDue }}
132
+                                ({{ $document->currencyCode }}):
133
+                            </td>
134
+                            <td class="text-right border-t-4 border-double pr-6 py-2">{{ $document->amountDue }}</td>
135
+                        </tr>
136
+                    @endif
137
+                    </tbody>
138
+                </table>
139
+            </div>
140
+        </div>
141
+    </x-company.invoice.line-items>
142
+
143
+    <!-- Footer -->
144
+    <x-company.invoice.footer class="classic-template-footer tracking-tight min-h-48">
145
+        <h4 class="font-semibold px-6 mb-2 text-sm">Terms & Conditions</h4>
146
+        <p class="px-6 break-words line-clamp-4 text-sm">{{ $document->terms }}</p>
147
+    </x-company.invoice.footer>
148
+</x-company.invoice.container>

+ 138
- 0
resources/views/filament/infolists/components/document-templates/default.blade.php Näytä tiedosto

@@ -0,0 +1,138 @@
1
+<x-company.invoice.container class="default-template-container">
2
+
3
+    <x-company.invoice.header class="default-template-header border-b-2 p-6 pb-4">
4
+        <div class="w-2/3">
5
+            @if($document->logo && $document->showLogo)
6
+                <x-company.invoice.logo :src="$document->logo"/>
7
+            @endif
8
+        </div>
9
+
10
+        <div class="w-1/3 text-right">
11
+            <div class="text-sm tracking-tight">
12
+                <h2 class="text-lg font-semibold">{{ $document->company->name }}</h2>
13
+                @if($formattedAddress = $document->company->getFormattedAddressHtml())
14
+                    {!! $formattedAddress !!}
15
+                @endif
16
+            </div>
17
+        </div>
18
+    </x-company.invoice.header>
19
+
20
+    <x-company.invoice.metadata class="default-template-metadata space-y-8">
21
+        <div>
22
+            <h1 class="text-4xl font-light uppercase">{{ $document->header }}</h1>
23
+            @if ($document->subheader)
24
+                <h2 class="text-base text-gray-600 dark:text-gray-400">{{ $document->subheader }}</h2>
25
+            @endif
26
+        </div>
27
+
28
+        <div class="flex justify-between items-end">
29
+            <!-- Billing Details -->
30
+            <div class="text-sm tracking-tight">
31
+                <h3 class="text-gray-600 dark:text-gray-400 font-medium tracking-tight mb-1">BILL TO</h3>
32
+                <p class="text-base font-bold">{{ $document->client->name }}</p>
33
+                @if($formattedAddress = $document->client->getFormattedAddressHtml())
34
+                    {!! $formattedAddress !!}
35
+                @endif
36
+            </div>
37
+
38
+            <div class="text-sm tracking-tight">
39
+                <table class="min-w-full">
40
+                    <tbody>
41
+                    <tr>
42
+                        <td class="font-semibold text-right pr-2">{{ $document->label->number }}:</td>
43
+                        <td class="text-left pl-2">{{ $document->number }}</td>
44
+                    </tr>
45
+                    @if($document->referenceNumber)
46
+                        <tr>
47
+                            <td class="font-semibold text-right pr-2">{{ $document->label->referenceNumber }}:</td>
48
+                            <td class="text-left pl-2">{{ $document->referenceNumber }}</td>
49
+                        </tr>
50
+                    @endif
51
+                    <tr>
52
+                        <td class="font-semibold text-right pr-2">{{ $document->label->date }}:</td>
53
+                        <td class="text-left pl-2">{{ $document->date }}</td>
54
+                    </tr>
55
+                    <tr>
56
+                        <td class="font-semibold text-right pr-2">{{ $document->label->dueDate }}:</td>
57
+                        <td class="text-left pl-2">{{ $document->dueDate }}</td>
58
+                    </tr>
59
+                    </tbody>
60
+                </table>
61
+            </div>
62
+        </div>
63
+    </x-company.invoice.metadata>
64
+
65
+    <!-- Line Items Table -->
66
+    <x-company.invoice.line-items class="default-template-line-items">
67
+        <table class="w-full text-left table-fixed">
68
+            <thead class="text-sm leading-relaxed" style="background: {{ $document->accentColor }}">
69
+            <tr class="text-white">
70
+                <th class="text-left pl-6 w-[50%] py-2">{{ $document->columnLabel->items }}</th>
71
+                <th class="text-center w-[10%] py-2">{{ $document->columnLabel->units }}</th>
72
+                <th class="text-right w-[20%] py-2">{{ $document->columnLabel->price }}</th>
73
+                <th class="text-right pr-6 w-[20%] py-2">{{ $document->columnLabel->amount }}</th>
74
+            </tr>
75
+            </thead>
76
+            <tbody class="text-sm tracking-tight border-b-2 border-gray-300">
77
+            @foreach($document->lineItems as $item)
78
+                <tr>
79
+                    <td class="text-left pl-6 font-semibold py-3">
80
+                        {{ $item->name }}
81
+                        @if($item->description)
82
+                            <div class="text-gray-600 font-normal line-clamp-2 mt-1">{{ $item->description }}</div>
83
+                        @endif
84
+                    </td>
85
+                    <td class="text-center py-3">{{ $item->quantity }}</td>
86
+                    <td class="text-right py-3">{{ $item->unitPrice }}</td>
87
+                    <td class="text-right pr-6 py-3">{{ $item->subtotal }}</td>
88
+                </tr>
89
+            @endforeach
90
+            </tbody>
91
+            <tfoot class="text-sm tracking-tight">
92
+            <tr>
93
+                <td class="pl-6 py-2" colspan="2"></td>
94
+                <td class="text-right font-semibold py-2">Subtotal:</td>
95
+                <td class="text-right pr-6 py-2">{{ $document->subtotal }}</td>
96
+            </tr>
97
+            @if($document->discount)
98
+                <tr class="text-success-800 dark:text-success-600">
99
+                    <td class="pl-6 py-2" colspan="2"></td>
100
+                    <td class="text-right py-2">Discount:</td>
101
+                    <td class="text-right pr-6 py-2">
102
+                        ({{ $document->discount }})
103
+                    </td>
104
+                </tr>
105
+            @endif
106
+            @if($document->tax)
107
+                <tr>
108
+                    <td class="pl-6 py-2" colspan="2"></td>
109
+                    <td class="text-right py-2">Tax:</td>
110
+                    <td class="text-right pr-6 py-2">{{ $document->tax }}</td>
111
+                </tr>
112
+            @endif
113
+            <tr>
114
+                <td class="pl-6 py-2" colspan="2"></td>
115
+                <td class="text-right font-semibold border-t py-2">Total:</td>
116
+                <td class="text-right border-t pr-6 py-2">{{ $document->total }}</td>
117
+            </tr>
118
+            @if($document->amountDue)
119
+                <tr>
120
+                    <td class="pl-6 py-2" colspan="2"></td>
121
+                    <td class="text-right font-semibold border-t-4 border-double py-2">{{ $document->label->amountDue }}
122
+                        ({{ $document->currencyCode }}):
123
+                    </td>
124
+                    <td class="text-right border-t-4 border-double pr-6 py-2">{{ $document->amountDue }}</td>
125
+                </tr>
126
+            @endif
127
+            </tfoot>
128
+        </table>
129
+    </x-company.invoice.line-items>
130
+
131
+    <!-- Footer Notes -->
132
+    <x-company.invoice.footer class="default-template-footer tracking-tight">
133
+        <p class="px-6 text-sm">{{ $document->footer }}</p>
134
+        <span class="border-t-2 my-2 border-gray-300 block w-full"></span>
135
+        <h4 class="font-semibold px-6 mb-2 text-sm">Terms & Conditions</h4>
136
+        <p class="px-6 break-words line-clamp-4 text-sm">{{ $document->terms }}</p>
137
+    </x-company.invoice.footer>
138
+</x-company.invoice.container>

+ 145
- 0
resources/views/filament/infolists/components/document-templates/modern.blade.php Näytä tiedosto

@@ -0,0 +1,145 @@
1
+<x-company.invoice.container class="modern-template-container">
2
+    <!-- Colored Header with Logo -->
3
+    <x-company.invoice.header class="bg-gray-800 h-24">
4
+        <!-- Logo -->
5
+        <div class="w-2/3">
6
+            @if($document->logo && $document->showLogo)
7
+                <x-company.invoice.logo class="ml-8" :src="$document->logo"/>
8
+            @endif
9
+        </div>
10
+
11
+        <!-- Ribbon Container -->
12
+        <div class="w-1/3 absolute right-0 top-0 p-3 h-32 flex flex-col justify-end rounded-bl-sm"
13
+             style="background: {{ $document->accentColor }};">
14
+            @if($document->header)
15
+                <h1 class="text-4xl font-bold text-white text-center uppercase">{{ $document->header }}</h1>
16
+            @endif
17
+        </div>
18
+    </x-company.invoice.header>
19
+
20
+    <!-- Company Details -->
21
+    <x-company.invoice.metadata class="modern-template-metadata space-y-8">
22
+        <div class="text-sm">
23
+            <h2 class="text-lg font-semibold">{{ $document->company->name }}</h2>
24
+            @if($formattedAddress = $document->company->getFormattedAddressHtml())
25
+                {!! $formattedAddress !!}
26
+            @endif
27
+        </div>
28
+
29
+        <div class="flex justify-between items-end">
30
+            <!-- Billing Details -->
31
+            <div class="text-sm tracking-tight">
32
+                <h3 class="text-gray-600 dark:text-gray-400 font-medium tracking-tight mb-1">BILL TO</h3>
33
+                <p class="text-base font-bold"
34
+                   style="color: {{ $document->accentColor }}">{{ $document->client->name }}</p>
35
+
36
+                @if($formattedAddress = $document->client->getFormattedAddressHtml())
37
+                    {!! $formattedAddress !!}
38
+                @endif
39
+            </div>
40
+
41
+            <div class="text-sm tracking-tight">
42
+                <table class="min-w-full">
43
+                    <tbody>
44
+                    <tr>
45
+                        <td class="font-semibold text-right pr-2">{{ $document->label->number }}:</td>
46
+                        <td class="text-left pl-2">{{ $document->number }}</td>
47
+                    </tr>
48
+                    @if($document->referenceNumber)
49
+                        <tr>
50
+                            <td class="font-semibold text-right pr-2">{{ $document->label->referenceNumber }}:</td>
51
+                            <td class="text-left pl-2">{{ $document->referenceNumber }}</td>
52
+                        </tr>
53
+                    @endif
54
+                    <tr>
55
+                        <td class="font-semibold text-right pr-2">{{ $document->label->date }}:</td>
56
+                        <td class="text-left pl-2">{{ $document->date }}</td>
57
+                    </tr>
58
+                    <tr>
59
+                        <td class="font-semibold text-right pr-2">{{ $document->label->dueDate }}:</td>
60
+                        <td class="text-left pl-2">{{ $document->dueDate }}</td>
61
+                    </tr>
62
+                    </tbody>
63
+                </table>
64
+            </div>
65
+        </div>
66
+    </x-company.invoice.metadata>
67
+
68
+    <!-- Line Items Table -->
69
+    <x-company.invoice.line-items class="modern-template-line-items">
70
+        <table class="w-full text-left table-fixed">
71
+            <thead class="text-sm leading-relaxed">
72
+            <tr class="text-gray-600 dark:text-gray-400">
73
+                <th class="text-left pl-6 w-[50%] py-4">{{ $document->columnLabel->items }}</th>
74
+                <th class="text-center w-[10%] py-4">{{ $document->columnLabel->units }}</th>
75
+                <th class="text-right w-[20%] py-4">{{ $document->columnLabel->price }}</th>
76
+                <th class="text-right pr-6 w-[20%] py-4">{{ $document->columnLabel->amount }}</th>
77
+            </tr>
78
+            </thead>
79
+            <tbody class="text-sm tracking-tight border-y-2">
80
+            @foreach($document->lineItems as $index => $item)
81
+                <tr @class(['bg-gray-100 dark:bg-gray-800' => $index % 2 === 0])>
82
+                    <td class="text-left pl-6 font-semibold py-3">
83
+                        {{ $item->name }}
84
+                        @if($item->description)
85
+                            <div class="text-gray-600 font-normal line-clamp-2 mt-1">{{ $item->description }}</div>
86
+                        @endif
87
+                    </td>
88
+                    <td class="text-center py-3">{{ $item->quantity }}</td>
89
+                    <td class="text-right py-3">{{ $item->unitPrice }}</td>
90
+                    <td class="text-right pr-6 py-3">{{ $item->subtotal }}</td>
91
+                </tr>
92
+            @endforeach
93
+            </tbody>
94
+            <tfoot class="text-sm tracking-tight">
95
+            <tr>
96
+                <td class="pl-6 py-2" colspan="2"></td>
97
+                <td class="text-right font-semibold py-2">Subtotal:</td>
98
+                <td class="text-right pr-6 py-2">{{ $document->subtotal }}</td>
99
+            </tr>
100
+            @if($document->discount)
101
+                <tr class="text-success-800 dark:text-success-600">
102
+                    <td class="pl-6 py-2" colspan="2"></td>
103
+                    <td class="text-right py-2">Discount:</td>
104
+                    <td class="text-right pr-6 py-2">
105
+                        ({{ $document->discount }})
106
+                    </td>
107
+                </tr>
108
+            @endif
109
+            @if($document->tax)
110
+                <tr>
111
+                    <td class="pl-6 py-2" colspan="2"></td>
112
+                    <td class="text-right py-2">Tax:</td>
113
+                    <td class="text-right pr-6 py-2">{{ $document->tax }}</td>
114
+                </tr>
115
+            @endif
116
+            <tr>
117
+                <td class="pl-6 py-2" colspan="2"></td>
118
+                <td class="text-right font-semibold border-t py-2">Total:</td>
119
+                <td class="text-right border-t pr-6 py-2">{{ $document->total }}</td>
120
+            </tr>
121
+            @if($document->amountDue)
122
+                <tr>
123
+                    <td class="pl-6 py-2" colspan="2"></td>
124
+                    <td class="text-right font-semibold border-t-4 border-double py-2">{{ $document->label->amountDue }}
125
+                        ({{ $document->currencyCode }}):
126
+                    </td>
127
+                    <td class="text-right border-t-4 border-double pr-6 py-2">{{ $document->amountDue }}</td>
128
+                </tr>
129
+            @endif
130
+            </tfoot>
131
+        </table>
132
+    </x-company.invoice.line-items>
133
+
134
+    <!-- Footer Notes -->
135
+    <x-company.invoice.footer class="modern-template-footer tracking-tight">
136
+        <h4 class="font-semibold px-6 text-sm" style="color: {{ $document->accentColor }}">
137
+            Terms & Conditions
138
+        </h4>
139
+        <span class="border-t-2 my-2 border-gray-300 block w-full"></span>
140
+        <div class="flex justify-between space-x-4 px-6 text-sm">
141
+            <p class="w-1/2 break-words line-clamp-4">{{ $document->terms }}</p>
142
+            <p class="w-1/2 break-words line-clamp-4">{{ $document->footer }}</p>
143
+        </div>
144
+    </x-company.invoice.footer>
145
+</x-company.invoice.container>

Loading…
Peruuta
Tallenna