@rolatech/angular-billing 20.3.2-beta.4 → 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.
|
@@ -14,7 +14,7 @@ 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
19
|
import { InvoiceService, PaymentService, EnumApiClient, BillingProfileService } from '@rolatech/angular-services';
|
|
20
20
|
import * as i6 from '@angular/material/paginator';
|
|
@@ -75,7 +75,7 @@ var InvoiceStatus;
|
|
|
75
75
|
|
|
76
76
|
class InvoiceItem {
|
|
77
77
|
constructor() {
|
|
78
|
-
this.invoice = input.required(...(ngDevMode ? [{ debugName: "invoice" }] : []));
|
|
78
|
+
this.invoice = input.required(...(ngDevMode ? [{ debugName: "invoice" }] : /* istanbul ignore next */ []));
|
|
79
79
|
this.status = InvoiceStatus;
|
|
80
80
|
this.download = output();
|
|
81
81
|
}
|
|
@@ -101,31 +101,31 @@ class InvoiceItem {
|
|
|
101
101
|
return 'invoice-item__status invoice-item__status--neutral';
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
105
|
-
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" }] }); }
|
|
106
106
|
}
|
|
107
|
-
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: [{
|
|
108
108
|
type: Component,
|
|
109
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"] }]
|
|
110
110
|
}], propDecorators: { invoice: [{ type: i0.Input, args: [{ isSignal: true, alias: "invoice", required: true }] }], download: [{ type: i0.Output, args: ["download"] }] } });
|
|
111
111
|
|
|
112
112
|
class InvoiceManageItem {
|
|
113
113
|
constructor() {
|
|
114
|
-
this.invoice = input.required(...(ngDevMode ? [{ debugName: "invoice" }] : []));
|
|
114
|
+
this.invoice = input.required(...(ngDevMode ? [{ debugName: "invoice" }] : /* istanbul ignore next */ []));
|
|
115
115
|
}
|
|
116
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
117
|
-
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: [""] }); }
|
|
118
118
|
}
|
|
119
|
-
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: [{
|
|
120
120
|
type: Component,
|
|
121
121
|
args: [{ selector: 'rolatech-invoice-manage-item', imports: [], template: "<p>invoice-manage-item works!</p>\n<div>{{invoice().id}}</div>\n" }]
|
|
122
122
|
}], propDecorators: { invoice: [{ type: i0.Input, args: [{ isSignal: true, alias: "invoice", required: true }] }] } });
|
|
123
123
|
|
|
124
124
|
class InvoiceItemSkeleton {
|
|
125
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
126
|
-
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 }); }
|
|
127
127
|
}
|
|
128
|
-
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: [{
|
|
129
129
|
type: Component,
|
|
130
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" }]
|
|
131
131
|
}] });
|
|
@@ -162,13 +162,13 @@ class InvoiceIndexComponent extends BaseComponent {
|
|
|
162
162
|
status: 'paid',
|
|
163
163
|
},
|
|
164
164
|
];
|
|
165
|
-
this.invoices = signal([], ...(ngDevMode ? [{ debugName: "invoices" }] : []));
|
|
165
|
+
this.invoices = signal([], ...(ngDevMode ? [{ debugName: "invoices" }] : /* istanbul ignore next */ []));
|
|
166
166
|
this.select = 0;
|
|
167
167
|
this.filter = false;
|
|
168
168
|
this.length = 100;
|
|
169
169
|
this.pageSize = 15;
|
|
170
170
|
this.pageSizeOptions = [5, 10, 25, 100];
|
|
171
|
-
this.pageIndex = signal(0, ...(ngDevMode ? [{ debugName: "pageIndex" }] : []));
|
|
171
|
+
this.pageIndex = signal(0, ...(ngDevMode ? [{ debugName: "pageIndex" }] : /* istanbul ignore next */ []));
|
|
172
172
|
this.pageMeta = {
|
|
173
173
|
baseLink: this.readRouteData('invoiceBaseLink', '/invoices'),
|
|
174
174
|
eyebrow: this.readRouteData('invoiceEyebrow', 'Billing workspace'),
|
|
@@ -183,7 +183,7 @@ class InvoiceIndexComponent extends BaseComponent {
|
|
|
183
183
|
{ label: 'Issued', value: invoices.filter((item) => item.status === InvoiceStatus.ISSUED || item.status === InvoiceStatus.SENT).length, hint: 'Sent and awaiting payment' },
|
|
184
184
|
{ label: 'Paid', value: invoices.filter((item) => item.status === InvoiceStatus.PAID).length, hint: 'Successfully settled' },
|
|
185
185
|
];
|
|
186
|
-
}, ...(ngDevMode ? [{ debugName: "stats" }] : []));
|
|
186
|
+
}, ...(ngDevMode ? [{ debugName: "stats" }] : /* istanbul ignore next */ []));
|
|
187
187
|
}
|
|
188
188
|
ngOnInit() {
|
|
189
189
|
const sub = this.route.queryParamMap
|
|
@@ -279,10 +279,10 @@ class InvoiceIndexComponent extends BaseComponent {
|
|
|
279
279
|
}
|
|
280
280
|
return fallback;
|
|
281
281
|
}
|
|
282
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
283
|
-
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 }); }
|
|
284
284
|
}
|
|
285
|
-
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: [{
|
|
286
286
|
type: Component,
|
|
287
287
|
args: [{ selector: 'rolatech-invoice-index', imports: [
|
|
288
288
|
RouterModule,
|
|
@@ -301,15 +301,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
301
301
|
|
|
302
302
|
class InvoiceUser {
|
|
303
303
|
constructor() {
|
|
304
|
-
this.firstName = input.required(...(ngDevMode ? [{ debugName: "firstName" }] : []));
|
|
305
|
-
this.lastName = input.required(...(ngDevMode ? [{ debugName: "lastName" }] : []));
|
|
306
|
-
this.email = input.required(...(ngDevMode ? [{ debugName: "email" }] : []));
|
|
307
|
-
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 */ []));
|
|
308
308
|
}
|
|
309
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
310
|
-
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 }); }
|
|
311
311
|
}
|
|
312
|
-
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: [{
|
|
313
313
|
type: Component,
|
|
314
314
|
args: [{ selector: 'rolatech-invoice-user', imports: [], encapsulation: ViewEncapsulation.None, host: {
|
|
315
315
|
id: 'rolatech-invoice-user',
|
|
@@ -319,28 +319,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
319
319
|
|
|
320
320
|
class InvoiceLines {
|
|
321
321
|
constructor() {
|
|
322
|
-
this.lines = input([], ...(ngDevMode ? [{ debugName: "lines" }] : []));
|
|
322
|
+
this.lines = input([], ...(ngDevMode ? [{ debugName: "lines" }] : /* istanbul ignore next */ []));
|
|
323
323
|
}
|
|
324
324
|
add() { }
|
|
325
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
326
|
-
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 }); }
|
|
327
327
|
}
|
|
328
|
-
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: [{
|
|
329
329
|
type: Component,
|
|
330
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"] }]
|
|
331
331
|
}], propDecorators: { lines: [{ type: i0.Input, args: [{ isSignal: true, alias: "lines", required: false }] }] } });
|
|
332
332
|
|
|
333
333
|
class InvoiceSummary {
|
|
334
334
|
constructor() {
|
|
335
|
-
this.tax = input.required(...(ngDevMode ? [{ debugName: "tax" }] : []));
|
|
336
|
-
this.credit = input(0, ...(ngDevMode ? [{ debugName: "credit" }] : []));
|
|
337
|
-
this.subtotal = input.required(...(ngDevMode ? [{ debugName: "subtotal" }] : []));
|
|
338
|
-
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 */ []));
|
|
339
339
|
}
|
|
340
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
341
|
-
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 }); }
|
|
342
342
|
}
|
|
343
|
-
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: [{
|
|
344
344
|
type: Component,
|
|
345
345
|
args: [{ selector: 'rolatech-invoice-summary', imports: [PricePipe], encapsulation: ViewEncapsulation.None, host: {
|
|
346
346
|
id: 'rolatech-invoice-summary',
|
|
@@ -349,28 +349,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
349
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 }] }] } });
|
|
350
350
|
|
|
351
351
|
class InvoiceUserSkeleton {
|
|
352
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
353
|
-
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 }); }
|
|
354
354
|
}
|
|
355
|
-
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: [{
|
|
356
356
|
type: Component,
|
|
357
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" }]
|
|
358
358
|
}] });
|
|
359
359
|
|
|
360
360
|
class InvoiceSummarySkeleton {
|
|
361
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
362
|
-
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 }); }
|
|
363
363
|
}
|
|
364
|
-
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: [{
|
|
365
365
|
type: Component,
|
|
366
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" }]
|
|
367
367
|
}] });
|
|
368
368
|
|
|
369
369
|
class InvoiceLinesSkeleton {
|
|
370
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
371
|
-
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 }); }
|
|
372
372
|
}
|
|
373
|
-
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: [{
|
|
374
374
|
type: Component,
|
|
375
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" }]
|
|
376
376
|
}] });
|
|
@@ -380,10 +380,10 @@ class InvoiceDetailComponent extends BaseComponent {
|
|
|
380
380
|
super(...arguments);
|
|
381
381
|
this.invoiceService = inject(InvoiceService);
|
|
382
382
|
this.paymentService = inject(PaymentService);
|
|
383
|
-
this.invoice = signal(null, ...(ngDevMode ? [{ debugName: "invoice" }] : []));
|
|
383
|
+
this.invoice = signal(null, ...(ngDevMode ? [{ debugName: "invoice" }] : /* istanbul ignore next */ []));
|
|
384
384
|
this.status = InvoiceStatus;
|
|
385
385
|
this.paying = false;
|
|
386
|
-
this.loading = signal(true, ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
386
|
+
this.loading = signal(true, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
387
387
|
this.pageMeta = {
|
|
388
388
|
baseLink: this.readRouteData('invoiceBaseLink', '/invoices'),
|
|
389
389
|
collectionTitle: this.readRouteData('invoiceCollectionTitle', 'Invoices'),
|
|
@@ -414,7 +414,7 @@ class InvoiceDetailComponent extends BaseComponent {
|
|
|
414
414
|
}
|
|
415
415
|
get(id) {
|
|
416
416
|
this.loading.set(true);
|
|
417
|
-
this.invoiceService.
|
|
417
|
+
this.invoiceService.getInvoice(id).subscribe({
|
|
418
418
|
next: (res) => {
|
|
419
419
|
this.invoice.set(res.data);
|
|
420
420
|
this.loading.set(false);
|
|
@@ -479,10 +479,10 @@ class InvoiceDetailComponent extends BaseComponent {
|
|
|
479
479
|
}
|
|
480
480
|
return fallback;
|
|
481
481
|
}
|
|
482
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
483
|
-
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" }] }); }
|
|
484
484
|
}
|
|
485
|
-
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: [{
|
|
486
486
|
type: Component,
|
|
487
487
|
args: [{ selector: 'rolatech-invoice-detail', imports: [
|
|
488
488
|
CommonModule,
|
|
@@ -515,13 +515,13 @@ class InvoiceManageIndex extends BaseComponent {
|
|
|
515
515
|
super(...arguments);
|
|
516
516
|
this.invoiceService = inject(InvoiceService);
|
|
517
517
|
this.enumApi = inject(EnumApiClient);
|
|
518
|
-
this.invoiceTypeMap = signal({}, ...(ngDevMode ? [{ debugName: "invoiceTypeMap" }] : []));
|
|
518
|
+
this.invoiceTypeMap = signal({}, ...(ngDevMode ? [{ debugName: "invoiceTypeMap" }] : /* istanbul ignore next */ []));
|
|
519
519
|
this.select = 0;
|
|
520
520
|
this.loading = false;
|
|
521
|
-
this.invoices = signal([], ...(ngDevMode ? [{ debugName: "invoices" }] : []));
|
|
521
|
+
this.invoices = signal([], ...(ngDevMode ? [{ debugName: "invoices" }] : /* istanbul ignore next */ []));
|
|
522
522
|
this.length = 100;
|
|
523
523
|
this.pageSize = 15;
|
|
524
|
-
this.pageIndex = signal(0, ...(ngDevMode ? [{ debugName: "pageIndex" }] : []));
|
|
524
|
+
this.pageIndex = signal(0, ...(ngDevMode ? [{ debugName: "pageIndex" }] : /* istanbul ignore next */ []));
|
|
525
525
|
this.pageSizeOptions = [5, 10, 25, 100];
|
|
526
526
|
this.links = [
|
|
527
527
|
{
|
|
@@ -572,10 +572,9 @@ class InvoiceManageIndex extends BaseComponent {
|
|
|
572
572
|
}))
|
|
573
573
|
.subscribe({
|
|
574
574
|
next: (res) => {
|
|
575
|
-
|
|
576
|
-
this.invoices.set(res.data);
|
|
575
|
+
this.invoices.set(res.data ?? []);
|
|
577
576
|
this.meta = res.meta;
|
|
578
|
-
this.length = res.meta
|
|
577
|
+
this.length = res.meta?.pagination?.count ?? res.data?.length ?? 0;
|
|
579
578
|
},
|
|
580
579
|
error: () => {
|
|
581
580
|
this.invoices.set([]);
|
|
@@ -618,10 +617,10 @@ class InvoiceManageIndex extends BaseComponent {
|
|
|
618
617
|
return 'bg-gray-100 text-gray-700';
|
|
619
618
|
}
|
|
620
619
|
}
|
|
621
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
622
|
-
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" }] }); }
|
|
623
622
|
}
|
|
624
|
-
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: [{
|
|
625
624
|
type: Component,
|
|
626
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" }]
|
|
627
626
|
}] });
|
|
@@ -716,10 +715,10 @@ class InvoiceManageCreate {
|
|
|
716
715
|
todayIso() {
|
|
717
716
|
return new Date().toISOString().slice(0, 10);
|
|
718
717
|
}
|
|
719
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
720
|
-
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: 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" }] }); }
|
|
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" }] }); }
|
|
721
720
|
}
|
|
722
|
-
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: [{
|
|
723
722
|
type: Component,
|
|
724
723
|
args: [{ selector: 'rolatech-invoice-manage-create', imports: [
|
|
725
724
|
CommonModule,
|
|
@@ -751,14 +750,14 @@ class InvoiceEdit extends BaseComponent {
|
|
|
751
750
|
constructor() {
|
|
752
751
|
super(...arguments);
|
|
753
752
|
this.minDate = new Date();
|
|
754
|
-
this.invoice = model.required(...(ngDevMode ? [{ debugName: "invoice" }] : []));
|
|
753
|
+
this.invoice = model.required(...(ngDevMode ? [{ debugName: "invoice" }] : /* istanbul ignore next */ []));
|
|
755
754
|
this.output = output();
|
|
756
755
|
}
|
|
757
756
|
ngDoCheck() {
|
|
758
757
|
this.output.emit(this.invoice());
|
|
759
758
|
}
|
|
760
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
761
|
-
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: [
|
|
762
761
|
{
|
|
763
762
|
provide: DateAdapter,
|
|
764
763
|
useClass: MomentDateAdapter,
|
|
@@ -767,7 +766,7 @@ class InvoiceEdit extends BaseComponent {
|
|
|
767
766
|
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS },
|
|
768
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 }] }); }
|
|
769
768
|
}
|
|
770
|
-
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: [{
|
|
771
770
|
type: Component,
|
|
772
771
|
args: [{ selector: 'rolatech-invoice-edit', imports: [CommonModule, FormsModule, MatInputModule, MatDatepickerModule, MatSelectModule, MatOptionModule], providers: [
|
|
773
772
|
{
|
|
@@ -782,21 +781,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
782
781
|
class InvoiceStore {
|
|
783
782
|
constructor() {
|
|
784
783
|
this.api = inject(InvoiceService);
|
|
785
|
-
this._lines = signal([], ...(ngDevMode ? [{ debugName: "_lines" }] : []));
|
|
784
|
+
this._lines = signal([], ...(ngDevMode ? [{ debugName: "_lines" }] : /* istanbul ignore next */ []));
|
|
786
785
|
// --- state ---
|
|
787
|
-
this.invoiceId = signal(null, ...(ngDevMode ? [{ debugName: "invoiceId" }] : []));
|
|
788
|
-
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 */ []));
|
|
789
788
|
/** last server-saved snapshot */
|
|
790
|
-
this.pristine = signal(null, ...(ngDevMode ? [{ debugName: "pristine" }] : []));
|
|
791
|
-
this.status = signal('idle', ...(ngDevMode ? [{ debugName: "status" }] : []));
|
|
792
|
-
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 */ []));
|
|
793
792
|
// editor UI state
|
|
794
|
-
this.editorOpen = signal(false, ...(ngDevMode ? [{ debugName: "editorOpen" }] : []));
|
|
795
|
-
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 */ []));
|
|
796
795
|
// --- derived ---
|
|
797
|
-
this.lines = computed(() => this.invoice()?.lines ?? [], ...(ngDevMode ? [{ debugName: "lines" }] : []));
|
|
798
|
-
this.currency = computed(() => this.invoice()?.currency ?? 'GBP', ...(ngDevMode ? [{ debugName: "currency" }] : []));
|
|
799
|
-
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 */ []));
|
|
800
799
|
this.totals = computed(() => {
|
|
801
800
|
const inv = this.invoice();
|
|
802
801
|
if (!inv) {
|
|
@@ -807,7 +806,7 @@ class InvoiceStore {
|
|
|
807
806
|
const vatTotal = lines.reduce((s, l) => s + (l.vatAmount ?? 0), 0);
|
|
808
807
|
const total = lines.reduce((s, l) => s + (l.lineTotal ?? 0), 0);
|
|
809
808
|
return { subtotal, vatTotal, total };
|
|
810
|
-
}, ...(ngDevMode ? [{ debugName: "totals" }] : []));
|
|
809
|
+
}, ...(ngDevMode ? [{ debugName: "totals" }] : /* istanbul ignore next */ []));
|
|
811
810
|
/** 🔴 DIRTY FLAG */
|
|
812
811
|
this.dirty = computed(() => {
|
|
813
812
|
const a = this.invoice();
|
|
@@ -815,7 +814,7 @@ class InvoiceStore {
|
|
|
815
814
|
if (!a || !b)
|
|
816
815
|
return false;
|
|
817
816
|
return JSON.stringify(a) !== JSON.stringify(b);
|
|
818
|
-
}, ...(ngDevMode ? [{ debugName: "dirty" }] : []));
|
|
817
|
+
}, ...(ngDevMode ? [{ debugName: "dirty" }] : /* istanbul ignore next */ []));
|
|
819
818
|
}
|
|
820
819
|
// --- API: load / refresh ---
|
|
821
820
|
load(id) {
|
|
@@ -825,7 +824,7 @@ class InvoiceStore {
|
|
|
825
824
|
// ✅ Adapt to your actual API signature:
|
|
826
825
|
// e.g. this.api.get(id) or this.api.getInvoice(id)
|
|
827
826
|
this.api
|
|
828
|
-
.
|
|
827
|
+
.getInvoice(id)
|
|
829
828
|
.pipe(finalize$1(() => this.status.set('idle')))
|
|
830
829
|
.subscribe({
|
|
831
830
|
next: (inv) => {
|
|
@@ -1043,19 +1042,19 @@ class InvoiceStore {
|
|
|
1043
1042
|
errMsg(e) {
|
|
1044
1043
|
return e?.error?.message || e?.message || 'Request failed';
|
|
1045
1044
|
}
|
|
1046
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1047
|
-
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' }); }
|
|
1048
1047
|
}
|
|
1049
|
-
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: [{
|
|
1050
1049
|
type: Injectable,
|
|
1051
1050
|
args: [{ providedIn: 'root' }]
|
|
1052
1051
|
}] });
|
|
1053
1052
|
|
|
1054
1053
|
class InvoiceLinePropertySelector {
|
|
1055
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1056
|
-
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: [""] }); }
|
|
1057
1056
|
}
|
|
1058
|
-
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: [{
|
|
1059
1058
|
type: Component,
|
|
1060
1059
|
args: [{ selector: 'rolatech-invoice-line-property-selector', imports: [], template: "<p>invoice-line-property-selector works!</p>\n" }]
|
|
1061
1060
|
}] });
|
|
@@ -1066,7 +1065,7 @@ class InvoiceManageLine {
|
|
|
1066
1065
|
this.dialog = inject(MatDialog);
|
|
1067
1066
|
this.lines = this.store.lines;
|
|
1068
1067
|
this.vatMode = this.store.vatMode;
|
|
1069
|
-
this.editMode = signal(false, ...(ngDevMode ? [{ debugName: "editMode" }] : []));
|
|
1068
|
+
this.editMode = signal(false, ...(ngDevMode ? [{ debugName: "editMode" }] : /* istanbul ignore next */ []));
|
|
1070
1069
|
this.openingForLine = new Set();
|
|
1071
1070
|
this.isVatExclusive = () => this.vatMode() === 'EXCLUSIVE';
|
|
1072
1071
|
}
|
|
@@ -1137,10 +1136,10 @@ class InvoiceManageLine {
|
|
|
1137
1136
|
});
|
|
1138
1137
|
}
|
|
1139
1138
|
saveLines() { }
|
|
1140
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1141
|
-
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 }); }
|
|
1142
1141
|
}
|
|
1143
|
-
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: [{
|
|
1144
1143
|
type: Component,
|
|
1145
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"] }]
|
|
1146
1145
|
}] });
|
|
@@ -1154,8 +1153,8 @@ class InvoiceManageDetail extends BaseComponent {
|
|
|
1154
1153
|
this.invoice = this.store.invoice;
|
|
1155
1154
|
this.status = InvoiceStatus;
|
|
1156
1155
|
// convenience derived values for template
|
|
1157
|
-
this.loading = computed(() => this.store.status() === 'loading', ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
1158
|
-
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 */ []));
|
|
1159
1158
|
}
|
|
1160
1159
|
ngOnInit() {
|
|
1161
1160
|
this.store.load(this.id);
|
|
@@ -1254,19 +1253,19 @@ class InvoiceManageDetail extends BaseComponent {
|
|
|
1254
1253
|
}
|
|
1255
1254
|
});
|
|
1256
1255
|
}
|
|
1257
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1258
|
-
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" }] }); }
|
|
1259
1258
|
}
|
|
1260
|
-
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: [{
|
|
1261
1260
|
type: Component,
|
|
1262
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" }]
|
|
1263
1262
|
}] });
|
|
1264
1263
|
|
|
1265
1264
|
class InvoiceManageDownload {
|
|
1266
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1267
|
-
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: [""] }); }
|
|
1268
1267
|
}
|
|
1269
|
-
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: [{
|
|
1270
1269
|
type: Component,
|
|
1271
1270
|
args: [{ selector: 'rolatech-invoice-manage-download', imports: [], template: "<p>invoice-manage-download works!</p>\n" }]
|
|
1272
1271
|
}] });
|
|
@@ -1302,10 +1301,10 @@ class AgentInvoiceIndex extends BaseComponent {
|
|
|
1302
1301
|
this.invoiceStatus = InvoiceStatus;
|
|
1303
1302
|
this.select = 0;
|
|
1304
1303
|
this.loading = false;
|
|
1305
|
-
this.invoices = signal([], ...(ngDevMode ? [{ debugName: "invoices" }] : []));
|
|
1304
|
+
this.invoices = signal([], ...(ngDevMode ? [{ debugName: "invoices" }] : /* istanbul ignore next */ []));
|
|
1306
1305
|
this.length = 100;
|
|
1307
1306
|
this.pageSize = 15;
|
|
1308
|
-
this.pageIndex = signal(0, ...(ngDevMode ? [{ debugName: "pageIndex" }] : []));
|
|
1307
|
+
this.pageIndex = signal(0, ...(ngDevMode ? [{ debugName: "pageIndex" }] : /* istanbul ignore next */ []));
|
|
1309
1308
|
this.pageSizeOptions = [5, 10, 25, 100];
|
|
1310
1309
|
this.links = [
|
|
1311
1310
|
{
|
|
@@ -1339,7 +1338,7 @@ class AgentInvoiceIndex extends BaseComponent {
|
|
|
1339
1338
|
{ label: 'Issued', value: invoices.filter((item) => item.status === InvoiceStatus.ISSUED || item.status === InvoiceStatus.SENT).length, hint: 'Awaiting payment' },
|
|
1340
1339
|
{ label: 'Paid', value: invoices.filter((item) => item.status === InvoiceStatus.PAID).length, hint: 'Completed payments' },
|
|
1341
1340
|
];
|
|
1342
|
-
}, ...(ngDevMode ? [{ debugName: "stats" }] : []));
|
|
1341
|
+
}, ...(ngDevMode ? [{ debugName: "stats" }] : /* istanbul ignore next */ []));
|
|
1343
1342
|
}
|
|
1344
1343
|
find() {
|
|
1345
1344
|
this.router.navigate([], {
|
|
@@ -1388,9 +1387,9 @@ class AgentInvoiceIndex extends BaseComponent {
|
|
|
1388
1387
|
}))
|
|
1389
1388
|
.subscribe({
|
|
1390
1389
|
next: (res) => {
|
|
1391
|
-
this.invoices.set(res.data);
|
|
1390
|
+
this.invoices.set(res.data ?? []);
|
|
1392
1391
|
this.meta = res.meta;
|
|
1393
|
-
this.length = res.meta
|
|
1392
|
+
this.length = res.meta?.pagination?.count ?? res.data?.length ?? 0;
|
|
1394
1393
|
},
|
|
1395
1394
|
error: () => {
|
|
1396
1395
|
this.invoices.set([]);
|
|
@@ -1434,10 +1433,10 @@ class AgentInvoiceIndex extends BaseComponent {
|
|
|
1434
1433
|
}
|
|
1435
1434
|
return fallback;
|
|
1436
1435
|
}
|
|
1437
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1438
|
-
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" }] }); }
|
|
1439
1438
|
}
|
|
1440
|
-
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: [{
|
|
1441
1440
|
type: Component,
|
|
1442
1441
|
args: [{ selector: 'rolatech-agent-invoice-index', imports: [
|
|
1443
1442
|
RouterModule,
|
|
@@ -1458,8 +1457,8 @@ class AgentInvoiceDetail extends BaseComponent {
|
|
|
1458
1457
|
this.invoiceService = inject(InvoiceService);
|
|
1459
1458
|
this.paymentService = inject(PaymentService);
|
|
1460
1459
|
this.pdfUrl = '';
|
|
1461
|
-
this.loading = signal(true, ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
1462
|
-
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 */ []));
|
|
1463
1462
|
this.status = InvoiceStatus;
|
|
1464
1463
|
this.pageMeta = {
|
|
1465
1464
|
baseLink: this.readRouteData('invoiceBaseLink', '/invoices'),
|
|
@@ -1539,10 +1538,10 @@ class AgentInvoiceDetail extends BaseComponent {
|
|
|
1539
1538
|
}
|
|
1540
1539
|
return fallback;
|
|
1541
1540
|
}
|
|
1542
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1543
|
-
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" }] }); }
|
|
1544
1543
|
}
|
|
1545
|
-
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: [{
|
|
1546
1545
|
type: Component,
|
|
1547
1546
|
args: [{ selector: 'rolatech-agent-invoice-detail', imports: [
|
|
1548
1547
|
CommonModule,
|
|
@@ -1571,12 +1570,12 @@ const agentInvoiceRoutes = [
|
|
|
1571
1570
|
class BillingProfileFacade {
|
|
1572
1571
|
constructor() {
|
|
1573
1572
|
this.billingProfileService = inject(BillingProfileService);
|
|
1574
|
-
this.loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
1575
|
-
this.saving = signal(false, ...(ngDevMode ? [{ debugName: "saving" }] : []));
|
|
1576
|
-
this.profile = signal(null, ...(ngDevMode ? [{ debugName: "profile" }] : []));
|
|
1577
|
-
this.error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
|
|
1578
|
-
this.saveMessage = signal(null, ...(ngDevMode ? [{ debugName: "saveMessage" }] : []));
|
|
1579
|
-
this.enabled = computed(() => this.profile()?.enabled ?? false, ...(ngDevMode ? [{ debugName: "enabled" }] : []));
|
|
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 */ []));
|
|
1580
1579
|
}
|
|
1581
1580
|
load() {
|
|
1582
1581
|
this.loading.set(true);
|
|
@@ -1616,7 +1615,7 @@ class BillingProfileFacade {
|
|
|
1616
1615
|
this.error.set(null);
|
|
1617
1616
|
this.saveMessage.set(null);
|
|
1618
1617
|
this.billingProfileService
|
|
1619
|
-
.
|
|
1618
|
+
.patchProfile({ enabled })
|
|
1620
1619
|
.pipe(finalize(() => this.saving.set(false)))
|
|
1621
1620
|
.subscribe({
|
|
1622
1621
|
next: (response) => {
|
|
@@ -1628,11 +1627,14 @@ class BillingProfileFacade {
|
|
|
1628
1627
|
},
|
|
1629
1628
|
});
|
|
1630
1629
|
}
|
|
1631
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1632
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
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' }); }
|
|
1633
1632
|
}
|
|
1634
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1635
|
-
type: Injectable
|
|
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
|
+
}]
|
|
1636
1638
|
}] });
|
|
1637
1639
|
|
|
1638
1640
|
function createBillingProfileForm(formBuilder) {
|
|
@@ -1690,7 +1692,7 @@ class BillingProfileSettingPage {
|
|
|
1690
1692
|
this.formBuilder = inject(FormBuilder);
|
|
1691
1693
|
this.facade = inject(BillingProfileFacade);
|
|
1692
1694
|
this.form = createBillingProfileForm(this.formBuilder);
|
|
1693
|
-
this.canSubmit = computed(() => !this.facade.loading() && !this.facade.saving(), ...(ngDevMode ? [{ debugName: "canSubmit" }] : []));
|
|
1695
|
+
this.canSubmit = computed(() => !this.facade.loading() && !this.facade.saving(), ...(ngDevMode ? [{ debugName: "canSubmit" }] : /* istanbul ignore next */ []));
|
|
1694
1696
|
effect(() => {
|
|
1695
1697
|
const profile = this.facade.profile();
|
|
1696
1698
|
patchBillingProfileForm(this.form, profile);
|
|
@@ -1706,10 +1708,10 @@ class BillingProfileSettingPage {
|
|
|
1706
1708
|
toggleEnabled(enabled) {
|
|
1707
1709
|
this.facade.setEnabled(enabled);
|
|
1708
1710
|
}
|
|
1709
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1710
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
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 }); }
|
|
1711
1713
|
}
|
|
1712
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1714
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: BillingProfileSettingPage, decorators: [{
|
|
1713
1715
|
type: Component,
|
|
1714
1716
|
args: [{ selector: 'rolatech-billing-profile-setting-page', imports: [
|
|
1715
1717
|
CommonModule,
|
|
@@ -1729,9 +1731,323 @@ const BILLING_PROFILE_ROUTES = [
|
|
|
1729
1731
|
},
|
|
1730
1732
|
];
|
|
1731
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
|
+
|
|
1732
2048
|
/**
|
|
1733
2049
|
* Generated bundle index. Do not edit.
|
|
1734
2050
|
*/
|
|
1735
2051
|
|
|
1736
|
-
export { BILLING_PROFILE_ROUTES, BillingProfileFacade, agentInvoiceRoutes, billingRoutes, invoiceManageRoutes, invoiceRoutes };
|
|
2052
|
+
export { BILLING_PROFILE_ROUTES, BillingProfileFacade, agentInvoiceRoutes, billingRoutes, invoiceManageRoutes, invoiceRoutes, marketInvoiceRoutes };
|
|
1737
2053
|
//# sourceMappingURL=rolatech-angular-billing.mjs.map
|