@rolatech/angular-billing 20.3.2-beta.3 → 20.3.3-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, output, Component, ViewEncapsulation, inject, signal, computed, model, Injectable } from '@angular/core';
|
|
2
|
+
import { input, output, Component, ViewEncapsulation, inject, signal, computed, model, Injectable, effect, ChangeDetectionStrategy } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/common';
|
|
4
4
|
import { CommonModule, DatePipe, KeyValuePipe } from '@angular/common';
|
|
5
5
|
import * as i1$2 from '@angular/forms';
|
|
6
|
-
import { FormsModule } from '@angular/forms';
|
|
6
|
+
import { FormsModule, Validators, FormBuilder, ReactiveFormsModule } from '@angular/forms';
|
|
7
7
|
import * as i2 from '@angular/material/button';
|
|
8
8
|
import { MatButtonModule } from '@angular/material/button';
|
|
9
9
|
import * as i4 from '@angular/material/form-field';
|
|
@@ -14,9 +14,9 @@ import * as i5 from '@angular/material/select';
|
|
|
14
14
|
import { MatSelectModule } from '@angular/material/select';
|
|
15
15
|
import * as i1$1 from '@angular/router';
|
|
16
16
|
import { RouterModule, RouterLink } from '@angular/router';
|
|
17
|
-
import { Skeleton, BaseComponent, PageCollectionShellComponent, ToolbarComponent, TabsComponent, TabComponent, EnumSelect, ConfirmationDialogComponent, MaterialModule } from '@rolatech/angular-components';
|
|
17
|
+
import { Skeleton, BaseComponent, PageCollectionShellComponent, ToolbarComponent, TabsComponent, TabComponent, EnumSelect, ConfirmationDialogComponent, MaterialModule, Breadcrumb } from '@rolatech/angular-components';
|
|
18
18
|
import { PricePipe } from '@rolatech/angular-common';
|
|
19
|
-
import { InvoiceService, PaymentService, EnumApiClient } from '@rolatech/angular-services';
|
|
19
|
+
import { InvoiceService, PaymentService, EnumApiClient, BillingProfileService } from '@rolatech/angular-services';
|
|
20
20
|
import * as i6 from '@angular/material/paginator';
|
|
21
21
|
import { MatPaginatorModule } from '@angular/material/paginator';
|
|
22
22
|
import { map, distinctUntilChanged, switchMap, finalize } from 'rxjs';
|
|
@@ -24,7 +24,7 @@ import * as i8 from '@angular/material/card';
|
|
|
24
24
|
import { MatCardModule } from '@angular/material/card';
|
|
25
25
|
import * as i7 from '@angular/material/divider';
|
|
26
26
|
import { MatDividerModule } from '@angular/material/divider';
|
|
27
|
-
import * as
|
|
27
|
+
import * as i4$1 from '@angular/material/input';
|
|
28
28
|
import { MatInputModule } from '@angular/material/input';
|
|
29
29
|
import * as i3$1 from '@angular/material/menu';
|
|
30
30
|
import { MatMenuModule } from '@angular/material/menu';
|
|
@@ -35,6 +35,8 @@ import { finalize as finalize$1 } from 'rxjs/operators';
|
|
|
35
35
|
import * as i1$3 from '@angular/cdk/drag-drop';
|
|
36
36
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
|
37
37
|
import { MatDialog } from '@angular/material/dialog';
|
|
38
|
+
import * as i5$1 from '@angular/material/slide-toggle';
|
|
39
|
+
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
|
38
40
|
|
|
39
41
|
const billingRoutes = [];
|
|
40
42
|
|
|
@@ -73,7 +75,7 @@ var InvoiceStatus;
|
|
|
73
75
|
|
|
74
76
|
class InvoiceItem {
|
|
75
77
|
constructor() {
|
|
76
|
-
this.invoice = input.required(...(ngDevMode ? [{ debugName: "invoice" }] : []));
|
|
78
|
+
this.invoice = input.required(...(ngDevMode ? [{ debugName: "invoice" }] : /* istanbul ignore next */ []));
|
|
77
79
|
this.status = InvoiceStatus;
|
|
78
80
|
this.download = output();
|
|
79
81
|
}
|
|
@@ -99,31 +101,31 @@ class InvoiceItem {
|
|
|
99
101
|
return 'invoice-item__status invoice-item__status--neutral';
|
|
100
102
|
}
|
|
101
103
|
}
|
|
102
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
103
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
104
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceItem, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
105
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: InvoiceItem, isStandalone: true, selector: "rolatech-invoice-item", inputs: { invoice: { classPropertyName: "invoice", publicName: "invoice", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { download: "download" }, ngImport: i0, template: "<article class=\"invoice-item\">\n <div class=\"invoice-item__top\">\n <div class=\"invoice-item__identity\">\n <span class=\"invoice-item__eyebrow\">Invoice</span>\n <h3 class=\"invoice-item__number\">{{ invoice().invoiceNumber || invoice().id }}</h3>\n <p class=\"invoice-item__customer\">{{ customerName() }}</p>\n </div>\n\n <div class=\"invoice-item__summary\">\n <span [ngClass]=\"statusBadgeClass(invoice().status)\">{{ status[invoice().status] }}</span>\n <strong class=\"invoice-item__total\">{{ invoice().total | price }}</strong>\n </div>\n </div>\n\n <div class=\"invoice-item__meta\">\n <div class=\"invoice-item__meta-card\">\n <span class=\"invoice-item__meta-label\">Created</span>\n <strong>{{ invoice().createdAt | date: 'dd/MM/yyyy':'Europe/London' }}</strong>\n </div>\n\n <div class=\"invoice-item__meta-card\">\n <span class=\"invoice-item__meta-label\">Due</span>\n <strong>\n @if (invoice().dueAt) {\n {{ invoice().dueAt | date: 'dd/MM/yyyy':'Europe/London' }}\n } @else {\n On receipt\n }\n </strong>\n </div>\n\n <div class=\"invoice-item__meta-card\">\n <span class=\"invoice-item__meta-label\">Type</span>\n <strong>{{ invoice().type }}</strong>\n </div>\n\n <button mat-stroked-button class=\"invoice-item__download\" (click)=\"onDownload(); $event.stopPropagation()\">\n <mat-icon>download</mat-icon>\n <span>Download</span>\n </button>\n </div>\n</article>\n", styles: [".scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}:host{display:block;--rt-workspace-status-issued-surface: color-mix(in srgb, var(--rt-brand-color) 14%, transparent);--rt-workspace-status-issued-color: color-mix(in srgb, var(--rt-brand-color) 82%, var(--rt-text-primary) 18%);--rt-workspace-status-paid-surface: color-mix(in srgb, var(--rt-brand-color) 16%, var(--rt-base-background, #ffffff));--rt-workspace-status-paid-color: color-mix(in srgb, var(--rt-brand-color) 58%, var(--rt-text-primary) 42%)}.invoice-item{display:flex;flex-direction:column;gap:1rem;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 18px 40px -36px color-mix(in srgb,var(--rt-text-primary) 18%,transparent)}.invoice-item__top{display:flex;justify-content:space-between;gap:1rem;align-items:flex-start}.invoice-item__identity{display:flex;flex-direction:column;gap:.32rem}.invoice-item__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.28rem .62rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase}.invoice-item__number{margin:0;color:var(--rt-text-primary);font-size:1.08rem;font-weight:700}.invoice-item__customer{margin:0;color:var(--rt-text-secondary);line-height:1.5}.invoice-item__summary{display:flex;flex-direction:column;gap:.5rem;align-items:flex-end}.invoice-item__total{color:var(--rt-text-primary);font-size:1.08rem}.invoice-item__status{display:inline-flex;align-items:center;border-radius:9999px;padding:.42rem .78rem;font-size:.78rem;font-weight:700}.invoice-item__status--draft{background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color)}.invoice-item__status--issued{background:var(--rt-workspace-status-issued-surface);color:var(--rt-workspace-status-issued-color)}.invoice-item__status--paid{background:var(--rt-workspace-status-paid-surface);color:var(--rt-workspace-status-paid-color)}.invoice-item__status--void{background:color-mix(in srgb,var(--mat-sys-error, #b91c1c) 14%,transparent);color:var(--mat-sys-error, #b91c1c)}.invoice-item__status--neutral{background:color-mix(in srgb,var(--rt-10-percent-layer, rgba(15, 23, 42, .08)) 72%,transparent);color:var(--rt-text-secondary)}.invoice-item__meta{display:grid;gap:.85rem}.invoice-item__meta-card{display:flex;flex-direction:column;gap:.22rem;padding:.85rem .95rem;border-radius:1rem;background:color-mix(in srgb,var(--rt-10-percent-layer, rgba(15, 23, 42, .08)) 72%,transparent)}.invoice-item__meta-label{color:var(--rt-text-secondary);font-size:.82rem}.invoice-item__meta-card strong{color:var(--rt-text-primary);font-size:.96rem}.invoice-item__download{min-height:100%;border-radius:1rem}@media(min-width:900px){.invoice-item__meta{grid-template-columns:repeat(4,minmax(0,1fr));align-items:stretch}.invoice-item__download{justify-content:center}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }] }); }
|
|
104
106
|
}
|
|
105
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
107
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceItem, decorators: [{
|
|
106
108
|
type: Component,
|
|
107
109
|
args: [{ selector: 'rolatech-invoice-item', imports: [CommonModule, MatButtonModule, MatIcon, DatePipe, PricePipe], template: "<article class=\"invoice-item\">\n <div class=\"invoice-item__top\">\n <div class=\"invoice-item__identity\">\n <span class=\"invoice-item__eyebrow\">Invoice</span>\n <h3 class=\"invoice-item__number\">{{ invoice().invoiceNumber || invoice().id }}</h3>\n <p class=\"invoice-item__customer\">{{ customerName() }}</p>\n </div>\n\n <div class=\"invoice-item__summary\">\n <span [ngClass]=\"statusBadgeClass(invoice().status)\">{{ status[invoice().status] }}</span>\n <strong class=\"invoice-item__total\">{{ invoice().total | price }}</strong>\n </div>\n </div>\n\n <div class=\"invoice-item__meta\">\n <div class=\"invoice-item__meta-card\">\n <span class=\"invoice-item__meta-label\">Created</span>\n <strong>{{ invoice().createdAt | date: 'dd/MM/yyyy':'Europe/London' }}</strong>\n </div>\n\n <div class=\"invoice-item__meta-card\">\n <span class=\"invoice-item__meta-label\">Due</span>\n <strong>\n @if (invoice().dueAt) {\n {{ invoice().dueAt | date: 'dd/MM/yyyy':'Europe/London' }}\n } @else {\n On receipt\n }\n </strong>\n </div>\n\n <div class=\"invoice-item__meta-card\">\n <span class=\"invoice-item__meta-label\">Type</span>\n <strong>{{ invoice().type }}</strong>\n </div>\n\n <button mat-stroked-button class=\"invoice-item__download\" (click)=\"onDownload(); $event.stopPropagation()\">\n <mat-icon>download</mat-icon>\n <span>Download</span>\n </button>\n </div>\n</article>\n", styles: [".scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}:host{display:block;--rt-workspace-status-issued-surface: color-mix(in srgb, var(--rt-brand-color) 14%, transparent);--rt-workspace-status-issued-color: color-mix(in srgb, var(--rt-brand-color) 82%, var(--rt-text-primary) 18%);--rt-workspace-status-paid-surface: color-mix(in srgb, var(--rt-brand-color) 16%, var(--rt-base-background, #ffffff));--rt-workspace-status-paid-color: color-mix(in srgb, var(--rt-brand-color) 58%, var(--rt-text-primary) 42%)}.invoice-item{display:flex;flex-direction:column;gap:1rem;padding:1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 18px 40px -36px color-mix(in srgb,var(--rt-text-primary) 18%,transparent)}.invoice-item__top{display:flex;justify-content:space-between;gap:1rem;align-items:flex-start}.invoice-item__identity{display:flex;flex-direction:column;gap:.32rem}.invoice-item__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.28rem .62rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase}.invoice-item__number{margin:0;color:var(--rt-text-primary);font-size:1.08rem;font-weight:700}.invoice-item__customer{margin:0;color:var(--rt-text-secondary);line-height:1.5}.invoice-item__summary{display:flex;flex-direction:column;gap:.5rem;align-items:flex-end}.invoice-item__total{color:var(--rt-text-primary);font-size:1.08rem}.invoice-item__status{display:inline-flex;align-items:center;border-radius:9999px;padding:.42rem .78rem;font-size:.78rem;font-weight:700}.invoice-item__status--draft{background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color)}.invoice-item__status--issued{background:var(--rt-workspace-status-issued-surface);color:var(--rt-workspace-status-issued-color)}.invoice-item__status--paid{background:var(--rt-workspace-status-paid-surface);color:var(--rt-workspace-status-paid-color)}.invoice-item__status--void{background:color-mix(in srgb,var(--mat-sys-error, #b91c1c) 14%,transparent);color:var(--mat-sys-error, #b91c1c)}.invoice-item__status--neutral{background:color-mix(in srgb,var(--rt-10-percent-layer, rgba(15, 23, 42, .08)) 72%,transparent);color:var(--rt-text-secondary)}.invoice-item__meta{display:grid;gap:.85rem}.invoice-item__meta-card{display:flex;flex-direction:column;gap:.22rem;padding:.85rem .95rem;border-radius:1rem;background:color-mix(in srgb,var(--rt-10-percent-layer, rgba(15, 23, 42, .08)) 72%,transparent)}.invoice-item__meta-label{color:var(--rt-text-secondary);font-size:.82rem}.invoice-item__meta-card strong{color:var(--rt-text-primary);font-size:.96rem}.invoice-item__download{min-height:100%;border-radius:1rem}@media(min-width:900px){.invoice-item__meta{grid-template-columns:repeat(4,minmax(0,1fr));align-items:stretch}.invoice-item__download{justify-content:center}}\n"] }]
|
|
108
110
|
}], propDecorators: { invoice: [{ type: i0.Input, args: [{ isSignal: true, alias: "invoice", required: true }] }], download: [{ type: i0.Output, args: ["download"] }] } });
|
|
109
111
|
|
|
110
112
|
class InvoiceManageItem {
|
|
111
113
|
constructor() {
|
|
112
|
-
this.invoice = input.required(...(ngDevMode ? [{ debugName: "invoice" }] : []));
|
|
114
|
+
this.invoice = input.required(...(ngDevMode ? [{ debugName: "invoice" }] : /* istanbul ignore next */ []));
|
|
113
115
|
}
|
|
114
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
115
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.
|
|
116
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceManageItem, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
117
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.5", type: InvoiceManageItem, isStandalone: true, selector: "rolatech-invoice-manage-item", inputs: { invoice: { classPropertyName: "invoice", publicName: "invoice", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<p>invoice-manage-item works!</p>\n<div>{{invoice().id}}</div>\n", styles: [""] }); }
|
|
116
118
|
}
|
|
117
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
119
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceManageItem, decorators: [{
|
|
118
120
|
type: Component,
|
|
119
121
|
args: [{ selector: 'rolatech-invoice-manage-item', imports: [], template: "<p>invoice-manage-item works!</p>\n<div>{{invoice().id}}</div>\n" }]
|
|
120
122
|
}], propDecorators: { invoice: [{ type: i0.Input, args: [{ isSignal: true, alias: "invoice", required: true }] }] } });
|
|
121
123
|
|
|
122
124
|
class InvoiceItemSkeleton {
|
|
123
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
124
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.
|
|
125
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceItemSkeleton, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
126
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.5", type: InvoiceItemSkeleton, isStandalone: true, selector: "rolatech-invoice-item-skeleton", ngImport: i0, template: "<div class=\"grid grid-cols-6 place-content-center h-16 p-3 gap-3\">\n <rolatech-skeleton class=\"col-span-1 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"col-start-3 col-span-2 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"col-start-6 col-span-1 h-4\"></rolatech-skeleton>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: Skeleton, selector: "rolatech-skeleton" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
125
127
|
}
|
|
126
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
128
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceItemSkeleton, decorators: [{
|
|
127
129
|
type: Component,
|
|
128
130
|
args: [{ selector: 'rolatech-invoice-item-skeleton', imports: [Skeleton], encapsulation: ViewEncapsulation.None, template: "<div class=\"grid grid-cols-6 place-content-center h-16 p-3 gap-3\">\n <rolatech-skeleton class=\"col-span-1 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"col-start-3 col-span-2 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"col-start-6 col-span-1 h-4\"></rolatech-skeleton>\n</div>\n" }]
|
|
129
131
|
}] });
|
|
@@ -160,13 +162,13 @@ class InvoiceIndexComponent extends BaseComponent {
|
|
|
160
162
|
status: 'paid',
|
|
161
163
|
},
|
|
162
164
|
];
|
|
163
|
-
this.invoices = signal([], ...(ngDevMode ? [{ debugName: "invoices" }] : []));
|
|
165
|
+
this.invoices = signal([], ...(ngDevMode ? [{ debugName: "invoices" }] : /* istanbul ignore next */ []));
|
|
164
166
|
this.select = 0;
|
|
165
167
|
this.filter = false;
|
|
166
168
|
this.length = 100;
|
|
167
169
|
this.pageSize = 15;
|
|
168
170
|
this.pageSizeOptions = [5, 10, 25, 100];
|
|
169
|
-
this.pageIndex = signal(0, ...(ngDevMode ? [{ debugName: "pageIndex" }] : []));
|
|
171
|
+
this.pageIndex = signal(0, ...(ngDevMode ? [{ debugName: "pageIndex" }] : /* istanbul ignore next */ []));
|
|
170
172
|
this.pageMeta = {
|
|
171
173
|
baseLink: this.readRouteData('invoiceBaseLink', '/invoices'),
|
|
172
174
|
eyebrow: this.readRouteData('invoiceEyebrow', 'Billing workspace'),
|
|
@@ -181,7 +183,7 @@ class InvoiceIndexComponent extends BaseComponent {
|
|
|
181
183
|
{ label: 'Issued', value: invoices.filter((item) => item.status === InvoiceStatus.ISSUED || item.status === InvoiceStatus.SENT).length, hint: 'Sent and awaiting payment' },
|
|
182
184
|
{ label: 'Paid', value: invoices.filter((item) => item.status === InvoiceStatus.PAID).length, hint: 'Successfully settled' },
|
|
183
185
|
];
|
|
184
|
-
}, ...(ngDevMode ? [{ debugName: "stats" }] : []));
|
|
186
|
+
}, ...(ngDevMode ? [{ debugName: "stats" }] : /* istanbul ignore next */ []));
|
|
185
187
|
}
|
|
186
188
|
ngOnInit() {
|
|
187
189
|
const sub = this.route.queryParamMap
|
|
@@ -277,10 +279,10 @@ class InvoiceIndexComponent extends BaseComponent {
|
|
|
277
279
|
}
|
|
278
280
|
return fallback;
|
|
279
281
|
}
|
|
280
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
281
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
282
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceIndexComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
283
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: InvoiceIndexComponent, isStandalone: true, selector: "rolatech-invoice-index", usesInheritance: true, ngImport: i0, template: "<rolatech-page-collection-shell [eyebrow]=\"pageMeta.eyebrow\" [title]=\"pageMeta.title\" [subtitle]=\"pageMeta.description\">\n <div page-shell-header-meta class=\"invoice-index-page__stats\">\n @for (stat of stats(); track stat.label) {\n <article class=\"invoice-index-page__stat\">\n <span class=\"invoice-index-page__stat-value\">{{ stat.value }}</span>\n <span class=\"invoice-index-page__stat-label\">{{ stat.label }}</span>\n <span class=\"invoice-index-page__stat-hint\">{{ stat.hint }}</span>\n </article>\n }\n </div>\n\n <section class=\"invoice-index-page__filters\">\n <nav class=\"invoice-index-page__status-nav\" aria-label=\"Invoice status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"invoice-index-page__status-link\"\n [class.invoice-index-page__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <div class=\"invoice-index-page__filter-grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label>Type</mat-label>\n <mat-select name=\"type\" [(ngModel)]=\"filterOptions.type\">\n @for (type of invoiceType | keyvalue; track type.key) {\n <mat-option [value]=\"type.key\">{{ type.value }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Status</mat-label>\n <mat-select [compareWith]=\"statusCompareFn\" [(ngModel)]=\"filterOptions.status\">\n @for (status of invoiceStatus | keyvalue; track status.key) {\n <mat-option [value]=\"status.key\">{{ status.value }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <div class=\"invoice-index-page__filter-actions\">\n <button mat-flat-button (click)=\"find()\">Apply filters</button>\n <button mat-stroked-button (click)=\"resetFilter()\">Reset</button>\n </div>\n </div>\n </section>\n\n <section class=\"invoice-index-page__list\">\n @if (loading) {\n @for (dummy of [0, 1, 2, 3]; track dummy) {\n <rolatech-invoice-item-skeleton></rolatech-invoice-item-skeleton>\n }\n } @else if (invoices().length > 0) {\n @for (item of invoices(); track item.id) {\n <rolatech-invoice-item class=\"cursor-pointer\" [routerLink]=\"['./', item.id]\" [invoice]=\"item\"></rolatech-invoice-item>\n }\n } @else {\n <div class=\"invoice-index-page__empty\">\n <h3>No invoices yet</h3>\n <p>The invoices for this workspace will appear here once billing is created.</p>\n </div>\n }\n\n <mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n</rolatech-page-collection-shell>\n", styles: [".collapsed{max-height:0;overflow:hidden;transition:max-height .5s cubic-bezier(.4,0,.2,1)}.expanded{max-height:1000px}:host{display:block}.invoice-index-page__filters,.invoice-index-page__list{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.invoice-index-page__stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.invoice-index-page__stat{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.invoice-index-page__stat-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.invoice-index-page__stat-label,.invoice-index-page__stat-hint{color:var(--rt-text-secondary);font-size:.84rem}.invoice-index-page__filters,.invoice-index-page__list{padding:1rem 1.1rem}.invoice-index-page__filters,.invoice-index-page__list{display:flex;flex-direction:column;gap:1rem}.invoice-index-page__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.invoice-index-page__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none}.invoice-index-page__status-link:hover,.invoice-index-page__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.invoice-index-page__filter-grid{display:grid;gap:.85rem}.invoice-index-page__filter-grid mat-form-field{width:100%}.invoice-index-page__filter-actions{display:flex;flex-wrap:wrap;gap:.75rem}.invoice-index-page__empty{display:flex;flex-direction:column;justify-content:center;min-height:10rem;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;background:var(--rt-base-background, #ffffff)}.invoice-index-page__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.invoice-index-page__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:900px){.invoice-index-page__filter-grid{grid-template-columns:repeat(3,minmax(0,1fr));align-items:start}.invoice-index-page__stats{grid-template-columns:repeat(4,minmax(0,1fr))}}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: InvoiceItem, selector: "rolatech-invoice-item", inputs: ["invoice"], outputs: ["download"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i6.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: InvoiceItemSkeleton, selector: "rolatech-invoice-item-skeleton" }, { kind: "component", type: PageCollectionShellComponent, selector: "rolatech-page-collection-shell", inputs: ["eyebrow", "title", "subtitle"] }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
282
284
|
}
|
|
283
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
285
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceIndexComponent, decorators: [{
|
|
284
286
|
type: Component,
|
|
285
287
|
args: [{ selector: 'rolatech-invoice-index', imports: [
|
|
286
288
|
RouterModule,
|
|
@@ -299,15 +301,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
299
301
|
|
|
300
302
|
class InvoiceUser {
|
|
301
303
|
constructor() {
|
|
302
|
-
this.firstName = input.required(...(ngDevMode ? [{ debugName: "firstName" }] : []));
|
|
303
|
-
this.lastName = input.required(...(ngDevMode ? [{ debugName: "lastName" }] : []));
|
|
304
|
-
this.email = input.required(...(ngDevMode ? [{ debugName: "email" }] : []));
|
|
305
|
-
this.phone = input.required(...(ngDevMode ? [{ debugName: "phone" }] : []));
|
|
304
|
+
this.firstName = input.required(...(ngDevMode ? [{ debugName: "firstName" }] : /* istanbul ignore next */ []));
|
|
305
|
+
this.lastName = input.required(...(ngDevMode ? [{ debugName: "lastName" }] : /* istanbul ignore next */ []));
|
|
306
|
+
this.email = input.required(...(ngDevMode ? [{ debugName: "email" }] : /* istanbul ignore next */ []));
|
|
307
|
+
this.phone = input.required(...(ngDevMode ? [{ debugName: "phone" }] : /* istanbul ignore next */ []));
|
|
306
308
|
}
|
|
307
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
308
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.
|
|
309
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceUser, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
310
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.5", type: InvoiceUser, isStandalone: true, selector: "rolatech-invoice-user", inputs: { firstName: { classPropertyName: "firstName", publicName: "firstName", isSignal: true, isRequired: true, transformFunction: null }, lastName: { classPropertyName: "lastName", publicName: "lastName", isSignal: true, isRequired: true, transformFunction: null }, email: { classPropertyName: "email", publicName: "email", isSignal: true, isRequired: true, transformFunction: null }, phone: { classPropertyName: "phone", publicName: "phone", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "id": "rolatech-invoice-user" }, classAttribute: "rolatech-invoice-user" }, ngImport: i0, template: "<div class=\"rounded-xl border border-(--rt-border-color) p-4 grid space-y-3\">\n <h2 class=\"text-sm font-semibold\">User</h2>\n <div class=\"text-sm\">\n <div class=\"font-medium\">{{ firstName() }}, {{ lastName() }}</div>\n <div class=\"font-medium\">{{ phone() }}</div>\n <div>{{ email() }}</div>\n </div>\n</div>\n", styles: [""], encapsulation: i0.ViewEncapsulation.None }); }
|
|
309
311
|
}
|
|
310
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
312
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceUser, decorators: [{
|
|
311
313
|
type: Component,
|
|
312
314
|
args: [{ selector: 'rolatech-invoice-user', imports: [], encapsulation: ViewEncapsulation.None, host: {
|
|
313
315
|
id: 'rolatech-invoice-user',
|
|
@@ -317,28 +319,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
317
319
|
|
|
318
320
|
class InvoiceLines {
|
|
319
321
|
constructor() {
|
|
320
|
-
this.lines = input([], ...(ngDevMode ? [{ debugName: "lines" }] : []));
|
|
322
|
+
this.lines = input([], ...(ngDevMode ? [{ debugName: "lines" }] : /* istanbul ignore next */ []));
|
|
321
323
|
}
|
|
322
324
|
add() { }
|
|
323
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
324
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
325
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceLines, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
326
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: InvoiceLines, isStandalone: true, selector: "rolatech-invoice-lines", inputs: { lines: { classPropertyName: "lines", publicName: "lines", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"rounded-xl border border-(--rt-border-color) p-4 grid space-y-3\">\n <div class=\"flex justify-between\">\n <h2 class=\"text-sm font-semibold\">Invoice lines</h2>\n </div>\n\n <div class=\"overflow-x-auto\">\n <table class=\"min-w-full text-sm border border-(--rt-border-color) rounded-lg\">\n <thead class=\"bg-(--rt-raised-background)\">\n <tr>\n <th class=\"px-3 py-2 text-left font-medium border-b border-(--rt-border-color)\">Title</th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-(--rt-border-color) w-28\">Unit (\u00A3)</th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-(--rt-border-color) w-20\">VAT %</th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-(--rt-border-color) w-28\">VAT (\u00A3)</th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-(--rt-border-color) w-32\">Total (\u00A3)</th>\n </tr>\n </thead>\n\n <tbody class=\"[&>tr:nth-child(even)]:bg-(--rt-raised-background)\">\n @if (lines().length > 0) { @for (line of lines(); track line.id) {\n <tr class=\"hover:bg-(--rt-raised-background) cursor-pointer\">\n <!-- Title -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color)\">\n <div class=\"font-medium\">{{ line.title }}</div>\n </td>\n\n <!-- Unit price -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color) text-right text-(--rt-text-secondary)\">\n {{ line.unitPrice | price }}\n </td>\n\n <!-- VAT rate -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color) text-right text-(--rt-text-secondary)\">\n {{ line.vatRate }}%\n </td>\n\n <!-- VAT amount -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color) text-right text-(--rt-text-secondary)\">\n {{ line.vatAmount | price }}\n </td>\n\n <!-- Total -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color) text-right font-semibold text-(--rt-text-secondary)\">\n {{ line.lineTotal | price }}\n </td>\n </tr>\n } } @else {\n <tr>\n <td [attr.colspan]=\"6\" class=\"px-3 py-4 text-center text-(--rt-text-secondary) text-sm\">No invoice lines</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n</div>\n", styles: [".cdk-drag-preview{box-shadow:0 10px 20px #00000026;border-radius:12px}.cdk-drag-placeholder{opacity:.3}\n"], dependencies: [{ kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
325
327
|
}
|
|
326
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
328
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceLines, decorators: [{
|
|
327
329
|
type: Component,
|
|
328
330
|
args: [{ selector: 'rolatech-invoice-lines', imports: [PricePipe], encapsulation: ViewEncapsulation.None, template: "<div class=\"rounded-xl border border-(--rt-border-color) p-4 grid space-y-3\">\n <div class=\"flex justify-between\">\n <h2 class=\"text-sm font-semibold\">Invoice lines</h2>\n </div>\n\n <div class=\"overflow-x-auto\">\n <table class=\"min-w-full text-sm border border-(--rt-border-color) rounded-lg\">\n <thead class=\"bg-(--rt-raised-background)\">\n <tr>\n <th class=\"px-3 py-2 text-left font-medium border-b border-(--rt-border-color)\">Title</th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-(--rt-border-color) w-28\">Unit (\u00A3)</th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-(--rt-border-color) w-20\">VAT %</th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-(--rt-border-color) w-28\">VAT (\u00A3)</th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-(--rt-border-color) w-32\">Total (\u00A3)</th>\n </tr>\n </thead>\n\n <tbody class=\"[&>tr:nth-child(even)]:bg-(--rt-raised-background)\">\n @if (lines().length > 0) { @for (line of lines(); track line.id) {\n <tr class=\"hover:bg-(--rt-raised-background) cursor-pointer\">\n <!-- Title -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color)\">\n <div class=\"font-medium\">{{ line.title }}</div>\n </td>\n\n <!-- Unit price -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color) text-right text-(--rt-text-secondary)\">\n {{ line.unitPrice | price }}\n </td>\n\n <!-- VAT rate -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color) text-right text-(--rt-text-secondary)\">\n {{ line.vatRate }}%\n </td>\n\n <!-- VAT amount -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color) text-right text-(--rt-text-secondary)\">\n {{ line.vatAmount | price }}\n </td>\n\n <!-- Total -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color) text-right font-semibold text-(--rt-text-secondary)\">\n {{ line.lineTotal | price }}\n </td>\n </tr>\n } } @else {\n <tr>\n <td [attr.colspan]=\"6\" class=\"px-3 py-4 text-center text-(--rt-text-secondary) text-sm\">No invoice lines</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n</div>\n", styles: [".cdk-drag-preview{box-shadow:0 10px 20px #00000026;border-radius:12px}.cdk-drag-placeholder{opacity:.3}\n"] }]
|
|
329
331
|
}], propDecorators: { lines: [{ type: i0.Input, args: [{ isSignal: true, alias: "lines", required: false }] }] } });
|
|
330
332
|
|
|
331
333
|
class InvoiceSummary {
|
|
332
334
|
constructor() {
|
|
333
|
-
this.tax = input.required(...(ngDevMode ? [{ debugName: "tax" }] : []));
|
|
334
|
-
this.credit = input(0, ...(ngDevMode ? [{ debugName: "credit" }] : []));
|
|
335
|
-
this.subtotal = input.required(...(ngDevMode ? [{ debugName: "subtotal" }] : []));
|
|
336
|
-
this.total = input.required(...(ngDevMode ? [{ debugName: "total" }] : []));
|
|
335
|
+
this.tax = input.required(...(ngDevMode ? [{ debugName: "tax" }] : /* istanbul ignore next */ []));
|
|
336
|
+
this.credit = input(0, ...(ngDevMode ? [{ debugName: "credit" }] : /* istanbul ignore next */ []));
|
|
337
|
+
this.subtotal = input.required(...(ngDevMode ? [{ debugName: "subtotal" }] : /* istanbul ignore next */ []));
|
|
338
|
+
this.total = input.required(...(ngDevMode ? [{ debugName: "total" }] : /* istanbul ignore next */ []));
|
|
337
339
|
}
|
|
338
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
339
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
340
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceSummary, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
341
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: InvoiceSummary, isStandalone: true, selector: "rolatech-invoice-summary", inputs: { tax: { classPropertyName: "tax", publicName: "tax", isSignal: true, isRequired: true, transformFunction: null }, credit: { classPropertyName: "credit", publicName: "credit", isSignal: true, isRequired: false, transformFunction: null }, subtotal: { classPropertyName: "subtotal", publicName: "subtotal", isSignal: true, isRequired: true, transformFunction: null }, total: { classPropertyName: "total", publicName: "total", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "id": "rolatech-invoice-summary" }, classAttribute: "rolatech-invoice-summary" }, ngImport: i0, template: "<div class=\"rounded-xl border border-(--rt-border-color) p-4 grid space-y-3\">\n <h2 class=\"text-sm font-semibold text-(--rt-text-secondary)\">Totals</h2>\n\n <dl class=\"space-y-2 text-sm\">\n <div class=\"flex justify-between\">\n <dt class=\"text-(--rt-text-secondary)\">Subtotal</dt>\n <dd class=\"font-medium\">{{ subtotal() | price }}</dd>\n </div>\n\n <div class=\"flex justify-between\">\n <dt class=\"text-(--rt-text-secondary)\">VAT</dt>\n <dd class=\"font-medium\">{{ tax() | price}}</dd>\n </div>\n\n @if (credit() > 0) {\n <div class=\"flex justify-between\">\n <dt class=\"text-(--rt-text-secondary)\">Less holding deposit</dt>\n <dd class=\"font-medium text-red-600\">\u2212{{ credit() | price }}</dd>\n </div>\n }\n\n <div class=\"border-t border-dashed border-(--rt-border-color) my-2\"></div>\n\n <div class=\"flex justify-between items-center\">\n <dt class=\"text-(--rt-text-secondary) font-semibold\">Total</dt>\n <dd class=\"text-lg font-semibold\">{{ total() | price }}</dd>\n </div>\n </dl>\n</div>\n", styles: [""], dependencies: [{ kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
340
342
|
}
|
|
341
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
343
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceSummary, decorators: [{
|
|
342
344
|
type: Component,
|
|
343
345
|
args: [{ selector: 'rolatech-invoice-summary', imports: [PricePipe], encapsulation: ViewEncapsulation.None, host: {
|
|
344
346
|
id: 'rolatech-invoice-summary',
|
|
@@ -347,28 +349,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
347
349
|
}], propDecorators: { tax: [{ type: i0.Input, args: [{ isSignal: true, alias: "tax", required: true }] }], credit: [{ type: i0.Input, args: [{ isSignal: true, alias: "credit", required: false }] }], subtotal: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtotal", required: true }] }], total: [{ type: i0.Input, args: [{ isSignal: true, alias: "total", required: true }] }] } });
|
|
348
350
|
|
|
349
351
|
class InvoiceUserSkeleton {
|
|
350
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
351
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.
|
|
352
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceUserSkeleton, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
353
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.5", type: InvoiceUserSkeleton, isStandalone: true, selector: "rolatech-invoice-user-skeleton", ngImport: i0, template: "<div class=\"rounded-xl border border-(--rt-border-color) p-4 grid space-y-3\">\n <rolatech-skeleton class=\"w-12 h-6\"></rolatech-skeleton>\n <div class=\"flex flex-col gap-2\">\n <rolatech-skeleton class=\"w-28 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-32 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-36 h-4\"></rolatech-skeleton>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: Skeleton, selector: "rolatech-skeleton" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
352
354
|
}
|
|
353
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
355
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceUserSkeleton, decorators: [{
|
|
354
356
|
type: Component,
|
|
355
357
|
args: [{ selector: 'rolatech-invoice-user-skeleton', imports: [Skeleton], encapsulation: ViewEncapsulation.None, template: "<div class=\"rounded-xl border border-(--rt-border-color) p-4 grid space-y-3\">\n <rolatech-skeleton class=\"w-12 h-6\"></rolatech-skeleton>\n <div class=\"flex flex-col gap-2\">\n <rolatech-skeleton class=\"w-28 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-32 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-36 h-4\"></rolatech-skeleton>\n </div>\n</div>\n" }]
|
|
356
358
|
}] });
|
|
357
359
|
|
|
358
360
|
class InvoiceSummarySkeleton {
|
|
359
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
360
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.
|
|
361
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceSummarySkeleton, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
362
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.5", type: InvoiceSummarySkeleton, isStandalone: true, selector: "rolatech-invoice-summary-skeleton", ngImport: i0, template: "<div class=\"rounded-xl border border-(--rt-border-color) p-4 grid space-y-3\">\n <rolatech-skeleton class=\"w-12 h-6\"></rolatech-skeleton>\n\n <dl class=\"space-y-2 text-sm\">\n <div class=\"flex justify-between\">\n <rolatech-skeleton class=\"w-28 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-11 h-4\"></rolatech-skeleton>\n </div>\n <div class=\"flex justify-between\">\n <rolatech-skeleton class=\"w-28 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-11 h-4\"></rolatech-skeleton>\n </div>\n <div class=\"flex justify-between\">\n <rolatech-skeleton class=\"w-32 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-11 h-4\"></rolatech-skeleton>\n </div>\n\n <div class=\"border-t border-dashed border-(--rt-border-color) my-2\"></div>\n\n <div class=\"flex justify-between\">\n <rolatech-skeleton class=\"w-11 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-28 h-4\"></rolatech-skeleton>\n </div>\n </dl>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: Skeleton, selector: "rolatech-skeleton" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
361
363
|
}
|
|
362
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
364
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceSummarySkeleton, decorators: [{
|
|
363
365
|
type: Component,
|
|
364
366
|
args: [{ selector: 'rolatech-invoice-summary-skeleton', imports: [Skeleton], encapsulation: ViewEncapsulation.None, template: "<div class=\"rounded-xl border border-(--rt-border-color) p-4 grid space-y-3\">\n <rolatech-skeleton class=\"w-12 h-6\"></rolatech-skeleton>\n\n <dl class=\"space-y-2 text-sm\">\n <div class=\"flex justify-between\">\n <rolatech-skeleton class=\"w-28 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-11 h-4\"></rolatech-skeleton>\n </div>\n <div class=\"flex justify-between\">\n <rolatech-skeleton class=\"w-28 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-11 h-4\"></rolatech-skeleton>\n </div>\n <div class=\"flex justify-between\">\n <rolatech-skeleton class=\"w-32 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-11 h-4\"></rolatech-skeleton>\n </div>\n\n <div class=\"border-t border-dashed border-(--rt-border-color) my-2\"></div>\n\n <div class=\"flex justify-between\">\n <rolatech-skeleton class=\"w-11 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-28 h-4\"></rolatech-skeleton>\n </div>\n </dl>\n</div>\n" }]
|
|
365
367
|
}] });
|
|
366
368
|
|
|
367
369
|
class InvoiceLinesSkeleton {
|
|
368
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
369
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
370
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceLinesSkeleton, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
371
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: InvoiceLinesSkeleton, isStandalone: true, selector: "rolatech-invoice-lines-skeleton", ngImport: i0, template: "<div class=\"rounded-xl border border-(--rt-border-color) p-4 grid space-y-3\">\n <rolatech-skeleton class=\"w-20 h-6\"></rolatech-skeleton>\n\n <div class=\"overflow-x-auto\">\n <table class=\"min-w-full text-sm border border-(--rt-border-color) rounded-lg\">\n <thead class=\"bg-(--rt-raised-background)\">\n <tr>\n <th class=\"px-3 py-2 text-left font-medium border-b border-(--rt-border-color)\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n <th class=\"px-3 py-2 text-center font-medium border-b border-(--rt-border-color) w-20\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-(--rt-border-color) w-28\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-(--rt-border-color) w-20\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-(--rt-border-color) w-28\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-(--rt-border-color) w-32\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n </tr>\n </thead>\n\n <tbody class=\"[&>tr:nth-child(even)]:bg-(--rt-raised-background)\">\n @for (dummy of [0, 1, 2, 3]; track dummy) {\n <tr>\n <!-- Title -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color)\">\n <rolatech-skeleton class=\"h-6\"></rolatech-skeleton>\n </td>\n\n <!-- Qty -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color) text-center text-(--rt-text-secondary)\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </td>\n\n <!-- Unit price -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color) text-right text-(--rt-text-secondary)\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </td>\n\n <!-- VAT rate -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color) text-right text-(--rt-text-secondary)\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </td>\n <!-- VAT amount -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color) text-right text-(--rt-text-secondary)\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </td>\n\n <!-- Total -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color) text-right font-semibold text-(--rt-text-secondary)\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: Skeleton, selector: "rolatech-skeleton" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
370
372
|
}
|
|
371
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
373
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceLinesSkeleton, decorators: [{
|
|
372
374
|
type: Component,
|
|
373
375
|
args: [{ selector: 'rolatech-invoice-lines-skeleton', imports: [Skeleton], encapsulation: ViewEncapsulation.None, template: "<div class=\"rounded-xl border border-(--rt-border-color) p-4 grid space-y-3\">\n <rolatech-skeleton class=\"w-20 h-6\"></rolatech-skeleton>\n\n <div class=\"overflow-x-auto\">\n <table class=\"min-w-full text-sm border border-(--rt-border-color) rounded-lg\">\n <thead class=\"bg-(--rt-raised-background)\">\n <tr>\n <th class=\"px-3 py-2 text-left font-medium border-b border-(--rt-border-color)\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n <th class=\"px-3 py-2 text-center font-medium border-b border-(--rt-border-color) w-20\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-(--rt-border-color) w-28\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-(--rt-border-color) w-20\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-(--rt-border-color) w-28\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-(--rt-border-color) w-32\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n </tr>\n </thead>\n\n <tbody class=\"[&>tr:nth-child(even)]:bg-(--rt-raised-background)\">\n @for (dummy of [0, 1, 2, 3]; track dummy) {\n <tr>\n <!-- Title -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color)\">\n <rolatech-skeleton class=\"h-6\"></rolatech-skeleton>\n </td>\n\n <!-- Qty -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color) text-center text-(--rt-text-secondary)\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </td>\n\n <!-- Unit price -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color) text-right text-(--rt-text-secondary)\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </td>\n\n <!-- VAT rate -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color) text-right text-(--rt-text-secondary)\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </td>\n <!-- VAT amount -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color) text-right text-(--rt-text-secondary)\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </td>\n\n <!-- Total -->\n <td class=\"px-3 py-2 border-t border-(--rt-border-color) text-right font-semibold text-(--rt-text-secondary)\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n</div>\n" }]
|
|
374
376
|
}] });
|
|
@@ -378,10 +380,10 @@ class InvoiceDetailComponent extends BaseComponent {
|
|
|
378
380
|
super(...arguments);
|
|
379
381
|
this.invoiceService = inject(InvoiceService);
|
|
380
382
|
this.paymentService = inject(PaymentService);
|
|
381
|
-
this.invoice = signal(null, ...(ngDevMode ? [{ debugName: "invoice" }] : []));
|
|
383
|
+
this.invoice = signal(null, ...(ngDevMode ? [{ debugName: "invoice" }] : /* istanbul ignore next */ []));
|
|
382
384
|
this.status = InvoiceStatus;
|
|
383
385
|
this.paying = false;
|
|
384
|
-
this.loading = signal(true, ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
386
|
+
this.loading = signal(true, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
385
387
|
this.pageMeta = {
|
|
386
388
|
baseLink: this.readRouteData('invoiceBaseLink', '/invoices'),
|
|
387
389
|
collectionTitle: this.readRouteData('invoiceCollectionTitle', 'Invoices'),
|
|
@@ -412,7 +414,7 @@ class InvoiceDetailComponent extends BaseComponent {
|
|
|
412
414
|
}
|
|
413
415
|
get(id) {
|
|
414
416
|
this.loading.set(true);
|
|
415
|
-
this.invoiceService.
|
|
417
|
+
this.invoiceService.getInvoice(id).subscribe({
|
|
416
418
|
next: (res) => {
|
|
417
419
|
this.invoice.set(res.data);
|
|
418
420
|
this.loading.set(false);
|
|
@@ -477,10 +479,10 @@ class InvoiceDetailComponent extends BaseComponent {
|
|
|
477
479
|
}
|
|
478
480
|
return fallback;
|
|
479
481
|
}
|
|
480
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
481
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
482
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceDetailComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
483
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: InvoiceDetailComponent, isStandalone: true, selector: "rolatech-invoice-detail", usesInheritance: true, ngImport: i0, template: "<section class=\"invoice-detail-page\">\n @if (loading()) {\n <section class=\"invoice-detail-page__hero invoice-detail-page__hero--loading\">\n <div class=\"invoice-detail-page__hero-copy\">\n <span class=\"invoice-detail-page__eyebrow\">{{ pageMeta.eyebrow }}</span>\n <h1 class=\"invoice-detail-page__title\">Loading invoice...</h1>\n </div>\n </section>\n\n <section class=\"invoice-detail-page__content\">\n <div class=\"invoice-detail-page__main\">\n <rolatech-invoice-user-skeleton></rolatech-invoice-user-skeleton>\n <rolatech-invoice-lines-skeleton></rolatech-invoice-lines-skeleton>\n </div>\n <div class=\"invoice-detail-page__side\">\n <rolatech-invoice-summary-skeleton></rolatech-invoice-summary-skeleton>\n </div>\n </section>\n } @else if (invoice(); as invoice) {\n <section class=\"invoice-detail-page__hero\">\n <div class=\"invoice-detail-page__hero-copy\">\n <a class=\"invoice-detail-page__back\" [routerLink]=\"pageMeta.baseLink\">\u2190 {{ pageMeta.collectionTitle }}</a>\n <span class=\"invoice-detail-page__eyebrow\">{{ pageMeta.eyebrow }}</span>\n <h1 class=\"invoice-detail-page__title\">{{ invoice.invoiceNumber || invoice.id }}</h1>\n <p class=\"invoice-detail-page__description\">\n Invoice for {{ invoice.fullName || invoice.firstName + ' ' + invoice.lastName }} created\n {{ invoice.createdAt | date: 'mediumDate' }}.\n </p>\n </div>\n\n <div class=\"invoice-detail-page__hero-side\">\n <span [class]=\"statusToneClass(invoice.status)\">{{ status[invoice.status] }}</span>\n <div class=\"invoice-detail-page__total\">{{ invoice.total | price }}</div>\n <div class=\"invoice-detail-page__note\">\n @if (invoice.dueAt) {\n Due {{ invoice.dueAt | date: 'mediumDate' }}\n } @else {\n Payable on receipt\n }\n </div>\n\n @if (invoice.status.toString() === 'ISSUED' || invoice.status.toString() === 'SENT') {\n <button mat-flat-button [disabled]=\"paying\" class=\"invoice-detail-page__cta\" (click)=\"pay()\">\n {{ paying ? 'Processing payment...' : 'Pay invoice' }}\n </button>\n }\n </div>\n </section>\n\n <section class=\"invoice-detail-page__content\">\n <div class=\"invoice-detail-page__main\">\n <rolatech-invoice-user\n [firstName]=\"invoice.firstName\"\n [lastName]=\"invoice.lastName\"\n [email]=\"invoice.email\"\n [phone]=\"invoice.phone\"\n ></rolatech-invoice-user>\n\n @if (invoice.lines; as lines) {\n <rolatech-invoice-lines [lines]=\"lines\"></rolatech-invoice-lines>\n }\n </div>\n\n <div class=\"invoice-detail-page__side\">\n <rolatech-invoice-summary\n [tax]=\"invoice.vatTotal\"\n [credit]=\"invoice.holdingDepositApplied\"\n [subtotal]=\"invoice.subtotal\"\n [total]=\"invoice.total\"\n ></rolatech-invoice-summary>\n\n <article class=\"invoice-detail-page__panel\">\n <h2 class=\"invoice-detail-page__panel-title\">Invoice note</h2>\n <p class=\"invoice-detail-page__panel-copy\">{{ invoice.note || 'No note added for this invoice.' }}</p>\n\n @if (invoice.status.toString() === 'CREATED' || invoice.status.toString() === 'PAID') {\n <div class=\"invoice-detail-page__panel-row\">\n <span>Payment method</span>\n <strong>Stripe</strong>\n </div>\n }\n </article>\n </div>\n </section>\n }\n</section>\n", styles: [":host{display:block;padding:1rem;--rt-workspace-status-issued-surface: color-mix(in srgb, var(--rt-brand-color) 14%, transparent);--rt-workspace-status-issued-color: color-mix(in srgb, var(--rt-brand-color) 82%, var(--rt-text-primary) 18%);--rt-workspace-status-paid-surface: color-mix(in srgb, var(--rt-brand-color) 16%, var(--rt-base-background, #ffffff));--rt-workspace-status-paid-color: color-mix(in srgb, var(--rt-brand-color) 58%, var(--rt-text-primary) 42%)}.invoice-detail-page{display:flex;flex-direction:column;gap:1rem}.invoice-detail-page__hero,.invoice-detail-page__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.invoice-detail-page__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.invoice-detail-page__hero-copy,.invoice-detail-page__hero-side,.invoice-detail-page__main,.invoice-detail-page__side{display:flex;flex-direction:column;gap:1rem}.invoice-detail-page__back{color:var(--rt-text-secondary);font-weight:600;text-decoration:none}.invoice-detail-page__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.invoice-detail-page__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.invoice-detail-page__description,.invoice-detail-page__note,.invoice-detail-page__panel-copy{margin:0;color:var(--rt-text-secondary);line-height:1.7}.invoice-detail-page__status{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.45rem .82rem;font-size:.8rem;font-weight:700}.invoice-detail-page__status--neutral{background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color)}.invoice-detail-page__status--issued{background:var(--rt-workspace-status-issued-surface);color:var(--rt-workspace-status-issued-color)}.invoice-detail-page__status--paid{background:var(--rt-workspace-status-paid-surface);color:var(--rt-workspace-status-paid-color)}.invoice-detail-page__status--void{background:color-mix(in srgb,var(--mat-sys-error, #b91c1c) 14%,transparent);color:var(--mat-sys-error, #b91c1c)}.invoice-detail-page__total{color:var(--rt-text-primary);font-size:clamp(1.8rem,2.4vw,2.3rem);font-weight:800}.invoice-detail-page__cta{min-height:3rem;border-radius:9999px}.invoice-detail-page__content{display:grid;gap:1rem}.invoice-detail-page__panel{padding:1rem 1.1rem}.invoice-detail-page__panel-title{margin:0 0 .6rem;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.invoice-detail-page__panel-row{display:flex;justify-content:space-between;gap:1rem;margin-top:1rem;color:var(--rt-text-secondary)}.invoice-detail-page__panel-row strong{color:var(--rt-text-primary)}@media(min-width:1024px){.invoice-detail-page__hero{grid-template-columns:minmax(0,1.65fr) minmax(18rem,.85fr);align-items:start}.invoice-detail-page__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}}@media(min-width:768px){:host{padding:1.25rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: InvoiceUser, selector: "rolatech-invoice-user", inputs: ["firstName", "lastName", "email", "phone"] }, { kind: "component", type: InvoiceLines, selector: "rolatech-invoice-lines", inputs: ["lines"] }, { kind: "component", type: InvoiceSummary, selector: "rolatech-invoice-summary", inputs: ["tax", "credit", "subtotal", "total"] }, { kind: "component", type: InvoiceUserSkeleton, selector: "rolatech-invoice-user-skeleton" }, { kind: "component", type: InvoiceSummarySkeleton, selector: "rolatech-invoice-summary-skeleton" }, { kind: "component", type: InvoiceLinesSkeleton, selector: "rolatech-invoice-lines-skeleton" }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }] }); }
|
|
482
484
|
}
|
|
483
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
485
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceDetailComponent, decorators: [{
|
|
484
486
|
type: Component,
|
|
485
487
|
args: [{ selector: 'rolatech-invoice-detail', imports: [
|
|
486
488
|
CommonModule,
|
|
@@ -513,13 +515,13 @@ class InvoiceManageIndex extends BaseComponent {
|
|
|
513
515
|
super(...arguments);
|
|
514
516
|
this.invoiceService = inject(InvoiceService);
|
|
515
517
|
this.enumApi = inject(EnumApiClient);
|
|
516
|
-
this.invoiceTypeMap = signal({}, ...(ngDevMode ? [{ debugName: "invoiceTypeMap" }] : []));
|
|
518
|
+
this.invoiceTypeMap = signal({}, ...(ngDevMode ? [{ debugName: "invoiceTypeMap" }] : /* istanbul ignore next */ []));
|
|
517
519
|
this.select = 0;
|
|
518
520
|
this.loading = false;
|
|
519
|
-
this.invoices = signal([], ...(ngDevMode ? [{ debugName: "invoices" }] : []));
|
|
521
|
+
this.invoices = signal([], ...(ngDevMode ? [{ debugName: "invoices" }] : /* istanbul ignore next */ []));
|
|
520
522
|
this.length = 100;
|
|
521
523
|
this.pageSize = 15;
|
|
522
|
-
this.pageIndex = signal(0, ...(ngDevMode ? [{ debugName: "pageIndex" }] : []));
|
|
524
|
+
this.pageIndex = signal(0, ...(ngDevMode ? [{ debugName: "pageIndex" }] : /* istanbul ignore next */ []));
|
|
523
525
|
this.pageSizeOptions = [5, 10, 25, 100];
|
|
524
526
|
this.links = [
|
|
525
527
|
{
|
|
@@ -570,10 +572,9 @@ class InvoiceManageIndex extends BaseComponent {
|
|
|
570
572
|
}))
|
|
571
573
|
.subscribe({
|
|
572
574
|
next: (res) => {
|
|
573
|
-
|
|
574
|
-
this.invoices.set(res.data);
|
|
575
|
+
this.invoices.set(res.data ?? []);
|
|
575
576
|
this.meta = res.meta;
|
|
576
|
-
this.length = res.meta
|
|
577
|
+
this.length = res.meta?.pagination?.count ?? res.data?.length ?? 0;
|
|
577
578
|
},
|
|
578
579
|
error: () => {
|
|
579
580
|
this.invoices.set([]);
|
|
@@ -616,10 +617,10 @@ class InvoiceManageIndex extends BaseComponent {
|
|
|
616
617
|
return 'bg-gray-100 text-gray-700';
|
|
617
618
|
}
|
|
618
619
|
}
|
|
619
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
620
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
620
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceManageIndex, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
621
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: InvoiceManageIndex, isStandalone: true, selector: "rolatech-invoice-manage-index", usesInheritance: true, ngImport: i0, template: "<rolatech-toolbar title=\"Invoices\">\n <div class=\"flex items-center gap-2\"></div>\n</rolatech-toolbar>\n<rolatech-tabs [select]=\"select\">\n @for (item of links; track item) { @if (item.status) {\n <rolatech-tab class=\"cursor-pointer\" [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab class=\"cursor-pointer\" [label]=\"item.name\" routerLink=\"./\"></rolatech-tab>\n } }\n</rolatech-tabs>\n<div class=\"p-3 overflow-x-auto\">\n <table class=\"min-w-full\">\n <thead class=\"bg-(--rt-raised-background)\">\n <tr>\n <th class=\"p-3 border-b border-(--rt-border-color) text-left\">#ID</th>\n <th class=\"p-3 border-b border-(--rt-border-color) text-left\">Status</th>\n <th class=\"p-3 border-b border-(--rt-border-color) text-left\">Email</th>\n <th class=\"p-3 border-b border-(--rt-border-color) text-left\">Profile</th>\n <th class=\"p-3 border-b border-(--rt-border-color) text-left\">Type</th>\n <th class=\"p-3 border-b border-(--rt-border-color) text-left\">Total</th>\n </tr>\n </thead>\n <tbody>\n @for (item of invoices(); track $index) {\n <!-- <rolatech-invoice-manage-item [invoice]=\"item\"></rolatech-invoice-manage-item> -->\n <tr class=\"hover:bg-(--rt-raised-background) hover:cursor-pointer cursor-pointer\" [routerLink]=\"['./', item.id]\">\n <td class=\"p-3 border-b border-(--rt-border-color)\">{{item.id}}</td>\n <td class=\"p-3 border-b border-(--rt-border-color)\">\n <span\n class=\"inline-flex items-center rounded-full px-3 py-1 text-xs font-medium\"\n [ngClass]=\"statusBadgeClass(item.status)\"\n >{{item.status}}</span\n >\n </td>\n <td class=\"p-3 border-b border-(--rt-border-color)\">{{item.email}}</td>\n <td class=\"p-3 border-b border-(--rt-border-color)\">{{item.fullName}}</td>\n <td class=\"p-3 border-b border-(--rt-border-color)\">{{invoiceTypeMap()[item.type] || item.type}}</td>\n <td class=\"p-3 border-b border-(--rt-border-color)\">{{item.total | price}}</td>\n </tr>\n }\n </tbody>\n </table>\n</div>\n<!-- <div>\n <table class=\"min-w-full text-sm\">\n <thead class=\"border-b border-(--rt-border-color) bg-(--rt-raised-background)\">\n <tr>\n <th class=\"py-2 text-left font-medium text-gray-600\">#ID</th>\n <th class=\"py-2 text-right font-medium text-gray-600\">Status</th>\n <th class=\"py-2 text-right font-medium text-gray-600\">Email</th>\n <th class=\"py-2 text-right font-medium text-gray-600\">Profile</th>\n <th class=\"py-2 text-right font-medium text-gray-600\">Total</th>\n </tr>\n </thead>\n\n <tbody>\n @for (item of invoices(); track $index) {\n <tr class=\"border-b border-(--rt-border-color) hover:bg-(--rt-raised-background) hover:cursor-pointer\">\n <td class=\"py-2\">{{item.id}}</td>\n <td class=\"py-2 text-right\">{{item.status}}</td>\n <td class=\"py-2 text-right\">{{item.email}}</td>\n <td class=\"py-2 text-right\">{{item.firstName}}, {{item.lastName}}</td>\n <td class=\"py-2 text-right font-semibold\">{{item.total | price}}</td>\n </tr>\n }\n </tbody>\n </table>\n</div> -->\n<mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n>\n</mat-paginator>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: TabsComponent, selector: "rolatech-tabs", inputs: ["select", "loading", "block", "mode"], outputs: ["selectChange"] }, { kind: "component", type: TabComponent, selector: "rolatech-tab", inputs: ["label"], outputs: ["selectRequested"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i6.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "pipe", type: PricePipe, name: "price" }] }); }
|
|
621
622
|
}
|
|
622
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
623
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceManageIndex, decorators: [{
|
|
623
624
|
type: Component,
|
|
624
625
|
args: [{ selector: 'rolatech-invoice-manage-index', imports: [CommonModule, RouterLink, ToolbarComponent, TabsComponent, TabComponent, MatPaginatorModule, PricePipe], template: "<rolatech-toolbar title=\"Invoices\">\n <div class=\"flex items-center gap-2\"></div>\n</rolatech-toolbar>\n<rolatech-tabs [select]=\"select\">\n @for (item of links; track item) { @if (item.status) {\n <rolatech-tab class=\"cursor-pointer\" [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab class=\"cursor-pointer\" [label]=\"item.name\" routerLink=\"./\"></rolatech-tab>\n } }\n</rolatech-tabs>\n<div class=\"p-3 overflow-x-auto\">\n <table class=\"min-w-full\">\n <thead class=\"bg-(--rt-raised-background)\">\n <tr>\n <th class=\"p-3 border-b border-(--rt-border-color) text-left\">#ID</th>\n <th class=\"p-3 border-b border-(--rt-border-color) text-left\">Status</th>\n <th class=\"p-3 border-b border-(--rt-border-color) text-left\">Email</th>\n <th class=\"p-3 border-b border-(--rt-border-color) text-left\">Profile</th>\n <th class=\"p-3 border-b border-(--rt-border-color) text-left\">Type</th>\n <th class=\"p-3 border-b border-(--rt-border-color) text-left\">Total</th>\n </tr>\n </thead>\n <tbody>\n @for (item of invoices(); track $index) {\n <!-- <rolatech-invoice-manage-item [invoice]=\"item\"></rolatech-invoice-manage-item> -->\n <tr class=\"hover:bg-(--rt-raised-background) hover:cursor-pointer cursor-pointer\" [routerLink]=\"['./', item.id]\">\n <td class=\"p-3 border-b border-(--rt-border-color)\">{{item.id}}</td>\n <td class=\"p-3 border-b border-(--rt-border-color)\">\n <span\n class=\"inline-flex items-center rounded-full px-3 py-1 text-xs font-medium\"\n [ngClass]=\"statusBadgeClass(item.status)\"\n >{{item.status}}</span\n >\n </td>\n <td class=\"p-3 border-b border-(--rt-border-color)\">{{item.email}}</td>\n <td class=\"p-3 border-b border-(--rt-border-color)\">{{item.fullName}}</td>\n <td class=\"p-3 border-b border-(--rt-border-color)\">{{invoiceTypeMap()[item.type] || item.type}}</td>\n <td class=\"p-3 border-b border-(--rt-border-color)\">{{item.total | price}}</td>\n </tr>\n }\n </tbody>\n </table>\n</div>\n<!-- <div>\n <table class=\"min-w-full text-sm\">\n <thead class=\"border-b border-(--rt-border-color) bg-(--rt-raised-background)\">\n <tr>\n <th class=\"py-2 text-left font-medium text-gray-600\">#ID</th>\n <th class=\"py-2 text-right font-medium text-gray-600\">Status</th>\n <th class=\"py-2 text-right font-medium text-gray-600\">Email</th>\n <th class=\"py-2 text-right font-medium text-gray-600\">Profile</th>\n <th class=\"py-2 text-right font-medium text-gray-600\">Total</th>\n </tr>\n </thead>\n\n <tbody>\n @for (item of invoices(); track $index) {\n <tr class=\"border-b border-(--rt-border-color) hover:bg-(--rt-raised-background) hover:cursor-pointer\">\n <td class=\"py-2\">{{item.id}}</td>\n <td class=\"py-2 text-right\">{{item.status}}</td>\n <td class=\"py-2 text-right\">{{item.email}}</td>\n <td class=\"py-2 text-right\">{{item.firstName}}, {{item.lastName}}</td>\n <td class=\"py-2 text-right font-semibold\">{{item.total | price}}</td>\n </tr>\n }\n </tbody>\n </table>\n</div> -->\n<mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n>\n</mat-paginator>\n" }]
|
|
625
626
|
}] });
|
|
@@ -714,10 +715,10 @@ class InvoiceManageCreate {
|
|
|
714
715
|
todayIso() {
|
|
715
716
|
return new Date().toISOString().slice(0, 10);
|
|
716
717
|
}
|
|
717
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
718
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: InvoiceManageCreate, isStandalone: true, selector: "rolatech-invoice-manage-create", ngImport: i0, template: "<rolatech-toolbar title=\"Create Invoice\">\n <button\n mat-flat-button\n color=\"primary\"\n (click)=\"save(invoiceForm)\"\n [disabled]=\"!invoiceForm.form.valid || invoice.lines.length === 0\"\n >\n Save\n </button>\n</rolatech-toolbar>\n<div class=\"p-4 space-y-6\">\n <form #invoiceForm=\"ngForm\" novalidate>\n <mat-card appearance=\"outlined\" class=\"p-4\">\n <div class=\"grid lg:grid-cols-2 gap-4\">\n <mat-form-field appearance=\"fill\">\n <mat-label>Customer name</mat-label>\n <input matInput name=\"customerName\" [(ngModel)]=\"invoice.customerName\" required />\n @if (invoiceForm.submitted && !invoice.customerName) {<mat-error>Name is required</mat-error>}\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Customer email</mat-label>\n <input matInput type=\"email\" name=\"customerEmail\" [(ngModel)]=\"invoice.customerEmail\" #email=\"ngModel\" email />\n @if (email.invalid && (email.dirty || email.touched)) {<mat-error>Invalid email</mat-error>}\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\" class=\"lg:col-span-2\">\n <mat-label>Customer address</mat-label>\n <textarea matInput rows=\"2\" name=\"customerAddress\" [(ngModel)]=\"invoice.customerAddress\"></textarea>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Currency</mat-label>\n <mat-select name=\"currency\" [(ngModel)]=\"invoice.currency\" required>\n <mat-option value=\"GBP\">GBP</mat-option>\n <mat-option value=\"USD\">USD</mat-option>\n <mat-option value=\"EUR\">EUR</mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Issue date</mat-label>\n <input matInput type=\"date\" name=\"issueDate\" [(ngModel)]=\"invoice.issueDate\" required />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Due date</mat-label>\n <input matInput type=\"date\" name=\"dueDate\" [(ngModel)]=\"invoice.dueDate\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\" class=\"lg:col-span-2\">\n <mat-label>Notes</mat-label>\n <textarea\n matInput\n rows=\"2\"\n name=\"notes\"\n [(ngModel)]=\"invoice.notes\"\n placeholder=\"Optional notes shown on the invoice\"\n ></textarea>\n </mat-form-field>\n </div>\n </mat-card>\n\n <mat-card appearance=\"outlined\" class=\"p-4 space-y-4 mt-4\">\n <div class=\"flex items-center justify-between\">\n <h2 class=\"text-xl font-medium\">Line items</h2>\n <button mat-stroked-button type=\"button\" (click)=\"addLine()\">\n <mat-icon>add</mat-icon>\n Add line\n </button>\n </div>\n\n <div class=\"hidden lg:grid grid-cols-12 gap-2 text-sm font-medium px-2\">\n <div class=\"col-span-3\">Type</div>\n <div class=\"col-span-3\">Description</div>\n <div class=\"col-span-1 text-right\">Qty</div>\n <div class=\"col-span-2 text-right\">Unit Price</div>\n <div class=\"col-span-2 text-right\">Tax %</div>\n <div class=\"col-span-1\"></div>\n </div>\n\n <div class=\"space-y-3\">\n @for ( line of invoice.lines; track line; let i = $index) {\n <div class=\"grid grid-cols-1 lg:grid-cols-12 gap-2 items-start\">\n <rolatech-enum-select\n class=\"lg:col-span-3\"\n [resource]=\"'billing'\"\n [enumName]=\"'InvoiceLineType'\"\n [label]=\"'Line type'\"\n [placeholder]=\"'Select\u2026'\"\n [(value)]=\"line.type\"\n ></rolatech-enum-select>\n <mat-form-field class=\"lg:col-span-3\" appearance=\"fill\">\n <mat-label>Description</mat-label>\n <input\n matInput\n [name]=\"'desc'+i\"\n [(ngModel)]=\"line.description\"\n required\n placeholder=\"e.g. Photo editing package\"\n />\n </mat-form-field>\n\n <mat-form-field class=\"lg:col-span-1\" appearance=\"fill\">\n <mat-label>Quantity</mat-label>\n <input matInput type=\"number\" min=\"1\" step=\"1\" [name]=\"'qty'+i\" [(ngModel)]=\"line.quantity\" required />\n </mat-form-field>\n\n <mat-form-field class=\"lg:col-span-2\" appearance=\"fill\">\n <mat-label>Unit Price</mat-label>\n <input matInput type=\"number\" min=\"0\" step=\"0.01\" [name]=\"'unit'+i\" [(ngModel)]=\"line.unitPrice\" required />\n <mat-hint>e.g. 99.99</mat-hint>\n </mat-form-field>\n\n <mat-form-field class=\"lg:col-span-2\" appearance=\"fill\">\n <mat-label>Tax %</mat-label>\n <input matInput type=\"number\" min=\"0\" step=\"0.01\" [name]=\"'tax'+i\" [(ngModel)]=\"line.taxRate\" />\n </mat-form-field>\n\n <div class=\"lg:col-span-1 flex items-center justify-end\">\n <button mat-icon-button color=\"warn\" type=\"button\" (click)=\"removeLine(i)\" aria-label=\"Remove line\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n </div>\n <mat-divider></mat-divider>\n }\n </div>\n\n <div class=\"flex flex-col items-end space-y-1\">\n <div class=\"text-sm\">\n Subtotal: <span class=\"font-medium\">{{ subtotalMajor() | number:'1.2-2' }} {{ invoice.currency }}</span>\n </div>\n <div class=\"text-sm\">\n Tax: <span class=\"font-medium\">{{ taxMajor() | number:'1.2-2' }} {{ invoice.currency }}</span>\n </div>\n <div class=\"text-lg font-semibold\">Total: {{ totalMajor() | number:'1.2-2' }} {{ invoice.currency }}</div>\n </div>\n </mat-card>\n </form>\n\n @if (lastResponse) {\n <mat-card appearance=\"outlined\" class=\"p-4 flex items-center justify-between\">\n <div>\n <div class=\"font-medium\">Created invoice #{{ lastResponse.data.id }}</div>\n <div class=\"text-sm opacity-70\">\n Total: {{ penceToMajor(lastResponse.data.total) | number:'1.2-2' }} {{ invoice.currency }}\n </div>\n </div>\n <div class=\"space-x-2\">\n <button mat-stroked-button (click)=\"resetForm()\">Create another</button>\n <button mat-flat-button color=\"primary\">Download PDF</button>\n </div>\n </mat-card>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i1$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i7.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i8.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: EnumSelect, selector: "rolatech-enum-select", inputs: ["resource", "enumName", "label", "placeholder", "disabled", "value"], outputs: ["valueChange"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }] }); }
|
|
718
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceManageCreate, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
719
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: InvoiceManageCreate, isStandalone: true, selector: "rolatech-invoice-manage-create", ngImport: i0, template: "<rolatech-toolbar title=\"Create Invoice\">\n <button\n mat-flat-button\n color=\"primary\"\n (click)=\"save(invoiceForm)\"\n [disabled]=\"!invoiceForm.form.valid || invoice.lines.length === 0\"\n >\n Save\n </button>\n</rolatech-toolbar>\n<div class=\"p-4 space-y-6\">\n <form #invoiceForm=\"ngForm\" novalidate>\n <mat-card appearance=\"outlined\" class=\"p-4\">\n <div class=\"grid lg:grid-cols-2 gap-4\">\n <mat-form-field appearance=\"fill\">\n <mat-label>Customer name</mat-label>\n <input matInput name=\"customerName\" [(ngModel)]=\"invoice.customerName\" required />\n @if (invoiceForm.submitted && !invoice.customerName) {<mat-error>Name is required</mat-error>}\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Customer email</mat-label>\n <input matInput type=\"email\" name=\"customerEmail\" [(ngModel)]=\"invoice.customerEmail\" #email=\"ngModel\" email />\n @if (email.invalid && (email.dirty || email.touched)) {<mat-error>Invalid email</mat-error>}\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\" class=\"lg:col-span-2\">\n <mat-label>Customer address</mat-label>\n <textarea matInput rows=\"2\" name=\"customerAddress\" [(ngModel)]=\"invoice.customerAddress\"></textarea>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Currency</mat-label>\n <mat-select name=\"currency\" [(ngModel)]=\"invoice.currency\" required>\n <mat-option value=\"GBP\">GBP</mat-option>\n <mat-option value=\"USD\">USD</mat-option>\n <mat-option value=\"EUR\">EUR</mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Issue date</mat-label>\n <input matInput type=\"date\" name=\"issueDate\" [(ngModel)]=\"invoice.issueDate\" required />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Due date</mat-label>\n <input matInput type=\"date\" name=\"dueDate\" [(ngModel)]=\"invoice.dueDate\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\" class=\"lg:col-span-2\">\n <mat-label>Notes</mat-label>\n <textarea\n matInput\n rows=\"2\"\n name=\"notes\"\n [(ngModel)]=\"invoice.notes\"\n placeholder=\"Optional notes shown on the invoice\"\n ></textarea>\n </mat-form-field>\n </div>\n </mat-card>\n\n <mat-card appearance=\"outlined\" class=\"p-4 space-y-4 mt-4\">\n <div class=\"flex items-center justify-between\">\n <h2 class=\"text-xl font-medium\">Line items</h2>\n <button mat-stroked-button type=\"button\" (click)=\"addLine()\">\n <mat-icon>add</mat-icon>\n Add line\n </button>\n </div>\n\n <div class=\"hidden lg:grid grid-cols-12 gap-2 text-sm font-medium px-2\">\n <div class=\"col-span-3\">Type</div>\n <div class=\"col-span-3\">Description</div>\n <div class=\"col-span-1 text-right\">Qty</div>\n <div class=\"col-span-2 text-right\">Unit Price</div>\n <div class=\"col-span-2 text-right\">Tax %</div>\n <div class=\"col-span-1\"></div>\n </div>\n\n <div class=\"space-y-3\">\n @for ( line of invoice.lines; track line; let i = $index) {\n <div class=\"grid grid-cols-1 lg:grid-cols-12 gap-2 items-start\">\n <rolatech-enum-select\n class=\"lg:col-span-3\"\n [resource]=\"'billing'\"\n [enumName]=\"'InvoiceLineType'\"\n [label]=\"'Line type'\"\n [placeholder]=\"'Select\u2026'\"\n [(value)]=\"line.type\"\n ></rolatech-enum-select>\n <mat-form-field class=\"lg:col-span-3\" appearance=\"fill\">\n <mat-label>Description</mat-label>\n <input\n matInput\n [name]=\"'desc'+i\"\n [(ngModel)]=\"line.description\"\n required\n placeholder=\"e.g. Photo editing package\"\n />\n </mat-form-field>\n\n <mat-form-field class=\"lg:col-span-1\" appearance=\"fill\">\n <mat-label>Quantity</mat-label>\n <input matInput type=\"number\" min=\"1\" step=\"1\" [name]=\"'qty'+i\" [(ngModel)]=\"line.quantity\" required />\n </mat-form-field>\n\n <mat-form-field class=\"lg:col-span-2\" appearance=\"fill\">\n <mat-label>Unit Price</mat-label>\n <input matInput type=\"number\" min=\"0\" step=\"0.01\" [name]=\"'unit'+i\" [(ngModel)]=\"line.unitPrice\" required />\n <mat-hint>e.g. 99.99</mat-hint>\n </mat-form-field>\n\n <mat-form-field class=\"lg:col-span-2\" appearance=\"fill\">\n <mat-label>Tax %</mat-label>\n <input matInput type=\"number\" min=\"0\" step=\"0.01\" [name]=\"'tax'+i\" [(ngModel)]=\"line.taxRate\" />\n </mat-form-field>\n\n <div class=\"lg:col-span-1 flex items-center justify-end\">\n <button mat-icon-button color=\"warn\" type=\"button\" (click)=\"removeLine(i)\" aria-label=\"Remove line\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n </div>\n <mat-divider></mat-divider>\n }\n </div>\n\n <div class=\"flex flex-col items-end space-y-1\">\n <div class=\"text-sm\">\n Subtotal: <span class=\"font-medium\">{{ subtotalMajor() | number:'1.2-2' }} {{ invoice.currency }}</span>\n </div>\n <div class=\"text-sm\">\n Tax: <span class=\"font-medium\">{{ taxMajor() | number:'1.2-2' }} {{ invoice.currency }}</span>\n </div>\n <div class=\"text-lg font-semibold\">Total: {{ totalMajor() | number:'1.2-2' }} {{ invoice.currency }}</div>\n </div>\n </mat-card>\n </form>\n\n @if (lastResponse) {\n <mat-card appearance=\"outlined\" class=\"p-4 flex items-center justify-between\">\n <div>\n <div class=\"font-medium\">Created invoice #{{ lastResponse.data.id }}</div>\n <div class=\"text-sm opacity-70\">\n Total: {{ penceToMajor(lastResponse.data.total) | number:'1.2-2' }} {{ invoice.currency }}\n </div>\n </div>\n <div class=\"space-x-2\">\n <button mat-stroked-button (click)=\"resetForm()\">Create another</button>\n <button mat-flat-button color=\"primary\">Download PDF</button>\n </div>\n </mat-card>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i1$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i7.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i8.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: EnumSelect, selector: "rolatech-enum-select", inputs: ["resource", "enumName", "label", "placeholder", "disabled", "value"], outputs: ["valueChange"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }] }); }
|
|
719
720
|
}
|
|
720
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
721
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceManageCreate, decorators: [{
|
|
721
722
|
type: Component,
|
|
722
723
|
args: [{ selector: 'rolatech-invoice-manage-create', imports: [
|
|
723
724
|
CommonModule,
|
|
@@ -749,23 +750,23 @@ class InvoiceEdit extends BaseComponent {
|
|
|
749
750
|
constructor() {
|
|
750
751
|
super(...arguments);
|
|
751
752
|
this.minDate = new Date();
|
|
752
|
-
this.invoice = model.required(...(ngDevMode ? [{ debugName: "invoice" }] : []));
|
|
753
|
+
this.invoice = model.required(...(ngDevMode ? [{ debugName: "invoice" }] : /* istanbul ignore next */ []));
|
|
753
754
|
this.output = output();
|
|
754
755
|
}
|
|
755
756
|
ngDoCheck() {
|
|
756
757
|
this.output.emit(this.invoice());
|
|
757
758
|
}
|
|
758
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
759
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.
|
|
759
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceEdit, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
760
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.5", type: InvoiceEdit, isStandalone: true, selector: "rolatech-invoice-edit", inputs: { invoice: { classPropertyName: "invoice", publicName: "invoice", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { invoice: "invoiceChange", output: "output" }, providers: [
|
|
760
761
|
{
|
|
761
762
|
provide: DateAdapter,
|
|
762
763
|
useClass: MomentDateAdapter,
|
|
763
764
|
deps: [MAT_DATE_LOCALE],
|
|
764
765
|
},
|
|
765
766
|
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS },
|
|
766
|
-
], usesInheritance: true, ngImport: i0, template: "<div class=\"flex flex-col gap-2\">\n <div class=\"flex justify-between gap-3\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Firstname</mat-label>\n <input matInput [(ngModel)]=\"invoice().firstName\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Lastname</mat-label>\n <input matInput [(ngModel)]=\"invoice().lastName\" />\n </mat-form-field>\n </div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Email</mat-label>\n <input matInput [(ngModel)]=\"invoice().email\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Phone</mat-label>\n <input matInput [(ngModel)]=\"invoice().phone\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Discount total</mat-label>\n <input matInput [(ngModel)]=\"invoice().discountAmount\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Total</mat-label>\n <input matInput [(ngModel)]=\"invoice().total\" />\n </mat-form-field>\n <!-- <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label i18n>Move-in date</mat-label>\n <input\n matInput\n placeholder=\"Move-in date\"\n [min]=\"minDate\"\n [matDatepicker]=\"startDatePicker\"\n (focus)=\"startDatePicker.open()\"\n [(ngModel)]=\"invoice().startDate\"\n (dateInput)=\"invoice().startDate = $event.value.format('YYYY-MM-DD')\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field> -->\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type:
|
|
767
|
+
], usesInheritance: true, ngImport: i0, template: "<div class=\"flex flex-col gap-2\">\n <div class=\"flex justify-between gap-3\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Firstname</mat-label>\n <input matInput [(ngModel)]=\"invoice().firstName\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Lastname</mat-label>\n <input matInput [(ngModel)]=\"invoice().lastName\" />\n </mat-form-field>\n </div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Email</mat-label>\n <input matInput [(ngModel)]=\"invoice().email\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Phone</mat-label>\n <input matInput [(ngModel)]=\"invoice().phone\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Discount total</mat-label>\n <input matInput [(ngModel)]=\"invoice().discountAmount\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Total</mat-label>\n <input matInput [(ngModel)]=\"invoice().total\" />\n </mat-form-field>\n <!-- <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label i18n>Move-in date</mat-label>\n <input\n matInput\n placeholder=\"Move-in date\"\n [min]=\"minDate\"\n [matDatepicker]=\"startDatePicker\"\n (focus)=\"startDatePicker.open()\"\n [(ngModel)]=\"invoice().startDate\"\n (dateInput)=\"invoice().startDate = $event.value.format('YYYY-MM-DD')\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field> -->\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatDatepickerModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatOptionModule }] }); }
|
|
767
768
|
}
|
|
768
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
769
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceEdit, decorators: [{
|
|
769
770
|
type: Component,
|
|
770
771
|
args: [{ selector: 'rolatech-invoice-edit', imports: [CommonModule, FormsModule, MatInputModule, MatDatepickerModule, MatSelectModule, MatOptionModule], providers: [
|
|
771
772
|
{
|
|
@@ -780,21 +781,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
780
781
|
class InvoiceStore {
|
|
781
782
|
constructor() {
|
|
782
783
|
this.api = inject(InvoiceService);
|
|
783
|
-
this._lines = signal([], ...(ngDevMode ? [{ debugName: "_lines" }] : []));
|
|
784
|
+
this._lines = signal([], ...(ngDevMode ? [{ debugName: "_lines" }] : /* istanbul ignore next */ []));
|
|
784
785
|
// --- state ---
|
|
785
|
-
this.invoiceId = signal(null, ...(ngDevMode ? [{ debugName: "invoiceId" }] : []));
|
|
786
|
-
this.invoice = signal(null, ...(ngDevMode ? [{ debugName: "invoice" }] : []));
|
|
786
|
+
this.invoiceId = signal(null, ...(ngDevMode ? [{ debugName: "invoiceId" }] : /* istanbul ignore next */ []));
|
|
787
|
+
this.invoice = signal(null, ...(ngDevMode ? [{ debugName: "invoice" }] : /* istanbul ignore next */ []));
|
|
787
788
|
/** last server-saved snapshot */
|
|
788
|
-
this.pristine = signal(null, ...(ngDevMode ? [{ debugName: "pristine" }] : []));
|
|
789
|
-
this.status = signal('idle', ...(ngDevMode ? [{ debugName: "status" }] : []));
|
|
790
|
-
this.error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
|
|
789
|
+
this.pristine = signal(null, ...(ngDevMode ? [{ debugName: "pristine" }] : /* istanbul ignore next */ []));
|
|
790
|
+
this.status = signal('idle', ...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
|
|
791
|
+
this.error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
|
|
791
792
|
// editor UI state
|
|
792
|
-
this.editorOpen = signal(false, ...(ngDevMode ? [{ debugName: "editorOpen" }] : []));
|
|
793
|
-
this.editingLineId = signal(null, ...(ngDevMode ? [{ debugName: "editingLineId" }] : []));
|
|
793
|
+
this.editorOpen = signal(false, ...(ngDevMode ? [{ debugName: "editorOpen" }] : /* istanbul ignore next */ []));
|
|
794
|
+
this.editingLineId = signal(null, ...(ngDevMode ? [{ debugName: "editingLineId" }] : /* istanbul ignore next */ []));
|
|
794
795
|
// --- derived ---
|
|
795
|
-
this.lines = computed(() => this.invoice()?.lines ?? [], ...(ngDevMode ? [{ debugName: "lines" }] : []));
|
|
796
|
-
this.currency = computed(() => this.invoice()?.currency ?? 'GBP', ...(ngDevMode ? [{ debugName: "currency" }] : []));
|
|
797
|
-
this.vatMode = computed(() => this.invoice()?.vatMode ?? InvoiceVatMode.EXCLUSIVE, ...(ngDevMode ? [{ debugName: "vatMode" }] : []));
|
|
796
|
+
this.lines = computed(() => this.invoice()?.lines ?? [], ...(ngDevMode ? [{ debugName: "lines" }] : /* istanbul ignore next */ []));
|
|
797
|
+
this.currency = computed(() => this.invoice()?.currency ?? 'GBP', ...(ngDevMode ? [{ debugName: "currency" }] : /* istanbul ignore next */ []));
|
|
798
|
+
this.vatMode = computed(() => this.invoice()?.vatMode ?? InvoiceVatMode.EXCLUSIVE, ...(ngDevMode ? [{ debugName: "vatMode" }] : /* istanbul ignore next */ []));
|
|
798
799
|
this.totals = computed(() => {
|
|
799
800
|
const inv = this.invoice();
|
|
800
801
|
if (!inv) {
|
|
@@ -805,7 +806,7 @@ class InvoiceStore {
|
|
|
805
806
|
const vatTotal = lines.reduce((s, l) => s + (l.vatAmount ?? 0), 0);
|
|
806
807
|
const total = lines.reduce((s, l) => s + (l.lineTotal ?? 0), 0);
|
|
807
808
|
return { subtotal, vatTotal, total };
|
|
808
|
-
}, ...(ngDevMode ? [{ debugName: "totals" }] : []));
|
|
809
|
+
}, ...(ngDevMode ? [{ debugName: "totals" }] : /* istanbul ignore next */ []));
|
|
809
810
|
/** 🔴 DIRTY FLAG */
|
|
810
811
|
this.dirty = computed(() => {
|
|
811
812
|
const a = this.invoice();
|
|
@@ -813,7 +814,7 @@ class InvoiceStore {
|
|
|
813
814
|
if (!a || !b)
|
|
814
815
|
return false;
|
|
815
816
|
return JSON.stringify(a) !== JSON.stringify(b);
|
|
816
|
-
}, ...(ngDevMode ? [{ debugName: "dirty" }] : []));
|
|
817
|
+
}, ...(ngDevMode ? [{ debugName: "dirty" }] : /* istanbul ignore next */ []));
|
|
817
818
|
}
|
|
818
819
|
// --- API: load / refresh ---
|
|
819
820
|
load(id) {
|
|
@@ -823,7 +824,7 @@ class InvoiceStore {
|
|
|
823
824
|
// ✅ Adapt to your actual API signature:
|
|
824
825
|
// e.g. this.api.get(id) or this.api.getInvoice(id)
|
|
825
826
|
this.api
|
|
826
|
-
.
|
|
827
|
+
.getInvoice(id)
|
|
827
828
|
.pipe(finalize$1(() => this.status.set('idle')))
|
|
828
829
|
.subscribe({
|
|
829
830
|
next: (inv) => {
|
|
@@ -1041,19 +1042,19 @@ class InvoiceStore {
|
|
|
1041
1042
|
errMsg(e) {
|
|
1042
1043
|
return e?.error?.message || e?.message || 'Request failed';
|
|
1043
1044
|
}
|
|
1044
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1045
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
1045
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1046
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceStore, providedIn: 'root' }); }
|
|
1046
1047
|
}
|
|
1047
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1048
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceStore, decorators: [{
|
|
1048
1049
|
type: Injectable,
|
|
1049
1050
|
args: [{ providedIn: 'root' }]
|
|
1050
1051
|
}] });
|
|
1051
1052
|
|
|
1052
1053
|
class InvoiceLinePropertySelector {
|
|
1053
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1054
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.
|
|
1054
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceLinePropertySelector, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1055
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.5", type: InvoiceLinePropertySelector, isStandalone: true, selector: "rolatech-invoice-line-property-selector", ngImport: i0, template: "<p>invoice-line-property-selector works!</p>\n", styles: [""] }); }
|
|
1055
1056
|
}
|
|
1056
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1057
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceLinePropertySelector, decorators: [{
|
|
1057
1058
|
type: Component,
|
|
1058
1059
|
args: [{ selector: 'rolatech-invoice-line-property-selector', imports: [], template: "<p>invoice-line-property-selector works!</p>\n" }]
|
|
1059
1060
|
}] });
|
|
@@ -1064,7 +1065,7 @@ class InvoiceManageLine {
|
|
|
1064
1065
|
this.dialog = inject(MatDialog);
|
|
1065
1066
|
this.lines = this.store.lines;
|
|
1066
1067
|
this.vatMode = this.store.vatMode;
|
|
1067
|
-
this.editMode = signal(false, ...(ngDevMode ? [{ debugName: "editMode" }] : []));
|
|
1068
|
+
this.editMode = signal(false, ...(ngDevMode ? [{ debugName: "editMode" }] : /* istanbul ignore next */ []));
|
|
1068
1069
|
this.openingForLine = new Set();
|
|
1069
1070
|
this.isVatExclusive = () => this.vatMode() === 'EXCLUSIVE';
|
|
1070
1071
|
}
|
|
@@ -1135,10 +1136,10 @@ class InvoiceManageLine {
|
|
|
1135
1136
|
});
|
|
1136
1137
|
}
|
|
1137
1138
|
saveLines() { }
|
|
1138
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1139
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
1139
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceManageLine, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1140
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: InvoiceManageLine, isStandalone: true, selector: "rolatech-invoice-manage-line", ngImport: i0, template: "<!-- invoice-lines.html -->\n<div class=\"rounded-xl border border-(--rt-border-color) p-4 grid space-y-3\">\n <!-- Header -->\n <div class=\"flex items-center justify-between\">\n <h2 class=\"text-sm font-semibold\">Invoice lines</h2>\n\n <div class=\"flex items-center gap-2\">\n <button mat-icon-button type=\"button\" aria-label=\"Toggle edit\" (click)=\"toggleEdit()\">\n <mat-icon> {{ editMode() ? 'done' : 'edit' }} </mat-icon>\n </button>\n <button mat-stroked-button type=\"button\" [disabled]=\"store.dirty() || store.status() === 'saving'\" (click)=\"saveLines()\">\n @if (store.status() === 'saving') { Saving\u2026 } @else { Save lines }\n </button>\n </div>\n </div>\n\n <!-- Column labels (desktop only) -->\n <div class=\"hidden lg:grid grid-cols-12 gap-2 text-sm font-medium px-2 text-(--rt-text-secondary)\">\n <!-- Drag column -->\n @if (editMode()) {\n <div class=\"col-span-1\"></div>\n }\n\n <!-- Type -->\n <div [class]=\"editMode() ? 'col-span-3' : 'col-span-3'\">Type</div>\n\n <!-- Description (take extra space when not editing) -->\n <div [class]=\"editMode() ? 'col-span-4' : 'col-span-5'\">Title</div>\n\n <!-- Unit Price -->\n <div class=\"col-span-2 text-right\">Unit Price</div>\n\n <!-- Tax % (optional) -->\n @if (!isVatExclusive()) {\n <div class=\"col-span-1 text-right\">Tax %</div>\n }\n\n <!-- Amount (take extra space when not editing) -->\n <div [class]=\"editMode() ? 'col-span-1' : 'col-span-2'\" class=\"text-right\">Amount</div>\n\n <!-- Delete column -->\n @if (editMode()) {\n <div class=\"col-span-1\"></div>\n }\n </div>\n\n <!-- invoice-lines.html (rows section) -->\n\n <div cdkDropList (cdkDropListDropped)=\"drop($event)\" [cdkDropListDisabled]=\"!editMode()\" class=\"space-y-2\">\n @if (lines().length > 0) { @for (line of lines(); track line.id; let i = $index) {\n\n <div\n cdkDrag\n class=\"grid grid-cols-12 gap-2 items-start rounded-xl border border-(--rt-border-color) bg-(--rt-background) p-2 hover:bg-(--rt-raised-background)\"\n [class.opacity-70]=\"!editMode()\"\n >\n <!-- Drag handle column (only in edit mode) -->\n @if (editMode()) {\n <div class=\"col-span-1 flex items-center justify-center\">\n <button mat-icon-button type=\"button\" cdkDragHandle aria-label=\"Reorder line\" (click)=\"$event.stopPropagation()\">\n <mat-icon>drag_indicator</mat-icon>\n </button>\n </div>\n }\n\n <!-- Type -->\n <div class=\"col-span-12 lg:col-span-3\">\n <rolatech-enum-select\n [resource]=\"'billing'\"\n [enumName]=\"'InvoiceLineType'\"\n [label]=\"'Line type'\"\n [placeholder]=\"'Select\u2026'\"\n [value]=\"line.type\"\n (valueChange)=\"editMode() && update(line.id!, { type: $event })\"\n ></rolatech-enum-select>\n </div>\n\n <!-- Title (expands when NOT editMode) -->\n <div [class]=\"editMode() ? 'col-span-12 lg:col-span-4' : 'col-span-12 lg:col-span-5'\">\n <mat-form-field appearance=\"fill\" class=\"w-full\" subscriptSizing=\"dynamic\">\n <mat-label>Title</mat-label>\n <input\n matInput\n placeholder=\"e.g. Photo editing package\"\n [readonly]=\"!editMode()\"\n [readonly]=\"isPropertyLine(line.type)\"\n (focus)=\"onTitleFocus(line)\"\n (click)=\"onTitleFocus(line)\"\n (keydown)=\"onTitleKeydown($event, line)\"\n [value]=\"line.title\"\n (input)=\"editMode() && update(line.id!, { title: $any($event.target).value })\"\n />\n </mat-form-field>\n </div>\n\n <!-- Unit Price -->\n <div class=\"col-span-6 lg:col-span-2\">\n <mat-form-field appearance=\"fill\" class=\"w-full\" subscriptSizing=\"dynamic\">\n <mat-label>Unit</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n class=\"text-right\"\n [readonly]=\"!editMode()\"\n [value]=\"line.unitPrice\"\n (input)=\"editMode() && update(line.id!, { unitPrice: +$any($event.target).value })\"\n />\n </mat-form-field>\n </div>\n\n <!-- Tax % (optional, hidden if VAT exclusive) -->\n @if (!isVatExclusive()) {\n <div class=\"col-span-6 lg:col-span-1\">\n <mat-form-field appearance=\"fill\" class=\"w-full\" subscriptSizing=\"dynamic\">\n <mat-label>Tax %</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n class=\"text-right\"\n [readonly]=\"!editMode()\"\n [value]=\"line.vatRate\"\n (input)=\"editMode() && update(line.id!, { vatRate: +$any($event.target).value })\"\n />\n </mat-form-field>\n </div>\n }\n\n <!-- Amount (expands when NOT editMode) -->\n <div\n [class]=\"editMode() ? 'col-span-6 lg:col-span-1' : 'col-span-6 lg:col-span-2'\"\n class=\"flex items-center justify-end px-1 lg:pt-3\"\n >\n <div class=\"text-right font-semibold text-(--rt-text-secondary)\">{{ line.lineTotal | price }}</div>\n </div>\n\n <!-- Delete column (only in edit mode) -->\n @if (editMode()) {\n <div class=\"col-span-6 lg:col-span-1 flex items-center justify-end lg:pt-1\">\n <button mat-icon-button color=\"warn\" type=\"button\" aria-label=\"Delete line\" (click)=\"remove(line.id!)\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n }\n </div>\n } } @else {\n <div class=\"py-8 text-center text-sm text-(--rt-text-secondary)\">No invoice lines</div>\n }\n </div>\n\n <!-- Footer -->\n <div>\n <button mat-stroked-button (click)=\"add()\">Add item</button>\n </div>\n</div>\n", styles: [".cdk-drag-preview{box-shadow:0 10px 20px #00000026;border-radius:12px}.cdk-drag-placeholder{opacity:.3}\n"], dependencies: [{ kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i1$3.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i1$3.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i1$3.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: EnumSelect, selector: "rolatech-enum-select", inputs: ["resource", "enumName", "label", "placeholder", "disabled", "value"], outputs: ["valueChange"] }, { kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
1140
1141
|
}
|
|
1141
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1142
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceManageLine, decorators: [{
|
|
1142
1143
|
type: Component,
|
|
1143
1144
|
args: [{ selector: 'rolatech-invoice-manage-line', imports: [DragDropModule, MatButtonModule, MatIcon, MatFormFieldModule, MatInputModule, PricePipe, EnumSelect], encapsulation: ViewEncapsulation.None, template: "<!-- invoice-lines.html -->\n<div class=\"rounded-xl border border-(--rt-border-color) p-4 grid space-y-3\">\n <!-- Header -->\n <div class=\"flex items-center justify-between\">\n <h2 class=\"text-sm font-semibold\">Invoice lines</h2>\n\n <div class=\"flex items-center gap-2\">\n <button mat-icon-button type=\"button\" aria-label=\"Toggle edit\" (click)=\"toggleEdit()\">\n <mat-icon> {{ editMode() ? 'done' : 'edit' }} </mat-icon>\n </button>\n <button mat-stroked-button type=\"button\" [disabled]=\"store.dirty() || store.status() === 'saving'\" (click)=\"saveLines()\">\n @if (store.status() === 'saving') { Saving\u2026 } @else { Save lines }\n </button>\n </div>\n </div>\n\n <!-- Column labels (desktop only) -->\n <div class=\"hidden lg:grid grid-cols-12 gap-2 text-sm font-medium px-2 text-(--rt-text-secondary)\">\n <!-- Drag column -->\n @if (editMode()) {\n <div class=\"col-span-1\"></div>\n }\n\n <!-- Type -->\n <div [class]=\"editMode() ? 'col-span-3' : 'col-span-3'\">Type</div>\n\n <!-- Description (take extra space when not editing) -->\n <div [class]=\"editMode() ? 'col-span-4' : 'col-span-5'\">Title</div>\n\n <!-- Unit Price -->\n <div class=\"col-span-2 text-right\">Unit Price</div>\n\n <!-- Tax % (optional) -->\n @if (!isVatExclusive()) {\n <div class=\"col-span-1 text-right\">Tax %</div>\n }\n\n <!-- Amount (take extra space when not editing) -->\n <div [class]=\"editMode() ? 'col-span-1' : 'col-span-2'\" class=\"text-right\">Amount</div>\n\n <!-- Delete column -->\n @if (editMode()) {\n <div class=\"col-span-1\"></div>\n }\n </div>\n\n <!-- invoice-lines.html (rows section) -->\n\n <div cdkDropList (cdkDropListDropped)=\"drop($event)\" [cdkDropListDisabled]=\"!editMode()\" class=\"space-y-2\">\n @if (lines().length > 0) { @for (line of lines(); track line.id; let i = $index) {\n\n <div\n cdkDrag\n class=\"grid grid-cols-12 gap-2 items-start rounded-xl border border-(--rt-border-color) bg-(--rt-background) p-2 hover:bg-(--rt-raised-background)\"\n [class.opacity-70]=\"!editMode()\"\n >\n <!-- Drag handle column (only in edit mode) -->\n @if (editMode()) {\n <div class=\"col-span-1 flex items-center justify-center\">\n <button mat-icon-button type=\"button\" cdkDragHandle aria-label=\"Reorder line\" (click)=\"$event.stopPropagation()\">\n <mat-icon>drag_indicator</mat-icon>\n </button>\n </div>\n }\n\n <!-- Type -->\n <div class=\"col-span-12 lg:col-span-3\">\n <rolatech-enum-select\n [resource]=\"'billing'\"\n [enumName]=\"'InvoiceLineType'\"\n [label]=\"'Line type'\"\n [placeholder]=\"'Select\u2026'\"\n [value]=\"line.type\"\n (valueChange)=\"editMode() && update(line.id!, { type: $event })\"\n ></rolatech-enum-select>\n </div>\n\n <!-- Title (expands when NOT editMode) -->\n <div [class]=\"editMode() ? 'col-span-12 lg:col-span-4' : 'col-span-12 lg:col-span-5'\">\n <mat-form-field appearance=\"fill\" class=\"w-full\" subscriptSizing=\"dynamic\">\n <mat-label>Title</mat-label>\n <input\n matInput\n placeholder=\"e.g. Photo editing package\"\n [readonly]=\"!editMode()\"\n [readonly]=\"isPropertyLine(line.type)\"\n (focus)=\"onTitleFocus(line)\"\n (click)=\"onTitleFocus(line)\"\n (keydown)=\"onTitleKeydown($event, line)\"\n [value]=\"line.title\"\n (input)=\"editMode() && update(line.id!, { title: $any($event.target).value })\"\n />\n </mat-form-field>\n </div>\n\n <!-- Unit Price -->\n <div class=\"col-span-6 lg:col-span-2\">\n <mat-form-field appearance=\"fill\" class=\"w-full\" subscriptSizing=\"dynamic\">\n <mat-label>Unit</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n class=\"text-right\"\n [readonly]=\"!editMode()\"\n [value]=\"line.unitPrice\"\n (input)=\"editMode() && update(line.id!, { unitPrice: +$any($event.target).value })\"\n />\n </mat-form-field>\n </div>\n\n <!-- Tax % (optional, hidden if VAT exclusive) -->\n @if (!isVatExclusive()) {\n <div class=\"col-span-6 lg:col-span-1\">\n <mat-form-field appearance=\"fill\" class=\"w-full\" subscriptSizing=\"dynamic\">\n <mat-label>Tax %</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n class=\"text-right\"\n [readonly]=\"!editMode()\"\n [value]=\"line.vatRate\"\n (input)=\"editMode() && update(line.id!, { vatRate: +$any($event.target).value })\"\n />\n </mat-form-field>\n </div>\n }\n\n <!-- Amount (expands when NOT editMode) -->\n <div\n [class]=\"editMode() ? 'col-span-6 lg:col-span-1' : 'col-span-6 lg:col-span-2'\"\n class=\"flex items-center justify-end px-1 lg:pt-3\"\n >\n <div class=\"text-right font-semibold text-(--rt-text-secondary)\">{{ line.lineTotal | price }}</div>\n </div>\n\n <!-- Delete column (only in edit mode) -->\n @if (editMode()) {\n <div class=\"col-span-6 lg:col-span-1 flex items-center justify-end lg:pt-1\">\n <button mat-icon-button color=\"warn\" type=\"button\" aria-label=\"Delete line\" (click)=\"remove(line.id!)\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n }\n </div>\n } } @else {\n <div class=\"py-8 text-center text-sm text-(--rt-text-secondary)\">No invoice lines</div>\n }\n </div>\n\n <!-- Footer -->\n <div>\n <button mat-stroked-button (click)=\"add()\">Add item</button>\n </div>\n</div>\n", styles: [".cdk-drag-preview{box-shadow:0 10px 20px #00000026;border-radius:12px}.cdk-drag-placeholder{opacity:.3}\n"] }]
|
|
1144
1145
|
}] });
|
|
@@ -1152,8 +1153,8 @@ class InvoiceManageDetail extends BaseComponent {
|
|
|
1152
1153
|
this.invoice = this.store.invoice;
|
|
1153
1154
|
this.status = InvoiceStatus;
|
|
1154
1155
|
// convenience derived values for template
|
|
1155
|
-
this.loading = computed(() => this.store.status() === 'loading', ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
1156
|
-
this.pdfUrl = computed(() => this.store.invoice()?.pdfUrl ?? '', ...(ngDevMode ? [{ debugName: "pdfUrl" }] : []));
|
|
1156
|
+
this.loading = computed(() => this.store.status() === 'loading', ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
1157
|
+
this.pdfUrl = computed(() => this.store.invoice()?.pdfUrl ?? '', ...(ngDevMode ? [{ debugName: "pdfUrl" }] : /* istanbul ignore next */ []));
|
|
1157
1158
|
}
|
|
1158
1159
|
ngOnInit() {
|
|
1159
1160
|
this.store.load(this.id);
|
|
@@ -1252,19 +1253,19 @@ class InvoiceManageDetail extends BaseComponent {
|
|
|
1252
1253
|
}
|
|
1253
1254
|
});
|
|
1254
1255
|
}
|
|
1255
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1256
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
1256
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceManageDetail, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
1257
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: InvoiceManageDetail, isStandalone: true, selector: "rolatech-invoice-manage-detail", usesInheritance: true, ngImport: i0, template: "@if (store.status() === 'loading') {\n<div class=\"flex justify-center py-16\">Loading invoice\u2026</div>\n} @else if (!store.invoice()) {\n<div class=\"flex justify-center py-16\">Invoice not found.</div>\n} @else { @if (store.invoice(); as inv) {\n\n<!-- Sticky header -->\n<div class=\"sticky top-0 z-10 border-b border-(--rt-border-color) bg-(--rt-background)\">\n <div class=\"px-4 py-3 flex items-start justify-between gap-4\">\n <div class=\"min-w-0\">\n <div class=\"flex items-center gap-2\">\n <h1 class=\"text-lg font-semibold truncate\">Invoice {{ inv.invoiceNumber || inv.id }}</h1>\n\n <span class=\"px-2 py-0.5 rounded-full text-xs font-semibold\" [class]=\"statusBadgeClass(inv.status)\">\n {{ inv.status }}\n </span>\n\n @if (store.dirty() ) {\n <span class=\"text-xs text-(--rt-text-secondary)\">\u2022 Unsaved changes</span>\n }\n </div>\n\n <div class=\"mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-sm text-(--rt-text-secondary)\">\n <span class=\"truncate\"> {{ inv.fullName }} \u2022 {{ inv.email }} </span>\n <span>Created {{ inv.createdAt | date:'dd/MM/yyyy':'Europe/London' }}</span>\n @if (inv.dueAt) { <span>Due {{ inv.dueAt | date:'dd/MM/yyyy':'Europe/London' }}</span> }\n </div>\n </div>\n\n <!-- Desktop actions -->\n\n <div class=\"hidden lg:flex items-center gap-2 shrink-0\">\n @if (inv.pdfUrl) {\n <button mat-stroked-button type=\"button\" (click)=\"openPdf()\">Preview PDF</button>\n } @if (inv.status.toString() === 'ISSUED') {\n <button mat-flat-button type=\"button\" (click)=\"sendEmail()\">Send email</button>\n } @if (inv.status.toString() !== 'SENT' && inv.status.toString() !== 'PAID') {\n <button mat-flat-button (click)=\"edit()\">Edit</button>\n } @if (inv.status.toString() === 'SENT') {\n <button mat-flat-button type=\"button\" (click)=\"sendEmail()\">Resend</button>\n }\n </div>\n\n <!-- Mobile actions -->\n <div class=\"lg:hidden shrink-0\">\n <button mat-icon-button type=\"button\" [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n </div>\n</div>\n\n<!-- Body -->\n<div class=\"p-4 space-y-4\">\n <div class=\"grid grid-cols-1 lg:grid-cols-3 gap-4\">\n <!-- Left column -->\n <div class=\"grid lg:col-span-2 space-y-4\">\n <!-- Customer -->\n <div class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-background) p-4\">\n <div class=\"flex items-center justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Customer</div>\n <div class=\"text-sm text-(--rt-text-secondary)\">{{ inv.fullName }} \u2022 {{ inv.email }}</div>\n </div>\n </div>\n\n <div class=\"mt-3 grid grid-cols-1 sm:grid-cols-2 gap-3 text-sm\">\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Phone</div>\n <div class=\"font-medium\">{{ inv.phone || '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Currency</div>\n <div class=\"font-medium\">{{ inv.currency }}</div>\n </div>\n </div>\n </div>\n\n <!-- Lines -->\n\n @if (inv.status.toString() !== 'SENT' && inv.status.toString() !== 'PAID') {\n <rolatech-invoice-manage-line></rolatech-invoice-manage-line>\n } @else {\n <rolatech-invoice-lines [lines]=\"store.lines()\"></rolatech-invoice-lines>\n }\n\n <!-- Notes (optional) -->\n @if (inv.note) {\n <div class=\"rounded-xl border border-(--rt-border-color) p-4\">\n <div class=\"text-sm font-semibold\">Notes</div>\n <div class=\"mt-2 text-sm text-(--rt-text-secondary) whitespace-pre-wrap\">{{ inv.note }}</div>\n </div>\n }\n </div>\n\n <!-- Right rail -->\n <div class=\"space-y-4\">\n <div class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-background) p-4\">\n <div class=\"text-sm font-semibold\">Summary</div>\n\n @if (store.totals(); as t) {\n <div class=\"mt-3 space-y-2 text-sm\">\n <div class=\"flex justify-between\">\n <span class=\"text-(--rt-text-secondary)\">Subtotal</span>\n <span class=\"font-medium\">{{ t.subtotal | price }}</span>\n </div>\n\n <div class=\"flex justify-between\">\n <span class=\"text-(--rt-text-secondary)\">VAT</span>\n <span class=\"font-medium\">{{ t.vatTotal | price }}</span>\n </div>\n\n @if (inv.holdingDepositApplied) {\n <div class=\"flex justify-between\">\n <span class=\"text-(--rt-text-secondary)\">Credit</span>\n <span class=\"font-medium\">-{{ inv.holdingDepositApplied | price }}</span>\n </div>\n }\n\n <div class=\"pt-2 mt-2 border-t border-(--rt-border-color) flex justify-between text-base\">\n <span class=\"font-semibold\">Total</span>\n <span class=\"font-semibold\">{{ t.total | price }}</span>\n </div>\n </div>\n }\n </div>\n <div class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-background) p-4\">\n <div class=\"text-sm font-semibold\">Payment</div>\n <div class=\"mt-2 text-sm text-(--rt-text-secondary)\">\n Status:\n <span class=\"font-medium text-(--rt-text-primary)\">{{ inv.status.toString() === 'PAID' ? 'Paid': 'Unpaid' }}</span>\n </div>\n @if (inv.paidAt) {\n <div class=\"mt-1 text-sm text-(--rt-text-secondary)\">\n Paid: <span class=\"font-medium text-(--rt-text-primary)\">{{ inv.paidAt | date:'dd/MM/yyyy':'Europe/London' }}</span>\n </div>\n }\n </div>\n </div>\n </div>\n</div>\n\n<mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n @if (inv.pdfUrl) {\n <button mat-menu-item type=\"button\" (click)=\"openPdf()\">Preview PDF</button>\n } @if (inv.status.toString() === 'ISSUED') {\n <button mat-menu-item type=\"button\" (click)=\"sendEmail()\">Send email</button>\n } @if (inv.status.toString() !== 'SENT' && inv.status.toString() !== 'PAID') {\n <button mat-menu-item type=\"button\" (click)=\"edit()\">Edit</button>\n\n }\n</mat-menu>\n} }\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MaterialModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i3$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i3$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i3$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: InvoiceLines, selector: "rolatech-invoice-lines", inputs: ["lines"] }, { kind: "component", type: InvoiceManageLine, selector: "rolatech-invoice-manage-line" }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }] }); }
|
|
1257
1258
|
}
|
|
1258
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1259
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceManageDetail, decorators: [{
|
|
1259
1260
|
type: Component,
|
|
1260
1261
|
args: [{ selector: 'rolatech-invoice-manage-detail', standalone: true, imports: [CommonModule, MaterialModule, MatMenuModule, InvoiceLines, InvoiceManageLine, PricePipe], template: "@if (store.status() === 'loading') {\n<div class=\"flex justify-center py-16\">Loading invoice\u2026</div>\n} @else if (!store.invoice()) {\n<div class=\"flex justify-center py-16\">Invoice not found.</div>\n} @else { @if (store.invoice(); as inv) {\n\n<!-- Sticky header -->\n<div class=\"sticky top-0 z-10 border-b border-(--rt-border-color) bg-(--rt-background)\">\n <div class=\"px-4 py-3 flex items-start justify-between gap-4\">\n <div class=\"min-w-0\">\n <div class=\"flex items-center gap-2\">\n <h1 class=\"text-lg font-semibold truncate\">Invoice {{ inv.invoiceNumber || inv.id }}</h1>\n\n <span class=\"px-2 py-0.5 rounded-full text-xs font-semibold\" [class]=\"statusBadgeClass(inv.status)\">\n {{ inv.status }}\n </span>\n\n @if (store.dirty() ) {\n <span class=\"text-xs text-(--rt-text-secondary)\">\u2022 Unsaved changes</span>\n }\n </div>\n\n <div class=\"mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-sm text-(--rt-text-secondary)\">\n <span class=\"truncate\"> {{ inv.fullName }} \u2022 {{ inv.email }} </span>\n <span>Created {{ inv.createdAt | date:'dd/MM/yyyy':'Europe/London' }}</span>\n @if (inv.dueAt) { <span>Due {{ inv.dueAt | date:'dd/MM/yyyy':'Europe/London' }}</span> }\n </div>\n </div>\n\n <!-- Desktop actions -->\n\n <div class=\"hidden lg:flex items-center gap-2 shrink-0\">\n @if (inv.pdfUrl) {\n <button mat-stroked-button type=\"button\" (click)=\"openPdf()\">Preview PDF</button>\n } @if (inv.status.toString() === 'ISSUED') {\n <button mat-flat-button type=\"button\" (click)=\"sendEmail()\">Send email</button>\n } @if (inv.status.toString() !== 'SENT' && inv.status.toString() !== 'PAID') {\n <button mat-flat-button (click)=\"edit()\">Edit</button>\n } @if (inv.status.toString() === 'SENT') {\n <button mat-flat-button type=\"button\" (click)=\"sendEmail()\">Resend</button>\n }\n </div>\n\n <!-- Mobile actions -->\n <div class=\"lg:hidden shrink-0\">\n <button mat-icon-button type=\"button\" [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n </div>\n</div>\n\n<!-- Body -->\n<div class=\"p-4 space-y-4\">\n <div class=\"grid grid-cols-1 lg:grid-cols-3 gap-4\">\n <!-- Left column -->\n <div class=\"grid lg:col-span-2 space-y-4\">\n <!-- Customer -->\n <div class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-background) p-4\">\n <div class=\"flex items-center justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Customer</div>\n <div class=\"text-sm text-(--rt-text-secondary)\">{{ inv.fullName }} \u2022 {{ inv.email }}</div>\n </div>\n </div>\n\n <div class=\"mt-3 grid grid-cols-1 sm:grid-cols-2 gap-3 text-sm\">\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Phone</div>\n <div class=\"font-medium\">{{ inv.phone || '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-(--rt-text-secondary)\">Currency</div>\n <div class=\"font-medium\">{{ inv.currency }}</div>\n </div>\n </div>\n </div>\n\n <!-- Lines -->\n\n @if (inv.status.toString() !== 'SENT' && inv.status.toString() !== 'PAID') {\n <rolatech-invoice-manage-line></rolatech-invoice-manage-line>\n } @else {\n <rolatech-invoice-lines [lines]=\"store.lines()\"></rolatech-invoice-lines>\n }\n\n <!-- Notes (optional) -->\n @if (inv.note) {\n <div class=\"rounded-xl border border-(--rt-border-color) p-4\">\n <div class=\"text-sm font-semibold\">Notes</div>\n <div class=\"mt-2 text-sm text-(--rt-text-secondary) whitespace-pre-wrap\">{{ inv.note }}</div>\n </div>\n }\n </div>\n\n <!-- Right rail -->\n <div class=\"space-y-4\">\n <div class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-background) p-4\">\n <div class=\"text-sm font-semibold\">Summary</div>\n\n @if (store.totals(); as t) {\n <div class=\"mt-3 space-y-2 text-sm\">\n <div class=\"flex justify-between\">\n <span class=\"text-(--rt-text-secondary)\">Subtotal</span>\n <span class=\"font-medium\">{{ t.subtotal | price }}</span>\n </div>\n\n <div class=\"flex justify-between\">\n <span class=\"text-(--rt-text-secondary)\">VAT</span>\n <span class=\"font-medium\">{{ t.vatTotal | price }}</span>\n </div>\n\n @if (inv.holdingDepositApplied) {\n <div class=\"flex justify-between\">\n <span class=\"text-(--rt-text-secondary)\">Credit</span>\n <span class=\"font-medium\">-{{ inv.holdingDepositApplied | price }}</span>\n </div>\n }\n\n <div class=\"pt-2 mt-2 border-t border-(--rt-border-color) flex justify-between text-base\">\n <span class=\"font-semibold\">Total</span>\n <span class=\"font-semibold\">{{ t.total | price }}</span>\n </div>\n </div>\n }\n </div>\n <div class=\"rounded-xl border border-(--rt-border-color) bg-(--rt-background) p-4\">\n <div class=\"text-sm font-semibold\">Payment</div>\n <div class=\"mt-2 text-sm text-(--rt-text-secondary)\">\n Status:\n <span class=\"font-medium text-(--rt-text-primary)\">{{ inv.status.toString() === 'PAID' ? 'Paid': 'Unpaid' }}</span>\n </div>\n @if (inv.paidAt) {\n <div class=\"mt-1 text-sm text-(--rt-text-secondary)\">\n Paid: <span class=\"font-medium text-(--rt-text-primary)\">{{ inv.paidAt | date:'dd/MM/yyyy':'Europe/London' }}</span>\n </div>\n }\n </div>\n </div>\n </div>\n</div>\n\n<mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n @if (inv.pdfUrl) {\n <button mat-menu-item type=\"button\" (click)=\"openPdf()\">Preview PDF</button>\n } @if (inv.status.toString() === 'ISSUED') {\n <button mat-menu-item type=\"button\" (click)=\"sendEmail()\">Send email</button>\n } @if (inv.status.toString() !== 'SENT' && inv.status.toString() !== 'PAID') {\n <button mat-menu-item type=\"button\" (click)=\"edit()\">Edit</button>\n\n }\n</mat-menu>\n} }\n" }]
|
|
1261
1262
|
}] });
|
|
1262
1263
|
|
|
1263
1264
|
class InvoiceManageDownload {
|
|
1264
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1265
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.
|
|
1265
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceManageDownload, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1266
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.5", type: InvoiceManageDownload, isStandalone: true, selector: "rolatech-invoice-manage-download", ngImport: i0, template: "<p>invoice-manage-download works!</p>\n", styles: [""] }); }
|
|
1266
1267
|
}
|
|
1267
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1268
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: InvoiceManageDownload, decorators: [{
|
|
1268
1269
|
type: Component,
|
|
1269
1270
|
args: [{ selector: 'rolatech-invoice-manage-download', imports: [], template: "<p>invoice-manage-download works!</p>\n" }]
|
|
1270
1271
|
}] });
|
|
@@ -1300,10 +1301,10 @@ class AgentInvoiceIndex extends BaseComponent {
|
|
|
1300
1301
|
this.invoiceStatus = InvoiceStatus;
|
|
1301
1302
|
this.select = 0;
|
|
1302
1303
|
this.loading = false;
|
|
1303
|
-
this.invoices = signal([], ...(ngDevMode ? [{ debugName: "invoices" }] : []));
|
|
1304
|
+
this.invoices = signal([], ...(ngDevMode ? [{ debugName: "invoices" }] : /* istanbul ignore next */ []));
|
|
1304
1305
|
this.length = 100;
|
|
1305
1306
|
this.pageSize = 15;
|
|
1306
|
-
this.pageIndex = signal(0, ...(ngDevMode ? [{ debugName: "pageIndex" }] : []));
|
|
1307
|
+
this.pageIndex = signal(0, ...(ngDevMode ? [{ debugName: "pageIndex" }] : /* istanbul ignore next */ []));
|
|
1307
1308
|
this.pageSizeOptions = [5, 10, 25, 100];
|
|
1308
1309
|
this.links = [
|
|
1309
1310
|
{
|
|
@@ -1337,7 +1338,7 @@ class AgentInvoiceIndex extends BaseComponent {
|
|
|
1337
1338
|
{ label: 'Issued', value: invoices.filter((item) => item.status === InvoiceStatus.ISSUED || item.status === InvoiceStatus.SENT).length, hint: 'Awaiting payment' },
|
|
1338
1339
|
{ label: 'Paid', value: invoices.filter((item) => item.status === InvoiceStatus.PAID).length, hint: 'Completed payments' },
|
|
1339
1340
|
];
|
|
1340
|
-
}, ...(ngDevMode ? [{ debugName: "stats" }] : []));
|
|
1341
|
+
}, ...(ngDevMode ? [{ debugName: "stats" }] : /* istanbul ignore next */ []));
|
|
1341
1342
|
}
|
|
1342
1343
|
find() {
|
|
1343
1344
|
this.router.navigate([], {
|
|
@@ -1386,9 +1387,9 @@ class AgentInvoiceIndex extends BaseComponent {
|
|
|
1386
1387
|
}))
|
|
1387
1388
|
.subscribe({
|
|
1388
1389
|
next: (res) => {
|
|
1389
|
-
this.invoices.set(res.data);
|
|
1390
|
+
this.invoices.set(res.data ?? []);
|
|
1390
1391
|
this.meta = res.meta;
|
|
1391
|
-
this.length = res.meta
|
|
1392
|
+
this.length = res.meta?.pagination?.count ?? res.data?.length ?? 0;
|
|
1392
1393
|
},
|
|
1393
1394
|
error: () => {
|
|
1394
1395
|
this.invoices.set([]);
|
|
@@ -1432,10 +1433,10 @@ class AgentInvoiceIndex extends BaseComponent {
|
|
|
1432
1433
|
}
|
|
1433
1434
|
return fallback;
|
|
1434
1435
|
}
|
|
1435
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1436
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: AgentInvoiceIndex, isStandalone: true, selector: "rolatech-agent-invoice-index", usesInheritance: true, ngImport: i0, template: "<section class=\"invoice-index-page\">\n <section class=\"invoice-index-page__hero\">\n <div class=\"invoice-index-page__copy\">\n <span class=\"invoice-index-page__eyebrow\">{{ pageMeta.eyebrow }}</span>\n <h1 class=\"invoice-index-page__title\">{{ pageMeta.title }}</h1>\n <p class=\"invoice-index-page__description\">{{ pageMeta.description }}</p>\n </div>\n\n <div class=\"invoice-index-page__stats\">\n @for (stat of stats(); track stat.label) {\n <article class=\"invoice-index-page__stat\">\n <span class=\"invoice-index-page__stat-value\">{{ stat.value }}</span>\n <span class=\"invoice-index-page__stat-label\">{{ stat.label }}</span>\n <span class=\"invoice-index-page__stat-hint\">{{ stat.hint }}</span>\n </article>\n }\n </div>\n </section>\n\n <section class=\"invoice-index-page__filters\">\n <nav class=\"invoice-index-page__status-nav\" aria-label=\"Invoice status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"invoice-index-page__status-link\"\n [class.invoice-index-page__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <div class=\"invoice-index-page__filter-grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label>Type</mat-label>\n <mat-select name=\"type\" [(ngModel)]=\"filterOptions.type\">\n @for (type of invoiceType | keyvalue; track type.key) {\n <mat-option [value]=\"type.key\">{{ type.value }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Status</mat-label>\n <mat-select [compareWith]=\"statusCompareFn\" [(ngModel)]=\"filterOptions.status\">\n @for (status of invoiceStatus | keyvalue; track status.key) {\n <mat-option [value]=\"status.key\">{{ status.value }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <div class=\"invoice-index-page__filter-actions\">\n <button mat-flat-button (click)=\"find()\">Apply filters</button>\n <button mat-stroked-button (click)=\"resetFilter()\">Reset</button>\n </div>\n </div>\n </section>\n\n <section class=\"invoice-index-page__list\">\n @if (loading) {\n @for (dummy of [0, 1, 2, 3]; track dummy) {\n <rolatech-invoice-item-skeleton></rolatech-invoice-item-skeleton>\n }\n } @else if (invoices().length > 0) {\n @for (item of invoices(); track item.id) {\n <rolatech-invoice-item class=\"cursor-pointer\" [routerLink]=\"['./', item.id]\" [invoice]=\"item\"></rolatech-invoice-item>\n }\n } @else {\n <div class=\"invoice-index-page__empty\">\n <h3>No invoices yet</h3>\n <p>The invoices for this workspace will appear here once billing is created.</p>\n </div>\n }\n\n <mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n</section>\n", styles: [".collapsed{max-height:0;overflow:hidden;transition:max-height .5s cubic-bezier(.4,0,.2,1)}.expanded{max-height:1000px}:host{display:block;padding:1rem}.invoice-index-page{display:flex;flex-direction:column;gap:1rem}.invoice-index-page__hero,.invoice-index-page__filters,.invoice-index-page__list{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.invoice-index-page__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.invoice-index-page__copy{display:flex;flex-direction:column;gap:.75rem}.invoice-index-page__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.invoice-index-page__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.invoice-index-page__description{margin:0;color:var(--rt-text-secondary);line-height:1.7}.invoice-index-page__stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.invoice-index-page__stat{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.invoice-index-page__stat-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.invoice-index-page__stat-label,.invoice-index-page__stat-hint{color:var(--rt-text-secondary);font-size:.84rem}.invoice-index-page__filters,.invoice-index-page__list{display:flex;flex-direction:column;gap:1rem;padding:1rem 1.1rem}.invoice-index-page__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.invoice-index-page__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none}.invoice-index-page__status-link:hover,.invoice-index-page__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.invoice-index-page__filter-grid{display:grid;gap:.85rem}.invoice-index-page__filter-grid mat-form-field{width:100%}.invoice-index-page__filter-actions{display:flex;flex-wrap:wrap;gap:.75rem}.invoice-index-page__empty{display:flex;flex-direction:column;justify-content:center;min-height:10rem;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.invoice-index-page__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.invoice-index-page__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:900px){.invoice-index-page__hero{grid-template-columns:minmax(0,1.6fr) minmax(19rem,.95fr);align-items:start}.invoice-index-page__filter-grid{grid-template-columns:repeat(3,minmax(0,1fr));align-items:start}}@media(min-width:768px){:host{padding:1.25rem}}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: InvoiceItem, selector: "rolatech-invoice-item", inputs: ["invoice"], outputs: ["download"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i6.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: InvoiceItemSkeleton, selector: "rolatech-invoice-item-skeleton" }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }] }); }
|
|
1436
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AgentInvoiceIndex, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
1437
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: AgentInvoiceIndex, isStandalone: true, selector: "rolatech-agent-invoice-index", usesInheritance: true, ngImport: i0, template: "<section class=\"invoice-index-page\">\n <section class=\"invoice-index-page__hero\">\n <div class=\"invoice-index-page__copy\">\n <span class=\"invoice-index-page__eyebrow\">{{ pageMeta.eyebrow }}</span>\n <h1 class=\"invoice-index-page__title\">{{ pageMeta.title }}</h1>\n <p class=\"invoice-index-page__description\">{{ pageMeta.description }}</p>\n </div>\n\n <div class=\"invoice-index-page__stats\">\n @for (stat of stats(); track stat.label) {\n <article class=\"invoice-index-page__stat\">\n <span class=\"invoice-index-page__stat-value\">{{ stat.value }}</span>\n <span class=\"invoice-index-page__stat-label\">{{ stat.label }}</span>\n <span class=\"invoice-index-page__stat-hint\">{{ stat.hint }}</span>\n </article>\n }\n </div>\n </section>\n\n <section class=\"invoice-index-page__filters\">\n <nav class=\"invoice-index-page__status-nav\" aria-label=\"Invoice status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"invoice-index-page__status-link\"\n [class.invoice-index-page__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <div class=\"invoice-index-page__filter-grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label>Type</mat-label>\n <mat-select name=\"type\" [(ngModel)]=\"filterOptions.type\">\n @for (type of invoiceType | keyvalue; track type.key) {\n <mat-option [value]=\"type.key\">{{ type.value }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Status</mat-label>\n <mat-select [compareWith]=\"statusCompareFn\" [(ngModel)]=\"filterOptions.status\">\n @for (status of invoiceStatus | keyvalue; track status.key) {\n <mat-option [value]=\"status.key\">{{ status.value }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <div class=\"invoice-index-page__filter-actions\">\n <button mat-flat-button (click)=\"find()\">Apply filters</button>\n <button mat-stroked-button (click)=\"resetFilter()\">Reset</button>\n </div>\n </div>\n </section>\n\n <section class=\"invoice-index-page__list\">\n @if (loading) {\n @for (dummy of [0, 1, 2, 3]; track dummy) {\n <rolatech-invoice-item-skeleton></rolatech-invoice-item-skeleton>\n }\n } @else if (invoices().length > 0) {\n @for (item of invoices(); track item.id) {\n <rolatech-invoice-item class=\"cursor-pointer\" [routerLink]=\"['./', item.id]\" [invoice]=\"item\"></rolatech-invoice-item>\n }\n } @else {\n <div class=\"invoice-index-page__empty\">\n <h3>No invoices yet</h3>\n <p>The invoices for this workspace will appear here once billing is created.</p>\n </div>\n }\n\n <mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n</section>\n", styles: [".collapsed{max-height:0;overflow:hidden;transition:max-height .5s cubic-bezier(.4,0,.2,1)}.expanded{max-height:1000px}:host{display:block;padding:1rem}.invoice-index-page{display:flex;flex-direction:column;gap:1rem}.invoice-index-page__hero,.invoice-index-page__filters,.invoice-index-page__list{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.invoice-index-page__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.invoice-index-page__copy{display:flex;flex-direction:column;gap:.75rem}.invoice-index-page__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.invoice-index-page__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.invoice-index-page__description{margin:0;color:var(--rt-text-secondary);line-height:1.7}.invoice-index-page__stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.invoice-index-page__stat{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.invoice-index-page__stat-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.invoice-index-page__stat-label,.invoice-index-page__stat-hint{color:var(--rt-text-secondary);font-size:.84rem}.invoice-index-page__filters,.invoice-index-page__list{display:flex;flex-direction:column;gap:1rem;padding:1rem 1.1rem}.invoice-index-page__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.invoice-index-page__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none}.invoice-index-page__status-link:hover,.invoice-index-page__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.invoice-index-page__filter-grid{display:grid;gap:.85rem}.invoice-index-page__filter-grid mat-form-field{width:100%}.invoice-index-page__filter-actions{display:flex;flex-wrap:wrap;gap:.75rem}.invoice-index-page__empty{display:flex;flex-direction:column;justify-content:center;min-height:10rem;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.invoice-index-page__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.invoice-index-page__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:900px){.invoice-index-page__hero{grid-template-columns:minmax(0,1.6fr) minmax(19rem,.95fr);align-items:start}.invoice-index-page__filter-grid{grid-template-columns:repeat(3,minmax(0,1fr));align-items:start}}@media(min-width:768px){:host{padding:1.25rem}}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: InvoiceItem, selector: "rolatech-invoice-item", inputs: ["invoice"], outputs: ["download"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i6.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: InvoiceItemSkeleton, selector: "rolatech-invoice-item-skeleton" }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }] }); }
|
|
1437
1438
|
}
|
|
1438
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1439
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AgentInvoiceIndex, decorators: [{
|
|
1439
1440
|
type: Component,
|
|
1440
1441
|
args: [{ selector: 'rolatech-agent-invoice-index', imports: [
|
|
1441
1442
|
RouterModule,
|
|
@@ -1456,8 +1457,8 @@ class AgentInvoiceDetail extends BaseComponent {
|
|
|
1456
1457
|
this.invoiceService = inject(InvoiceService);
|
|
1457
1458
|
this.paymentService = inject(PaymentService);
|
|
1458
1459
|
this.pdfUrl = '';
|
|
1459
|
-
this.loading = signal(true, ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
1460
|
-
this.invoice = signal(null, ...(ngDevMode ? [{ debugName: "invoice" }] : []));
|
|
1460
|
+
this.loading = signal(true, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
1461
|
+
this.invoice = signal(null, ...(ngDevMode ? [{ debugName: "invoice" }] : /* istanbul ignore next */ []));
|
|
1461
1462
|
this.status = InvoiceStatus;
|
|
1462
1463
|
this.pageMeta = {
|
|
1463
1464
|
baseLink: this.readRouteData('invoiceBaseLink', '/invoices'),
|
|
@@ -1537,10 +1538,10 @@ class AgentInvoiceDetail extends BaseComponent {
|
|
|
1537
1538
|
}
|
|
1538
1539
|
return fallback;
|
|
1539
1540
|
}
|
|
1540
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1541
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
1541
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AgentInvoiceDetail, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
1542
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: AgentInvoiceDetail, isStandalone: true, selector: "rolatech-agent-invoice-detail", usesInheritance: true, ngImport: i0, template: "<section class=\"invoice-detail-page\">\n @if (loading()) {\n <section class=\"invoice-detail-page__hero invoice-detail-page__hero--loading\">\n <div class=\"invoice-detail-page__hero-copy\">\n <span class=\"invoice-detail-page__eyebrow\">{{ pageMeta.eyebrow }}</span>\n <h1 class=\"invoice-detail-page__title\">Loading invoice...</h1>\n </div>\n </section>\n } @else if (!invoice()) {\n <section class=\"invoice-detail-page__panel\">\n <h2 class=\"invoice-detail-page__panel-title\">Invoice not found</h2>\n <p class=\"invoice-detail-page__panel-copy\">The requested invoice could not be loaded for this workspace.</p>\n </section>\n } @else if (invoice(); as invoice) {\n <section class=\"invoice-detail-page__hero\">\n <div class=\"invoice-detail-page__hero-copy\">\n <a class=\"invoice-detail-page__back\" [routerLink]=\"pageMeta.baseLink\">\u2190 {{ pageMeta.collectionTitle }}</a>\n <span class=\"invoice-detail-page__eyebrow\">{{ pageMeta.eyebrow }}</span>\n <h1 class=\"invoice-detail-page__title\">{{ invoice.invoiceNumber || invoice.id }}</h1>\n <p class=\"invoice-detail-page__description\">\n Invoice for {{ invoice.fullName || invoice.firstName + ' ' + invoice.lastName }} created\n {{ invoice.createdAt | date: 'mediumDate' }}.\n </p>\n </div>\n\n <div class=\"invoice-detail-page__hero-side\">\n <span [class]=\"statusToneClass(invoice.status)\">{{ status[invoice.status] }}</span>\n <div class=\"invoice-detail-page__total\">{{ invoice.total | price }}</div>\n <div class=\"invoice-detail-page__note\">\n @if (invoice.dueAt) {\n Due {{ invoice.dueAt | date: 'mediumDate' }}\n } @else {\n Payable on receipt\n }\n </div>\n\n <div class=\"invoice-detail-page__hero-actions\">\n @if (invoice.pdfUrl) {\n <button mat-stroked-button (click)=\"openPdf()\">Preview PDF</button>\n }\n <button mat-flat-button (click)=\"edit()\">Edit invoice</button>\n </div>\n </div>\n </section>\n\n <section class=\"invoice-detail-page__content\">\n <div class=\"invoice-detail-page__main\">\n <rolatech-invoice-user\n [firstName]=\"invoice.firstName\"\n [lastName]=\"invoice.lastName\"\n [email]=\"invoice.email\"\n [phone]=\"invoice.phone\"\n ></rolatech-invoice-user>\n\n @if (invoice.lines; as lines) {\n <rolatech-invoice-lines [lines]=\"lines\"></rolatech-invoice-lines>\n }\n </div>\n\n <div class=\"invoice-detail-page__side\">\n <rolatech-invoice-summary\n [tax]=\"invoice.vatTotal\"\n [credit]=\"invoice.holdingDepositApplied\"\n [subtotal]=\"invoice.subtotal\"\n [total]=\"invoice.total\"\n ></rolatech-invoice-summary>\n\n <article class=\"invoice-detail-page__panel\">\n <h2 class=\"invoice-detail-page__panel-title\">Invoice note</h2>\n <p class=\"invoice-detail-page__panel-copy\">{{ invoice.note || 'No note added for this invoice.' }}</p>\n </article>\n </div>\n </section>\n }\n</section>\n", styles: [":host{display:block;padding:1rem;--rt-workspace-status-issued-surface: color-mix(in srgb, var(--rt-brand-color) 14%, transparent);--rt-workspace-status-issued-color: color-mix(in srgb, var(--rt-brand-color) 82%, var(--rt-text-primary) 18%);--rt-workspace-status-paid-surface: color-mix(in srgb, var(--rt-brand-color) 16%, var(--rt-base-background, #ffffff));--rt-workspace-status-paid-color: color-mix(in srgb, var(--rt-brand-color) 58%, var(--rt-text-primary) 42%)}.invoice-detail-page{display:flex;flex-direction:column;gap:1rem}.invoice-detail-page__hero,.invoice-detail-page__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.invoice-detail-page__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.invoice-detail-page__hero-copy,.invoice-detail-page__hero-side,.invoice-detail-page__hero-actions,.invoice-detail-page__main,.invoice-detail-page__side{display:flex;flex-direction:column;gap:1rem}.invoice-detail-page__back{color:var(--rt-text-secondary);font-weight:600;text-decoration:none}.invoice-detail-page__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.invoice-detail-page__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.invoice-detail-page__description,.invoice-detail-page__note,.invoice-detail-page__panel-copy{margin:0;color:var(--rt-text-secondary);line-height:1.7}.invoice-detail-page__status{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.45rem .82rem;font-size:.8rem;font-weight:700}.invoice-detail-page__status--neutral{background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color)}.invoice-detail-page__status--issued{background:var(--rt-workspace-status-issued-surface);color:var(--rt-workspace-status-issued-color)}.invoice-detail-page__status--paid{background:var(--rt-workspace-status-paid-surface);color:var(--rt-workspace-status-paid-color)}.invoice-detail-page__status--void{background:color-mix(in srgb,var(--mat-sys-error, #b91c1c) 14%,transparent);color:var(--mat-sys-error, #b91c1c)}.invoice-detail-page__total{color:var(--rt-text-primary);font-size:clamp(1.8rem,2.4vw,2.3rem);font-weight:800}.invoice-detail-page__hero-actions{flex-wrap:wrap}.invoice-detail-page__content{display:grid;gap:1rem}.invoice-detail-page__panel{padding:1rem 1.1rem}.invoice-detail-page__panel-title{margin:0 0 .6rem;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}@media(min-width:1024px){.invoice-detail-page__hero{grid-template-columns:minmax(0,1.65fr) minmax(18rem,.85fr);align-items:start}.invoice-detail-page__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}}@media(min-width:768px){:host{padding:1.25rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: InvoiceSummary, selector: "rolatech-invoice-summary", inputs: ["tax", "credit", "subtotal", "total"] }, { kind: "component", type: InvoiceLines, selector: "rolatech-invoice-lines", inputs: ["lines"] }, { kind: "component", type: InvoiceUser, selector: "rolatech-invoice-user", inputs: ["firstName", "lastName", "email", "phone"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }] }); }
|
|
1542
1543
|
}
|
|
1543
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1544
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: AgentInvoiceDetail, decorators: [{
|
|
1544
1545
|
type: Component,
|
|
1545
1546
|
args: [{ selector: 'rolatech-agent-invoice-detail', imports: [
|
|
1546
1547
|
CommonModule,
|
|
@@ -1566,9 +1567,487 @@ const agentInvoiceRoutes = [
|
|
|
1566
1567
|
},
|
|
1567
1568
|
];
|
|
1568
1569
|
|
|
1570
|
+
class BillingProfileFacade {
|
|
1571
|
+
constructor() {
|
|
1572
|
+
this.billingProfileService = inject(BillingProfileService);
|
|
1573
|
+
this.loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
1574
|
+
this.saving = signal(false, ...(ngDevMode ? [{ debugName: "saving" }] : /* istanbul ignore next */ []));
|
|
1575
|
+
this.profile = signal(null, ...(ngDevMode ? [{ debugName: "profile" }] : /* istanbul ignore next */ []));
|
|
1576
|
+
this.error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
|
|
1577
|
+
this.saveMessage = signal(null, ...(ngDevMode ? [{ debugName: "saveMessage" }] : /* istanbul ignore next */ []));
|
|
1578
|
+
this.enabled = computed(() => this.profile()?.enabled ?? false, ...(ngDevMode ? [{ debugName: "enabled" }] : /* istanbul ignore next */ []));
|
|
1579
|
+
}
|
|
1580
|
+
load() {
|
|
1581
|
+
this.loading.set(true);
|
|
1582
|
+
this.error.set(null);
|
|
1583
|
+
this.billingProfileService
|
|
1584
|
+
.getOrCreate()
|
|
1585
|
+
.pipe(finalize(() => this.loading.set(false)))
|
|
1586
|
+
.subscribe({
|
|
1587
|
+
next: (response) => {
|
|
1588
|
+
this.profile.set(response.data);
|
|
1589
|
+
},
|
|
1590
|
+
error: (error) => {
|
|
1591
|
+
this.error.set(error?.error?.message ?? 'Failed to load billing profile.');
|
|
1592
|
+
},
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1595
|
+
save(body, done) {
|
|
1596
|
+
this.saving.set(true);
|
|
1597
|
+
this.error.set(null);
|
|
1598
|
+
this.saveMessage.set(null);
|
|
1599
|
+
this.billingProfileService
|
|
1600
|
+
.updateProfile(body)
|
|
1601
|
+
.pipe(finalize(() => this.saving.set(false)))
|
|
1602
|
+
.subscribe({
|
|
1603
|
+
next: (response) => {
|
|
1604
|
+
this.profile.set(response.data);
|
|
1605
|
+
this.saveMessage.set('Billing profile saved successfully.');
|
|
1606
|
+
done?.(response.data);
|
|
1607
|
+
},
|
|
1608
|
+
error: (error) => {
|
|
1609
|
+
this.error.set(error?.error?.message ?? 'Failed to save billing profile.');
|
|
1610
|
+
},
|
|
1611
|
+
});
|
|
1612
|
+
}
|
|
1613
|
+
setEnabled(enabled) {
|
|
1614
|
+
this.saving.set(true);
|
|
1615
|
+
this.error.set(null);
|
|
1616
|
+
this.saveMessage.set(null);
|
|
1617
|
+
this.billingProfileService
|
|
1618
|
+
.patchProfile({ enabled })
|
|
1619
|
+
.pipe(finalize(() => this.saving.set(false)))
|
|
1620
|
+
.subscribe({
|
|
1621
|
+
next: (response) => {
|
|
1622
|
+
this.profile.set(response.data);
|
|
1623
|
+
this.saveMessage.set(enabled ? 'Billing profile enabled.' : 'Billing profile disabled.');
|
|
1624
|
+
},
|
|
1625
|
+
error: (error) => {
|
|
1626
|
+
this.error.set(error?.error?.message ?? 'Failed to update billing profile status.');
|
|
1627
|
+
},
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: BillingProfileFacade, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1631
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: BillingProfileFacade, providedIn: 'root' }); }
|
|
1632
|
+
}
|
|
1633
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: BillingProfileFacade, decorators: [{
|
|
1634
|
+
type: Injectable,
|
|
1635
|
+
args: [{
|
|
1636
|
+
providedIn: 'root',
|
|
1637
|
+
}]
|
|
1638
|
+
}] });
|
|
1639
|
+
|
|
1640
|
+
function createBillingProfileForm(formBuilder) {
|
|
1641
|
+
return formBuilder.nonNullable.group({
|
|
1642
|
+
displayName: [''],
|
|
1643
|
+
companyName: [''],
|
|
1644
|
+
companyWebsite: [''],
|
|
1645
|
+
companyRegistrationNo: [''],
|
|
1646
|
+
companyVatNo: [''],
|
|
1647
|
+
companyAddressLine: [''],
|
|
1648
|
+
companyEmail: ['', [Validators.email]],
|
|
1649
|
+
companyPhone: [''],
|
|
1650
|
+
bankInstitution: [''],
|
|
1651
|
+
bankAccountName: [''],
|
|
1652
|
+
bankSortCode: [''],
|
|
1653
|
+
bankAccountNo: [''],
|
|
1654
|
+
bankSwift: [''],
|
|
1655
|
+
bankIban: [''],
|
|
1656
|
+
bankPaymentRef: [''],
|
|
1657
|
+
invoiceFooter: [''],
|
|
1658
|
+
paymentInstructions: [''],
|
|
1659
|
+
logoUrl: [''],
|
|
1660
|
+
enabled: [true],
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1663
|
+
function patchBillingProfileForm(form, value) {
|
|
1664
|
+
if (!value) {
|
|
1665
|
+
return;
|
|
1666
|
+
}
|
|
1667
|
+
form.patchValue({
|
|
1668
|
+
displayName: value.displayName ?? '',
|
|
1669
|
+
companyName: value.companyName ?? '',
|
|
1670
|
+
companyWebsite: value.companyWebsite ?? '',
|
|
1671
|
+
companyRegistrationNo: value.companyRegistrationNo ?? '',
|
|
1672
|
+
companyVatNo: value.companyVatNo ?? '',
|
|
1673
|
+
companyAddressLine: value.companyAddressLine ?? '',
|
|
1674
|
+
companyEmail: value.companyEmail ?? '',
|
|
1675
|
+
companyPhone: value.companyPhone ?? '',
|
|
1676
|
+
bankInstitution: value.bankInstitution ?? '',
|
|
1677
|
+
bankAccountName: value.bankAccountName ?? '',
|
|
1678
|
+
bankSortCode: value.bankSortCode ?? '',
|
|
1679
|
+
bankAccountNo: value.bankAccountNo ?? '',
|
|
1680
|
+
bankSwift: value.bankSwift ?? '',
|
|
1681
|
+
bankIban: value.bankIban ?? '',
|
|
1682
|
+
bankPaymentRef: value.bankPaymentRef ?? '',
|
|
1683
|
+
invoiceFooter: value.invoiceFooter ?? '',
|
|
1684
|
+
paymentInstructions: value.paymentInstructions ?? '',
|
|
1685
|
+
logoUrl: value.logoUrl ?? '',
|
|
1686
|
+
enabled: value.enabled ?? true,
|
|
1687
|
+
});
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
class BillingProfileSettingPage {
|
|
1691
|
+
constructor() {
|
|
1692
|
+
this.formBuilder = inject(FormBuilder);
|
|
1693
|
+
this.facade = inject(BillingProfileFacade);
|
|
1694
|
+
this.form = createBillingProfileForm(this.formBuilder);
|
|
1695
|
+
this.canSubmit = computed(() => !this.facade.loading() && !this.facade.saving(), ...(ngDevMode ? [{ debugName: "canSubmit" }] : /* istanbul ignore next */ []));
|
|
1696
|
+
effect(() => {
|
|
1697
|
+
const profile = this.facade.profile();
|
|
1698
|
+
patchBillingProfileForm(this.form, profile);
|
|
1699
|
+
});
|
|
1700
|
+
this.facade.load();
|
|
1701
|
+
}
|
|
1702
|
+
submit() {
|
|
1703
|
+
if (!this.canSubmit()) {
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
this.facade.save(this.form.getRawValue());
|
|
1707
|
+
}
|
|
1708
|
+
toggleEnabled(enabled) {
|
|
1709
|
+
this.facade.setEnabled(enabled);
|
|
1710
|
+
}
|
|
1711
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: BillingProfileSettingPage, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1712
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: BillingProfileSettingPage, isStandalone: true, selector: "rolatech-billing-profile-setting-page", providers: [BillingProfileFacade], ngImport: i0, template: "<section class=\"rt-platform-theme flex flex-col gap-6 bg-(--rt-base-background) p-6 text-(--rt-text-primary)\">\n <header class=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div class=\"min-w-0\">\n <div class=\"space-y-1\">\n <h1 class=\"text-2xl font-semibold tracking-tight\">Member Management</h1>\n <p class=\"text-sm text-muted-foreground\">\n Review organization members, invitation state, and current role assignments from one workspace.\n </p>\n </div>\n </div>\n </header>\n <section class=\"grid gap-4\">\n @if (facade.error()) {\n <div class=\"mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\">{{ facade.error() }}</div>\n } @if (facade.saveMessage()) {\n <div class=\"mb-4 rounded-xl border border-green-200 bg-green-50 px-4 py-3 text-sm text-green-700\">\n {{ facade.saveMessage() }}\n </div>\n }\n\n <form [formGroup]=\"form\" (ngSubmit)=\"submit()\" class=\"space-y-6\">\n <mat-card class=\"rounded-2xl\">\n <div class=\"grid gap-4 p-4 md:grid-cols-2\">\n <div class=\"md:col-span-2 flex items-center justify-between\">\n <div>\n <div class=\"text-base font-medium\">Billing Profile Status</div>\n <div class=\"text-sm text-gray-500\">Enable this profile for invoice generation and payment instructions.</div>\n </div>\n\n <mat-slide-toggle formControlName=\"enabled\" (change)=\"toggleEnabled($event.checked)\"> Enabled </mat-slide-toggle>\n </div>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>Display Name</mat-label>\n <input matInput formControlName=\"displayName\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>Logo URL</mat-label>\n <input matInput formControlName=\"logoUrl\" />\n </mat-form-field>\n </div>\n </mat-card>\n\n <mat-card class=\"rounded-2xl\">\n <div class=\"p-4\">\n <h2 class=\"mb-4 text-lg font-medium\">Company Information</h2>\n\n <div class=\"grid gap-4 md:grid-cols-2\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Company Name</mat-label>\n <input matInput formControlName=\"companyName\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>Website</mat-label>\n <input matInput formControlName=\"companyWebsite\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>Registration No</mat-label>\n <input matInput formControlName=\"companyRegistrationNo\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>VAT No</mat-label>\n <input matInput formControlName=\"companyVatNo\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" class=\"md:col-span-2\">\n <mat-label>Address</mat-label>\n <textarea matInput rows=\"3\" formControlName=\"companyAddressLine\"></textarea>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>Company Email</mat-label>\n <input matInput formControlName=\"companyEmail\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>Company Phone</mat-label>\n <input matInput formControlName=\"companyPhone\" />\n </mat-form-field>\n </div>\n </div>\n </mat-card>\n\n <mat-card class=\"rounded-2xl\">\n <div class=\"p-4\">\n <h2 class=\"mb-4 text-lg font-medium\">Bank Information</h2>\n\n <div class=\"grid gap-4 md:grid-cols-2\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Bank Institution</mat-label>\n <input matInput formControlName=\"bankInstitution\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>Account Name</mat-label>\n <input matInput formControlName=\"bankAccountName\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>Sort Code</mat-label>\n <input matInput formControlName=\"bankSortCode\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>Account No</mat-label>\n <input matInput formControlName=\"bankAccountNo\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>SWIFT</mat-label>\n <input matInput formControlName=\"bankSwift\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>IBAN</mat-label>\n <input matInput formControlName=\"bankIban\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" class=\"md:col-span-2\">\n <mat-label>Payment Reference</mat-label>\n <input matInput formControlName=\"bankPaymentRef\" />\n </mat-form-field>\n </div>\n </div>\n </mat-card>\n\n <mat-card class=\"rounded-2xl\">\n <div class=\"p-4\">\n <h2 class=\"mb-4 text-lg font-medium\">Invoice & Payment Content</h2>\n\n <div class=\"grid gap-4\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Invoice Footer</mat-label>\n <textarea matInput rows=\"4\" formControlName=\"invoiceFooter\"></textarea>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>Payment Instructions</mat-label>\n <textarea matInput rows=\"5\" formControlName=\"paymentInstructions\"></textarea>\n </mat-form-field>\n </div>\n </div>\n </mat-card>\n\n <div class=\"flex justify-end\">\n <button mat-flat-button color=\"primary\" type=\"submit\" [disabled]=\"facade.saving() || facade.loading()\">\n {{ facade.saving() ? 'Saving...' : 'Save Billing Profile' }}\n </button>\n </div>\n </form>\n </section>\n</section>\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i5$1.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i8.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1713
|
+
}
|
|
1714
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: BillingProfileSettingPage, decorators: [{
|
|
1715
|
+
type: Component,
|
|
1716
|
+
args: [{ selector: 'rolatech-billing-profile-setting-page', imports: [
|
|
1717
|
+
CommonModule,
|
|
1718
|
+
ReactiveFormsModule,
|
|
1719
|
+
MatButtonModule,
|
|
1720
|
+
MatFormFieldModule,
|
|
1721
|
+
MatInputModule,
|
|
1722
|
+
MatSlideToggleModule,
|
|
1723
|
+
MatCardModule,
|
|
1724
|
+
], providers: [BillingProfileFacade], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section class=\"rt-platform-theme flex flex-col gap-6 bg-(--rt-base-background) p-6 text-(--rt-text-primary)\">\n <header class=\"flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between\">\n <div class=\"min-w-0\">\n <div class=\"space-y-1\">\n <h1 class=\"text-2xl font-semibold tracking-tight\">Member Management</h1>\n <p class=\"text-sm text-muted-foreground\">\n Review organization members, invitation state, and current role assignments from one workspace.\n </p>\n </div>\n </div>\n </header>\n <section class=\"grid gap-4\">\n @if (facade.error()) {\n <div class=\"mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700\">{{ facade.error() }}</div>\n } @if (facade.saveMessage()) {\n <div class=\"mb-4 rounded-xl border border-green-200 bg-green-50 px-4 py-3 text-sm text-green-700\">\n {{ facade.saveMessage() }}\n </div>\n }\n\n <form [formGroup]=\"form\" (ngSubmit)=\"submit()\" class=\"space-y-6\">\n <mat-card class=\"rounded-2xl\">\n <div class=\"grid gap-4 p-4 md:grid-cols-2\">\n <div class=\"md:col-span-2 flex items-center justify-between\">\n <div>\n <div class=\"text-base font-medium\">Billing Profile Status</div>\n <div class=\"text-sm text-gray-500\">Enable this profile for invoice generation and payment instructions.</div>\n </div>\n\n <mat-slide-toggle formControlName=\"enabled\" (change)=\"toggleEnabled($event.checked)\"> Enabled </mat-slide-toggle>\n </div>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>Display Name</mat-label>\n <input matInput formControlName=\"displayName\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>Logo URL</mat-label>\n <input matInput formControlName=\"logoUrl\" />\n </mat-form-field>\n </div>\n </mat-card>\n\n <mat-card class=\"rounded-2xl\">\n <div class=\"p-4\">\n <h2 class=\"mb-4 text-lg font-medium\">Company Information</h2>\n\n <div class=\"grid gap-4 md:grid-cols-2\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Company Name</mat-label>\n <input matInput formControlName=\"companyName\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>Website</mat-label>\n <input matInput formControlName=\"companyWebsite\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>Registration No</mat-label>\n <input matInput formControlName=\"companyRegistrationNo\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>VAT No</mat-label>\n <input matInput formControlName=\"companyVatNo\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" class=\"md:col-span-2\">\n <mat-label>Address</mat-label>\n <textarea matInput rows=\"3\" formControlName=\"companyAddressLine\"></textarea>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>Company Email</mat-label>\n <input matInput formControlName=\"companyEmail\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>Company Phone</mat-label>\n <input matInput formControlName=\"companyPhone\" />\n </mat-form-field>\n </div>\n </div>\n </mat-card>\n\n <mat-card class=\"rounded-2xl\">\n <div class=\"p-4\">\n <h2 class=\"mb-4 text-lg font-medium\">Bank Information</h2>\n\n <div class=\"grid gap-4 md:grid-cols-2\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Bank Institution</mat-label>\n <input matInput formControlName=\"bankInstitution\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>Account Name</mat-label>\n <input matInput formControlName=\"bankAccountName\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>Sort Code</mat-label>\n <input matInput formControlName=\"bankSortCode\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>Account No</mat-label>\n <input matInput formControlName=\"bankAccountNo\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>SWIFT</mat-label>\n <input matInput formControlName=\"bankSwift\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>IBAN</mat-label>\n <input matInput formControlName=\"bankIban\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\" class=\"md:col-span-2\">\n <mat-label>Payment Reference</mat-label>\n <input matInput formControlName=\"bankPaymentRef\" />\n </mat-form-field>\n </div>\n </div>\n </mat-card>\n\n <mat-card class=\"rounded-2xl\">\n <div class=\"p-4\">\n <h2 class=\"mb-4 text-lg font-medium\">Invoice & Payment Content</h2>\n\n <div class=\"grid gap-4\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Invoice Footer</mat-label>\n <textarea matInput rows=\"4\" formControlName=\"invoiceFooter\"></textarea>\n </mat-form-field>\n\n <mat-form-field appearance=\"outline\">\n <mat-label>Payment Instructions</mat-label>\n <textarea matInput rows=\"5\" formControlName=\"paymentInstructions\"></textarea>\n </mat-form-field>\n </div>\n </div>\n </mat-card>\n\n <div class=\"flex justify-end\">\n <button mat-flat-button color=\"primary\" type=\"submit\" [disabled]=\"facade.saving() || facade.loading()\">\n {{ facade.saving() ? 'Saving...' : 'Save Billing Profile' }}\n </button>\n </div>\n </form>\n </section>\n</section>\n", styles: [":host{display:block}\n"] }]
|
|
1725
|
+
}], ctorParameters: () => [] });
|
|
1726
|
+
|
|
1727
|
+
const BILLING_PROFILE_ROUTES = [
|
|
1728
|
+
{
|
|
1729
|
+
path: '',
|
|
1730
|
+
component: BillingProfileSettingPage,
|
|
1731
|
+
},
|
|
1732
|
+
];
|
|
1733
|
+
|
|
1734
|
+
class MarketInvoiceIndex extends BaseComponent {
|
|
1735
|
+
constructor() {
|
|
1736
|
+
super(...arguments);
|
|
1737
|
+
this.invoiceService = inject(InvoiceService);
|
|
1738
|
+
this.filterOptions = {
|
|
1739
|
+
type: '',
|
|
1740
|
+
status: '',
|
|
1741
|
+
};
|
|
1742
|
+
this.invoiceType = InvoiceType;
|
|
1743
|
+
this.invoiceStatus = InvoiceStatus;
|
|
1744
|
+
this.select = 0;
|
|
1745
|
+
this.loading = false;
|
|
1746
|
+
this.invoices = signal([], ...(ngDevMode ? [{ debugName: "invoices" }] : /* istanbul ignore next */ []));
|
|
1747
|
+
this.length = 100;
|
|
1748
|
+
this.pageSize = 15;
|
|
1749
|
+
this.pageIndex = signal(0, ...(ngDevMode ? [{ debugName: "pageIndex" }] : /* istanbul ignore next */ []));
|
|
1750
|
+
this.pageSizeOptions = [5, 10, 25, 100];
|
|
1751
|
+
this.links = [
|
|
1752
|
+
{
|
|
1753
|
+
name: 'All',
|
|
1754
|
+
status: '',
|
|
1755
|
+
},
|
|
1756
|
+
{
|
|
1757
|
+
name: 'Created',
|
|
1758
|
+
status: 'created',
|
|
1759
|
+
},
|
|
1760
|
+
{
|
|
1761
|
+
name: 'Issued',
|
|
1762
|
+
status: 'issued',
|
|
1763
|
+
},
|
|
1764
|
+
{
|
|
1765
|
+
name: 'Paid',
|
|
1766
|
+
status: 'paid',
|
|
1767
|
+
},
|
|
1768
|
+
];
|
|
1769
|
+
this.pageMeta = {
|
|
1770
|
+
baseLink: this.readRouteData('invoiceBaseLink', '/invoices'),
|
|
1771
|
+
eyebrow: this.readRouteData('invoiceEyebrow', 'Agent workspace'),
|
|
1772
|
+
title: this.readRouteData('invoiceCollectionTitle', 'Listing invoices'),
|
|
1773
|
+
description: this.readRouteData('invoiceCollectionDescription', 'Monitor invoices tied to your listings, with billing status and payment movement visible at a glance.'),
|
|
1774
|
+
};
|
|
1775
|
+
this.stats = computed(() => {
|
|
1776
|
+
const invoices = this.invoices();
|
|
1777
|
+
return [
|
|
1778
|
+
{ label: 'Invoices', value: this.length || invoices.length, hint: 'Billing records in your agent workspace' },
|
|
1779
|
+
{
|
|
1780
|
+
label: 'Created',
|
|
1781
|
+
value: invoices.filter((item) => item.status === InvoiceStatus.CREATED).length,
|
|
1782
|
+
hint: 'Prepared but not yet settled',
|
|
1783
|
+
},
|
|
1784
|
+
{
|
|
1785
|
+
label: 'Issued',
|
|
1786
|
+
value: invoices.filter((item) => item.status === InvoiceStatus.ISSUED || item.status === InvoiceStatus.SENT).length,
|
|
1787
|
+
hint: 'Awaiting payment',
|
|
1788
|
+
},
|
|
1789
|
+
{
|
|
1790
|
+
label: 'Paid',
|
|
1791
|
+
value: invoices.filter((item) => item.status === InvoiceStatus.PAID).length,
|
|
1792
|
+
hint: 'Completed payments',
|
|
1793
|
+
},
|
|
1794
|
+
];
|
|
1795
|
+
}, ...(ngDevMode ? [{ debugName: "stats" }] : /* istanbul ignore next */ []));
|
|
1796
|
+
}
|
|
1797
|
+
find() {
|
|
1798
|
+
this.router.navigate([], {
|
|
1799
|
+
queryParams: {
|
|
1800
|
+
type: this.filterOptions.type || null,
|
|
1801
|
+
status: this.filterOptions.status || null,
|
|
1802
|
+
page: 1,
|
|
1803
|
+
},
|
|
1804
|
+
queryParamsHandling: 'merge',
|
|
1805
|
+
replaceUrl: true,
|
|
1806
|
+
});
|
|
1807
|
+
}
|
|
1808
|
+
ngOnInit() {
|
|
1809
|
+
const sub = this.route.queryParamMap
|
|
1810
|
+
.pipe(map((p) => {
|
|
1811
|
+
const page = p.get('page') ? Number(p.get('page')) : 1;
|
|
1812
|
+
this.pageIndex.set(Math.max(page - 1, 0));
|
|
1813
|
+
const status = p.get('status') || undefined;
|
|
1814
|
+
const type = p.get('type') || undefined;
|
|
1815
|
+
this.filterOptions = {
|
|
1816
|
+
type: type || '',
|
|
1817
|
+
status: status || '',
|
|
1818
|
+
};
|
|
1819
|
+
const filters = [];
|
|
1820
|
+
if (status) {
|
|
1821
|
+
this.select = this.links.findIndex((item) => item.status === status);
|
|
1822
|
+
filters.push(`status:${status.toUpperCase()}`);
|
|
1823
|
+
}
|
|
1824
|
+
else {
|
|
1825
|
+
this.select = 0;
|
|
1826
|
+
}
|
|
1827
|
+
if (type) {
|
|
1828
|
+
filters.push(`type:${type}`);
|
|
1829
|
+
}
|
|
1830
|
+
return {
|
|
1831
|
+
page,
|
|
1832
|
+
filter: filters.join(','),
|
|
1833
|
+
limit: p.get('limit') ? Number(p.get('limit')) : 15,
|
|
1834
|
+
sort: p.get('sort') || undefined,
|
|
1835
|
+
};
|
|
1836
|
+
}),
|
|
1837
|
+
// Cheap deep compare via JSON to avoid spam calls when nothing changed
|
|
1838
|
+
map((o) => JSON.stringify(o)), distinctUntilChanged(), map((s) => JSON.parse(s)), switchMap((params) => {
|
|
1839
|
+
this.loading = true;
|
|
1840
|
+
return this.invoiceService.findByManager(params).pipe(finalize(() => (this.loading = false)));
|
|
1841
|
+
}))
|
|
1842
|
+
.subscribe({
|
|
1843
|
+
next: (res) => {
|
|
1844
|
+
this.invoices.set(res.data ?? []);
|
|
1845
|
+
this.meta = res.meta;
|
|
1846
|
+
this.length = res.meta?.pagination?.count ?? res.data?.length ?? 0;
|
|
1847
|
+
},
|
|
1848
|
+
error: () => {
|
|
1849
|
+
this.invoices.set([]);
|
|
1850
|
+
this.length = 0;
|
|
1851
|
+
},
|
|
1852
|
+
});
|
|
1853
|
+
// // auto-unsubscribe on destroy
|
|
1854
|
+
this.destroyRef.onDestroy(() => sub.unsubscribe());
|
|
1855
|
+
}
|
|
1856
|
+
onPage(e) {
|
|
1857
|
+
this.router.navigate([], {
|
|
1858
|
+
queryParams: { page: e.pageIndex + 1, limit: e.pageSize },
|
|
1859
|
+
queryParamsHandling: 'merge',
|
|
1860
|
+
replaceUrl: true, // optional: avoid stacking history on every page click
|
|
1861
|
+
});
|
|
1862
|
+
}
|
|
1863
|
+
resetFilter() {
|
|
1864
|
+
this.filterOptions = {
|
|
1865
|
+
type: '',
|
|
1866
|
+
status: '',
|
|
1867
|
+
};
|
|
1868
|
+
this.router.navigate([], {
|
|
1869
|
+
queryParams: {
|
|
1870
|
+
type: null,
|
|
1871
|
+
status: null,
|
|
1872
|
+
page: 1,
|
|
1873
|
+
},
|
|
1874
|
+
queryParamsHandling: 'merge',
|
|
1875
|
+
replaceUrl: true,
|
|
1876
|
+
});
|
|
1877
|
+
}
|
|
1878
|
+
statusCompareFn(t1, t2) {
|
|
1879
|
+
return t1 === t2;
|
|
1880
|
+
}
|
|
1881
|
+
readRouteData(key, fallback) {
|
|
1882
|
+
for (const snapshot of [...this.route.snapshot.pathFromRoot].reverse()) {
|
|
1883
|
+
const value = snapshot.data?.[key];
|
|
1884
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
1885
|
+
return value;
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
return fallback;
|
|
1889
|
+
}
|
|
1890
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: MarketInvoiceIndex, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
1891
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: MarketInvoiceIndex, isStandalone: true, selector: "rolatech-agent-invoice-index", usesInheritance: true, ngImport: i0, template: "<section class=\"invoice-index-page\">\n <section class=\"invoice-index-page__hero\">\n <div class=\"invoice-index-page__copy\">\n <span class=\"invoice-index-page__eyebrow\">{{ pageMeta.eyebrow }}</span>\n <h1 class=\"invoice-index-page__title\">{{ pageMeta.title }}</h1>\n <p class=\"invoice-index-page__description\">{{ pageMeta.description }}</p>\n </div>\n\n <div class=\"invoice-index-page__stats\">\n @for (stat of stats(); track stat.label) {\n <article class=\"invoice-index-page__stat\">\n <span class=\"invoice-index-page__stat-value\">{{ stat.value }}</span>\n <span class=\"invoice-index-page__stat-label\">{{ stat.label }}</span>\n <span class=\"invoice-index-page__stat-hint\">{{ stat.hint }}</span>\n </article>\n }\n </div>\n </section>\n\n <section class=\"invoice-index-page__filters\">\n <nav class=\"invoice-index-page__status-nav\" aria-label=\"Invoice status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"invoice-index-page__status-link\"\n [class.invoice-index-page__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <div class=\"invoice-index-page__filter-grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label>Type</mat-label>\n <mat-select name=\"type\" [(ngModel)]=\"filterOptions.type\">\n @for (type of invoiceType | keyvalue; track type.key) {\n <mat-option [value]=\"type.key\">{{ type.value }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Status</mat-label>\n <mat-select [compareWith]=\"statusCompareFn\" [(ngModel)]=\"filterOptions.status\">\n @for (status of invoiceStatus | keyvalue; track status.key) {\n <mat-option [value]=\"status.key\">{{ status.value }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <div class=\"invoice-index-page__filter-actions\">\n <button mat-flat-button (click)=\"find()\">Apply filters</button>\n <button mat-stroked-button (click)=\"resetFilter()\">Reset</button>\n </div>\n </div>\n </section>\n\n <section class=\"invoice-index-page__list\">\n @if (loading) { @for (dummy of [0, 1, 2, 3]; track dummy) {\n <rolatech-invoice-item-skeleton></rolatech-invoice-item-skeleton>\n } } @else if (invoices().length > 0) { @for (item of invoices(); track item.id) {\n <rolatech-invoice-item class=\"cursor-pointer\" [routerLink]=\"['./', item.id]\" [invoice]=\"item\"></rolatech-invoice-item>\n } } @else {\n <div class=\"invoice-index-page__empty\">\n <h3>No invoices yet</h3>\n <p>The invoices for this workspace will appear here once billing is created.</p>\n </div>\n }\n\n <mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n</section>\n", styles: [".collapsed{max-height:0;overflow:hidden;transition:max-height .5s cubic-bezier(.4,0,.2,1)}.expanded{max-height:1000px}:host{display:block;padding:1rem}.invoice-index-page{display:flex;flex-direction:column;gap:1rem}.invoice-index-page__hero,.invoice-index-page__filters,.invoice-index-page__list{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.invoice-index-page__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.invoice-index-page__copy{display:flex;flex-direction:column;gap:.75rem}.invoice-index-page__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.invoice-index-page__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.invoice-index-page__description{margin:0;color:var(--rt-text-secondary);line-height:1.7}.invoice-index-page__stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.invoice-index-page__stat{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.invoice-index-page__stat-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.invoice-index-page__stat-label,.invoice-index-page__stat-hint{color:var(--rt-text-secondary);font-size:.84rem}.invoice-index-page__filters,.invoice-index-page__list{display:flex;flex-direction:column;gap:1rem;padding:1rem 1.1rem}.invoice-index-page__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.invoice-index-page__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none}.invoice-index-page__status-link:hover,.invoice-index-page__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.invoice-index-page__filter-grid{display:grid;gap:.85rem}.invoice-index-page__filter-grid mat-form-field{width:100%}.invoice-index-page__filter-actions{display:flex;flex-wrap:wrap;gap:.75rem}.invoice-index-page__empty{display:flex;flex-direction:column;justify-content:center;min-height:10rem;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.invoice-index-page__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.invoice-index-page__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:900px){.invoice-index-page__hero{grid-template-columns:minmax(0,1.6fr) minmax(19rem,.95fr);align-items:start}.invoice-index-page__filter-grid{grid-template-columns:repeat(3,minmax(0,1fr));align-items:start}}@media(min-width:768px){:host{padding:1.25rem}}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: InvoiceItem, selector: "rolatech-invoice-item", inputs: ["invoice"], outputs: ["download"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i6.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: InvoiceItemSkeleton, selector: "rolatech-invoice-item-skeleton" }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }] }); }
|
|
1892
|
+
}
|
|
1893
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: MarketInvoiceIndex, decorators: [{
|
|
1894
|
+
type: Component,
|
|
1895
|
+
args: [{ selector: 'rolatech-agent-invoice-index', imports: [
|
|
1896
|
+
RouterModule,
|
|
1897
|
+
InvoiceItem,
|
|
1898
|
+
MatButtonModule,
|
|
1899
|
+
FormsModule,
|
|
1900
|
+
MatFormFieldModule,
|
|
1901
|
+
MatSelectModule,
|
|
1902
|
+
KeyValuePipe,
|
|
1903
|
+
MatPaginatorModule,
|
|
1904
|
+
InvoiceItemSkeleton,
|
|
1905
|
+
], template: "<section class=\"invoice-index-page\">\n <section class=\"invoice-index-page__hero\">\n <div class=\"invoice-index-page__copy\">\n <span class=\"invoice-index-page__eyebrow\">{{ pageMeta.eyebrow }}</span>\n <h1 class=\"invoice-index-page__title\">{{ pageMeta.title }}</h1>\n <p class=\"invoice-index-page__description\">{{ pageMeta.description }}</p>\n </div>\n\n <div class=\"invoice-index-page__stats\">\n @for (stat of stats(); track stat.label) {\n <article class=\"invoice-index-page__stat\">\n <span class=\"invoice-index-page__stat-value\">{{ stat.value }}</span>\n <span class=\"invoice-index-page__stat-label\">{{ stat.label }}</span>\n <span class=\"invoice-index-page__stat-hint\">{{ stat.hint }}</span>\n </article>\n }\n </div>\n </section>\n\n <section class=\"invoice-index-page__filters\">\n <nav class=\"invoice-index-page__status-nav\" aria-label=\"Invoice status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"invoice-index-page__status-link\"\n [class.invoice-index-page__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <div class=\"invoice-index-page__filter-grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label>Type</mat-label>\n <mat-select name=\"type\" [(ngModel)]=\"filterOptions.type\">\n @for (type of invoiceType | keyvalue; track type.key) {\n <mat-option [value]=\"type.key\">{{ type.value }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Status</mat-label>\n <mat-select [compareWith]=\"statusCompareFn\" [(ngModel)]=\"filterOptions.status\">\n @for (status of invoiceStatus | keyvalue; track status.key) {\n <mat-option [value]=\"status.key\">{{ status.value }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <div class=\"invoice-index-page__filter-actions\">\n <button mat-flat-button (click)=\"find()\">Apply filters</button>\n <button mat-stroked-button (click)=\"resetFilter()\">Reset</button>\n </div>\n </div>\n </section>\n\n <section class=\"invoice-index-page__list\">\n @if (loading) { @for (dummy of [0, 1, 2, 3]; track dummy) {\n <rolatech-invoice-item-skeleton></rolatech-invoice-item-skeleton>\n } } @else if (invoices().length > 0) { @for (item of invoices(); track item.id) {\n <rolatech-invoice-item class=\"cursor-pointer\" [routerLink]=\"['./', item.id]\" [invoice]=\"item\"></rolatech-invoice-item>\n } } @else {\n <div class=\"invoice-index-page__empty\">\n <h3>No invoices yet</h3>\n <p>The invoices for this workspace will appear here once billing is created.</p>\n </div>\n }\n\n <mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n</section>\n", styles: [".collapsed{max-height:0;overflow:hidden;transition:max-height .5s cubic-bezier(.4,0,.2,1)}.expanded{max-height:1000px}:host{display:block;padding:1rem}.invoice-index-page{display:flex;flex-direction:column;gap:1rem}.invoice-index-page__hero,.invoice-index-page__filters,.invoice-index-page__list{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.invoice-index-page__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.invoice-index-page__copy{display:flex;flex-direction:column;gap:.75rem}.invoice-index-page__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.invoice-index-page__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.invoice-index-page__description{margin:0;color:var(--rt-text-secondary);line-height:1.7}.invoice-index-page__stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.invoice-index-page__stat{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.invoice-index-page__stat-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.invoice-index-page__stat-label,.invoice-index-page__stat-hint{color:var(--rt-text-secondary);font-size:.84rem}.invoice-index-page__filters,.invoice-index-page__list{display:flex;flex-direction:column;gap:1rem;padding:1rem 1.1rem}.invoice-index-page__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.invoice-index-page__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none}.invoice-index-page__status-link:hover,.invoice-index-page__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.invoice-index-page__filter-grid{display:grid;gap:.85rem}.invoice-index-page__filter-grid mat-form-field{width:100%}.invoice-index-page__filter-actions{display:flex;flex-wrap:wrap;gap:.75rem}.invoice-index-page__empty{display:flex;flex-direction:column;justify-content:center;min-height:10rem;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.invoice-index-page__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.invoice-index-page__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:900px){.invoice-index-page__hero{grid-template-columns:minmax(0,1.6fr) minmax(19rem,.95fr);align-items:start}.invoice-index-page__filter-grid{grid-template-columns:repeat(3,minmax(0,1fr));align-items:start}}@media(min-width:768px){:host{padding:1.25rem}}\n"] }]
|
|
1906
|
+
}] });
|
|
1907
|
+
|
|
1908
|
+
class MarketInvoiceDetail extends BaseComponent {
|
|
1909
|
+
constructor() {
|
|
1910
|
+
super(...arguments);
|
|
1911
|
+
this.invoiceService = inject(InvoiceService);
|
|
1912
|
+
this.paymentService = inject(PaymentService);
|
|
1913
|
+
this.invoice = signal(null, ...(ngDevMode ? [{ debugName: "invoice" }] : /* istanbul ignore next */ []));
|
|
1914
|
+
this.status = InvoiceStatus;
|
|
1915
|
+
this.paying = false;
|
|
1916
|
+
this.loading = signal(true, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
1917
|
+
this.pageMeta = {
|
|
1918
|
+
baseLink: this.readRouteData('invoiceBaseLink', '/invoices'),
|
|
1919
|
+
collectionTitle: this.readRouteData('invoiceCollectionTitle', 'Invoices'),
|
|
1920
|
+
eyebrow: this.readRouteData('invoiceEyebrow', 'Billing workspace'),
|
|
1921
|
+
};
|
|
1922
|
+
this.breadcrumbs = computed(() => [
|
|
1923
|
+
{ label: 'Invoices', link: '/properties/invoices' },
|
|
1924
|
+
{ label: 'Invoice detail' },
|
|
1925
|
+
], ...(ngDevMode ? [{ debugName: "breadcrumbs" }] : /* istanbul ignore next */ []));
|
|
1926
|
+
}
|
|
1927
|
+
ngOnInit() {
|
|
1928
|
+
this.route.params.subscribe((params) => {
|
|
1929
|
+
const id = params['id'];
|
|
1930
|
+
this.get(id);
|
|
1931
|
+
});
|
|
1932
|
+
this.route.queryParams.subscribe(async (params) => {
|
|
1933
|
+
const sessionId = params['session_id'];
|
|
1934
|
+
if (sessionId) {
|
|
1935
|
+
// this.invoiceService.checkOfferPaymentStatus(this.id, sessionId).subscribe({
|
|
1936
|
+
// next: (res) => {
|
|
1937
|
+
// if (res.status === 'paid') {
|
|
1938
|
+
// this.invoice.update((state) => {
|
|
1939
|
+
// if (!state) return state;
|
|
1940
|
+
// state.status = InvoiceStatus.PAID;
|
|
1941
|
+
// return state;
|
|
1942
|
+
// });
|
|
1943
|
+
// }
|
|
1944
|
+
// },
|
|
1945
|
+
// });
|
|
1946
|
+
}
|
|
1947
|
+
});
|
|
1948
|
+
}
|
|
1949
|
+
get(id) {
|
|
1950
|
+
this.loading.set(true);
|
|
1951
|
+
this.invoiceService.getInvoice(id).subscribe({
|
|
1952
|
+
next: (res) => {
|
|
1953
|
+
this.invoice.set(res.data);
|
|
1954
|
+
this.loading.set(false);
|
|
1955
|
+
},
|
|
1956
|
+
error: () => {
|
|
1957
|
+
this.loading.set(false);
|
|
1958
|
+
},
|
|
1959
|
+
});
|
|
1960
|
+
}
|
|
1961
|
+
pay() {
|
|
1962
|
+
this.paying = true;
|
|
1963
|
+
const data = {
|
|
1964
|
+
businessType: 'INVOICE',
|
|
1965
|
+
businessId: this.id,
|
|
1966
|
+
provider: 'STRIPE',
|
|
1967
|
+
method: 'STRIPE_CHECKOUT',
|
|
1968
|
+
};
|
|
1969
|
+
this.paymentService
|
|
1970
|
+
.createPaymentIntent(data)
|
|
1971
|
+
.pipe(switchMap((res) => {
|
|
1972
|
+
const intent = {
|
|
1973
|
+
intentId: res.data.id,
|
|
1974
|
+
provider: 'STRIPE',
|
|
1975
|
+
method: 'STRIPE_CHECKOUT',
|
|
1976
|
+
successUrl: window.location.href + '?session_id={CHECKOUT_SESSION_ID}',
|
|
1977
|
+
cancelUrl: window.location.href,
|
|
1978
|
+
};
|
|
1979
|
+
return this.paymentService.createPayment(intent);
|
|
1980
|
+
}))
|
|
1981
|
+
.subscribe({
|
|
1982
|
+
next: (res) => {
|
|
1983
|
+
this.paying = false;
|
|
1984
|
+
const { checkoutUrl } = res.data;
|
|
1985
|
+
window.open(checkoutUrl, '_blank');
|
|
1986
|
+
},
|
|
1987
|
+
error: (error) => {
|
|
1988
|
+
this.paying = false;
|
|
1989
|
+
this.snackBarService.open(error.message);
|
|
1990
|
+
},
|
|
1991
|
+
});
|
|
1992
|
+
}
|
|
1993
|
+
statusToneClass(status) {
|
|
1994
|
+
switch (status) {
|
|
1995
|
+
case InvoiceStatus.PAID:
|
|
1996
|
+
return 'invoice-detail-page__status invoice-detail-page__status--paid';
|
|
1997
|
+
case InvoiceStatus.ISSUED:
|
|
1998
|
+
case InvoiceStatus.SENT:
|
|
1999
|
+
case InvoiceStatus.PARTIALLY_PAID:
|
|
2000
|
+
return 'invoice-detail-page__status invoice-detail-page__status--issued';
|
|
2001
|
+
case InvoiceStatus.VOID:
|
|
2002
|
+
return 'invoice-detail-page__status invoice-detail-page__status--void';
|
|
2003
|
+
default:
|
|
2004
|
+
return 'invoice-detail-page__status invoice-detail-page__status--neutral';
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
readRouteData(key, fallback) {
|
|
2008
|
+
for (const snapshot of [...this.route.snapshot.pathFromRoot].reverse()) {
|
|
2009
|
+
const value = snapshot.data?.[key];
|
|
2010
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
2011
|
+
return value;
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
return fallback;
|
|
2015
|
+
}
|
|
2016
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: MarketInvoiceDetail, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
2017
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: MarketInvoiceDetail, isStandalone: true, selector: "rolatech-market-invoice-detail", usesInheritance: true, ngImport: i0, template: "<section class=\"invoice-detail-page\">\n <rolatech-breadcrumb [items]=\"breadcrumbs()\"></rolatech-breadcrumb>\n @if (loading()) {\n <section class=\"invoice-detail-page__hero invoice-detail-page__hero--loading\">\n <div class=\"invoice-detail-page__hero-copy\">\n <span class=\"invoice-detail-page__eyebrow\">{{ pageMeta.eyebrow }}</span>\n <h1 class=\"invoice-detail-page__title\">Loading invoice...</h1>\n </div>\n </section>\n\n <section class=\"invoice-detail-page__content\">\n <div class=\"invoice-detail-page__main\">\n <rolatech-invoice-user-skeleton></rolatech-invoice-user-skeleton>\n <rolatech-invoice-lines-skeleton></rolatech-invoice-lines-skeleton>\n </div>\n <div class=\"invoice-detail-page__side\">\n <rolatech-invoice-summary-skeleton></rolatech-invoice-summary-skeleton>\n </div>\n </section>\n } @else if (invoice(); as invoice) {\n <section class=\"invoice-detail-page__hero\">\n <div class=\"invoice-detail-page__hero-copy\">\n <span class=\"invoice-detail-page__eyebrow\">{{ pageMeta.eyebrow }}</span>\n <h1 class=\"invoice-detail-page__title\">Invoice detail</h1>\n <h1 class=\"invoice-detail-page__title\"></h1>\n <p class=\"invoice-detail-page__description\">#{{ invoice.invoiceNumber || invoice.id }}</p>\n <p class=\"invoice-detail-page__description\">\n Invoice for {{ invoice.fullName || invoice.firstName + ' ' + invoice.lastName }} created {{ invoice.createdAt | date:\n 'mediumDate' }}.\n </p>\n </div>\n\n <div class=\"invoice-detail-page__hero-side\">\n <span [class]=\"statusToneClass(invoice.status)\">{{ status[invoice.status] }}</span>\n <div class=\"invoice-detail-page__total\">{{ invoice.total | price }}</div>\n <div class=\"invoice-detail-page__note\">\n @if (invoice.dueAt) { Due {{ invoice.dueAt | date: 'mediumDate' }} } @else { Payable on receipt }\n </div>\n\n @if (invoice.status.toString() === 'ISSUED' || invoice.status.toString() === 'SENT') {\n <button mat-flat-button [disabled]=\"paying\" class=\"invoice-detail-page__cta\" (click)=\"pay()\">\n {{ paying ? 'Processing payment...' : 'Pay invoice' }}\n </button>\n }\n </div>\n </section>\n\n <section class=\"invoice-detail-page__content\">\n <div class=\"invoice-detail-page__main\">\n <rolatech-invoice-user\n [firstName]=\"invoice.firstName\"\n [lastName]=\"invoice.lastName\"\n [email]=\"invoice.email\"\n [phone]=\"invoice.phone\"\n ></rolatech-invoice-user>\n\n @if (invoice.lines; as lines) {\n <rolatech-invoice-lines [lines]=\"lines\"></rolatech-invoice-lines>\n }\n </div>\n\n <div class=\"invoice-detail-page__side\">\n <rolatech-invoice-summary\n [tax]=\"invoice.vatTotal\"\n [credit]=\"invoice.holdingDepositApplied\"\n [subtotal]=\"invoice.subtotal\"\n [total]=\"invoice.total\"\n ></rolatech-invoice-summary>\n\n <article class=\"invoice-detail-page__panel\">\n <h2 class=\"invoice-detail-page__panel-title\">Invoice note</h2>\n <p class=\"invoice-detail-page__panel-copy\">{{ invoice.note || 'No note added for this invoice.' }}</p>\n\n @if (invoice.status.toString() === 'CREATED' || invoice.status.toString() === 'PAID') {\n <div class=\"invoice-detail-page__panel-row\">\n <span>Payment method</span>\n <strong>Stripe</strong>\n </div>\n }\n </article>\n </div>\n </section>\n }\n</section>\n", styles: [":host{display:block;padding:1rem;--rt-workspace-status-issued-surface: color-mix(in srgb, var(--rt-brand-color) 14%, transparent);--rt-workspace-status-issued-color: color-mix(in srgb, var(--rt-brand-color) 82%, var(--rt-text-primary) 18%);--rt-workspace-status-paid-surface: color-mix(in srgb, var(--rt-brand-color) 16%, var(--rt-base-background, #ffffff));--rt-workspace-status-paid-color: color-mix(in srgb, var(--rt-brand-color) 58%, var(--rt-text-primary) 42%)}.invoice-detail-page{display:flex;flex-direction:column;gap:1rem}.invoice-detail-page__hero,.invoice-detail-page__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.invoice-detail-page__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.invoice-detail-page__hero-copy,.invoice-detail-page__hero-side,.invoice-detail-page__main,.invoice-detail-page__side{display:flex;flex-direction:column;gap:1rem}.invoice-detail-page__back{color:var(--rt-text-secondary);font-weight:600;text-decoration:none}.invoice-detail-page__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.invoice-detail-page__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.invoice-detail-page__description,.invoice-detail-page__note,.invoice-detail-page__panel-copy{margin:0;color:var(--rt-text-secondary);line-height:1.7}.invoice-detail-page__status{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.45rem .82rem;font-size:.8rem;font-weight:700}.invoice-detail-page__status--neutral{background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color)}.invoice-detail-page__status--issued{background:var(--rt-workspace-status-issued-surface);color:var(--rt-workspace-status-issued-color)}.invoice-detail-page__status--paid{background:var(--rt-workspace-status-paid-surface);color:var(--rt-workspace-status-paid-color)}.invoice-detail-page__status--void{background:color-mix(in srgb,var(--mat-sys-error, #b91c1c) 14%,transparent);color:var(--mat-sys-error, #b91c1c)}.invoice-detail-page__total{color:var(--rt-text-primary);font-size:clamp(1.8rem,2.4vw,2.3rem);font-weight:800}.invoice-detail-page__cta{min-height:3rem;border-radius:9999px}.invoice-detail-page__content{display:grid;gap:1rem}.invoice-detail-page__panel{padding:1rem 1.1rem}.invoice-detail-page__panel-title{margin:0 0 .6rem;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.invoice-detail-page__panel-row{display:flex;justify-content:space-between;gap:1rem;margin-top:1rem;color:var(--rt-text-secondary)}.invoice-detail-page__panel-row strong{color:var(--rt-text-primary)}@media(min-width:1024px){.invoice-detail-page__hero{grid-template-columns:minmax(0,1.65fr) minmax(18rem,.85fr);align-items:start}.invoice-detail-page__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}}@media(min-width:768px){:host{padding:1.25rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: InvoiceUser, selector: "rolatech-invoice-user", inputs: ["firstName", "lastName", "email", "phone"] }, { kind: "component", type: InvoiceLines, selector: "rolatech-invoice-lines", inputs: ["lines"] }, { kind: "component", type: InvoiceSummary, selector: "rolatech-invoice-summary", inputs: ["tax", "credit", "subtotal", "total"] }, { kind: "component", type: InvoiceUserSkeleton, selector: "rolatech-invoice-user-skeleton" }, { kind: "component", type: InvoiceSummarySkeleton, selector: "rolatech-invoice-summary-skeleton" }, { kind: "component", type: InvoiceLinesSkeleton, selector: "rolatech-invoice-lines-skeleton" }, { kind: "component", type: Breadcrumb, selector: "rolatech-breadcrumb", inputs: ["items"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }] }); }
|
|
2018
|
+
}
|
|
2019
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: MarketInvoiceDetail, decorators: [{
|
|
2020
|
+
type: Component,
|
|
2021
|
+
args: [{ selector: 'rolatech-market-invoice-detail', imports: [
|
|
2022
|
+
CommonModule,
|
|
2023
|
+
RouterModule,
|
|
2024
|
+
MatButtonModule,
|
|
2025
|
+
MatIconModule,
|
|
2026
|
+
InvoiceUser,
|
|
2027
|
+
InvoiceLines,
|
|
2028
|
+
InvoiceSummary,
|
|
2029
|
+
InvoiceUserSkeleton,
|
|
2030
|
+
InvoiceSummarySkeleton,
|
|
2031
|
+
InvoiceLinesSkeleton,
|
|
2032
|
+
PricePipe,
|
|
2033
|
+
Breadcrumb,
|
|
2034
|
+
], template: "<section class=\"invoice-detail-page\">\n <rolatech-breadcrumb [items]=\"breadcrumbs()\"></rolatech-breadcrumb>\n @if (loading()) {\n <section class=\"invoice-detail-page__hero invoice-detail-page__hero--loading\">\n <div class=\"invoice-detail-page__hero-copy\">\n <span class=\"invoice-detail-page__eyebrow\">{{ pageMeta.eyebrow }}</span>\n <h1 class=\"invoice-detail-page__title\">Loading invoice...</h1>\n </div>\n </section>\n\n <section class=\"invoice-detail-page__content\">\n <div class=\"invoice-detail-page__main\">\n <rolatech-invoice-user-skeleton></rolatech-invoice-user-skeleton>\n <rolatech-invoice-lines-skeleton></rolatech-invoice-lines-skeleton>\n </div>\n <div class=\"invoice-detail-page__side\">\n <rolatech-invoice-summary-skeleton></rolatech-invoice-summary-skeleton>\n </div>\n </section>\n } @else if (invoice(); as invoice) {\n <section class=\"invoice-detail-page__hero\">\n <div class=\"invoice-detail-page__hero-copy\">\n <span class=\"invoice-detail-page__eyebrow\">{{ pageMeta.eyebrow }}</span>\n <h1 class=\"invoice-detail-page__title\">Invoice detail</h1>\n <h1 class=\"invoice-detail-page__title\"></h1>\n <p class=\"invoice-detail-page__description\">#{{ invoice.invoiceNumber || invoice.id }}</p>\n <p class=\"invoice-detail-page__description\">\n Invoice for {{ invoice.fullName || invoice.firstName + ' ' + invoice.lastName }} created {{ invoice.createdAt | date:\n 'mediumDate' }}.\n </p>\n </div>\n\n <div class=\"invoice-detail-page__hero-side\">\n <span [class]=\"statusToneClass(invoice.status)\">{{ status[invoice.status] }}</span>\n <div class=\"invoice-detail-page__total\">{{ invoice.total | price }}</div>\n <div class=\"invoice-detail-page__note\">\n @if (invoice.dueAt) { Due {{ invoice.dueAt | date: 'mediumDate' }} } @else { Payable on receipt }\n </div>\n\n @if (invoice.status.toString() === 'ISSUED' || invoice.status.toString() === 'SENT') {\n <button mat-flat-button [disabled]=\"paying\" class=\"invoice-detail-page__cta\" (click)=\"pay()\">\n {{ paying ? 'Processing payment...' : 'Pay invoice' }}\n </button>\n }\n </div>\n </section>\n\n <section class=\"invoice-detail-page__content\">\n <div class=\"invoice-detail-page__main\">\n <rolatech-invoice-user\n [firstName]=\"invoice.firstName\"\n [lastName]=\"invoice.lastName\"\n [email]=\"invoice.email\"\n [phone]=\"invoice.phone\"\n ></rolatech-invoice-user>\n\n @if (invoice.lines; as lines) {\n <rolatech-invoice-lines [lines]=\"lines\"></rolatech-invoice-lines>\n }\n </div>\n\n <div class=\"invoice-detail-page__side\">\n <rolatech-invoice-summary\n [tax]=\"invoice.vatTotal\"\n [credit]=\"invoice.holdingDepositApplied\"\n [subtotal]=\"invoice.subtotal\"\n [total]=\"invoice.total\"\n ></rolatech-invoice-summary>\n\n <article class=\"invoice-detail-page__panel\">\n <h2 class=\"invoice-detail-page__panel-title\">Invoice note</h2>\n <p class=\"invoice-detail-page__panel-copy\">{{ invoice.note || 'No note added for this invoice.' }}</p>\n\n @if (invoice.status.toString() === 'CREATED' || invoice.status.toString() === 'PAID') {\n <div class=\"invoice-detail-page__panel-row\">\n <span>Payment method</span>\n <strong>Stripe</strong>\n </div>\n }\n </article>\n </div>\n </section>\n }\n</section>\n", styles: [":host{display:block;padding:1rem;--rt-workspace-status-issued-surface: color-mix(in srgb, var(--rt-brand-color) 14%, transparent);--rt-workspace-status-issued-color: color-mix(in srgb, var(--rt-brand-color) 82%, var(--rt-text-primary) 18%);--rt-workspace-status-paid-surface: color-mix(in srgb, var(--rt-brand-color) 16%, var(--rt-base-background, #ffffff));--rt-workspace-status-paid-color: color-mix(in srgb, var(--rt-brand-color) 58%, var(--rt-text-primary) 42%)}.invoice-detail-page{display:flex;flex-direction:column;gap:1rem}.invoice-detail-page__hero,.invoice-detail-page__panel{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.invoice-detail-page__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.invoice-detail-page__hero-copy,.invoice-detail-page__hero-side,.invoice-detail-page__main,.invoice-detail-page__side{display:flex;flex-direction:column;gap:1rem}.invoice-detail-page__back{color:var(--rt-text-secondary);font-weight:600;text-decoration:none}.invoice-detail-page__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.invoice-detail-page__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.invoice-detail-page__description,.invoice-detail-page__note,.invoice-detail-page__panel-copy{margin:0;color:var(--rt-text-secondary);line-height:1.7}.invoice-detail-page__status{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.45rem .82rem;font-size:.8rem;font-weight:700}.invoice-detail-page__status--neutral{background:color-mix(in srgb,var(--rt-brand-color) 10%,transparent);color:var(--rt-brand-color)}.invoice-detail-page__status--issued{background:var(--rt-workspace-status-issued-surface);color:var(--rt-workspace-status-issued-color)}.invoice-detail-page__status--paid{background:var(--rt-workspace-status-paid-surface);color:var(--rt-workspace-status-paid-color)}.invoice-detail-page__status--void{background:color-mix(in srgb,var(--mat-sys-error, #b91c1c) 14%,transparent);color:var(--mat-sys-error, #b91c1c)}.invoice-detail-page__total{color:var(--rt-text-primary);font-size:clamp(1.8rem,2.4vw,2.3rem);font-weight:800}.invoice-detail-page__cta{min-height:3rem;border-radius:9999px}.invoice-detail-page__content{display:grid;gap:1rem}.invoice-detail-page__panel{padding:1rem 1.1rem}.invoice-detail-page__panel-title{margin:0 0 .6rem;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.invoice-detail-page__panel-row{display:flex;justify-content:space-between;gap:1rem;margin-top:1rem;color:var(--rt-text-secondary)}.invoice-detail-page__panel-row strong{color:var(--rt-text-primary)}@media(min-width:1024px){.invoice-detail-page__hero{grid-template-columns:minmax(0,1.65fr) minmax(18rem,.85fr);align-items:start}.invoice-detail-page__content{grid-template-columns:minmax(0,1.65fr) minmax(20rem,.85fr);align-items:start}}@media(min-width:768px){:host{padding:1.25rem}}\n"] }]
|
|
2035
|
+
}] });
|
|
2036
|
+
|
|
2037
|
+
const marketInvoiceRoutes = [
|
|
2038
|
+
{
|
|
2039
|
+
path: '',
|
|
2040
|
+
component: MarketInvoiceIndex,
|
|
2041
|
+
},
|
|
2042
|
+
{
|
|
2043
|
+
path: ':id',
|
|
2044
|
+
component: MarketInvoiceDetail,
|
|
2045
|
+
},
|
|
2046
|
+
];
|
|
2047
|
+
|
|
1569
2048
|
/**
|
|
1570
2049
|
* Generated bundle index. Do not edit.
|
|
1571
2050
|
*/
|
|
1572
2051
|
|
|
1573
|
-
export { agentInvoiceRoutes, billingRoutes, invoiceManageRoutes, invoiceRoutes };
|
|
2052
|
+
export { BILLING_PROFILE_ROUTES, BillingProfileFacade, agentInvoiceRoutes, billingRoutes, invoiceManageRoutes, invoiceRoutes, marketInvoiceRoutes };
|
|
1574
2053
|
//# sourceMappingURL=rolatech-angular-billing.mjs.map
|