@rolatech/angular-billing 20.3.0-beta.4 → 20.3.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,38 +1,36 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, output, Component, ViewEncapsulation, inject, signal,
|
|
2
|
+
import { input, output, Component, ViewEncapsulation, inject, signal, computed, model, Injectable } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/common';
|
|
4
4
|
import { CommonModule, DatePipe, KeyValuePipe } from '@angular/common';
|
|
5
5
|
import * as i1$2 from '@angular/forms';
|
|
6
6
|
import { FormsModule } from '@angular/forms';
|
|
7
7
|
import * as i2 from '@angular/material/button';
|
|
8
8
|
import { MatButtonModule } from '@angular/material/button';
|
|
9
|
-
import * as i6 from '@angular/material/core';
|
|
10
|
-
import { MatOptionModule, MAT_DATE_LOCALE, DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core';
|
|
11
|
-
import { MatDatepickerModule } from '@angular/material/datepicker';
|
|
12
9
|
import * as i4 from '@angular/material/form-field';
|
|
13
10
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
14
11
|
import * as i3 from '@angular/material/icon';
|
|
15
12
|
import { MatIcon, MatIconModule } from '@angular/material/icon';
|
|
16
|
-
import * as i5 from '@angular/material/
|
|
17
|
-
import { MatInputModule } from '@angular/material/input';
|
|
18
|
-
import * as i7 from '@angular/material/select';
|
|
13
|
+
import * as i5 from '@angular/material/select';
|
|
19
14
|
import { MatSelectModule } from '@angular/material/select';
|
|
20
15
|
import * as i1$1 from '@angular/router';
|
|
21
16
|
import { RouterModule, RouterLink } from '@angular/router';
|
|
22
|
-
import { Skeleton, BaseComponent,
|
|
17
|
+
import { Skeleton, BaseComponent, PageCollectionShellComponent, ToolbarComponent, TabsComponent, TabComponent, EnumSelect, ConfirmationDialogComponent, MaterialModule } from '@rolatech/angular-components';
|
|
23
18
|
import { PricePipe } from '@rolatech/angular-common';
|
|
24
19
|
import { InvoiceService, PaymentService, EnumApiClient } from '@rolatech/angular-services';
|
|
25
|
-
import
|
|
26
|
-
import * as i8 from '@angular/material/paginator';
|
|
20
|
+
import * as i6 from '@angular/material/paginator';
|
|
27
21
|
import { MatPaginatorModule } from '@angular/material/paginator';
|
|
28
22
|
import { map, distinctUntilChanged, switchMap, finalize } from 'rxjs';
|
|
29
|
-
import * as
|
|
23
|
+
import * as i8 from '@angular/material/card';
|
|
30
24
|
import { MatCardModule } from '@angular/material/card';
|
|
31
|
-
import * as
|
|
25
|
+
import * as i7 from '@angular/material/divider';
|
|
32
26
|
import { MatDividerModule } from '@angular/material/divider';
|
|
27
|
+
import * as i5$1 from '@angular/material/input';
|
|
28
|
+
import { MatInputModule } from '@angular/material/input';
|
|
33
29
|
import * as i3$1 from '@angular/material/menu';
|
|
34
30
|
import { MatMenuModule } from '@angular/material/menu';
|
|
35
31
|
import { MomentDateAdapter } from '@angular/material-moment-adapter';
|
|
32
|
+
import { MatOptionModule, MAT_DATE_LOCALE, DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core';
|
|
33
|
+
import { MatDatepickerModule } from '@angular/material/datepicker';
|
|
36
34
|
import { finalize as finalize$1 } from 'rxjs/operators';
|
|
37
35
|
import * as i1$3 from '@angular/cdk/drag-drop';
|
|
38
36
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
|
@@ -79,29 +77,34 @@ class InvoiceItem {
|
|
|
79
77
|
this.status = InvoiceStatus;
|
|
80
78
|
this.download = output();
|
|
81
79
|
}
|
|
80
|
+
customerName() {
|
|
81
|
+
return this.invoice().fullName || `${this.invoice().firstName} ${this.invoice().lastName}`.trim();
|
|
82
|
+
}
|
|
82
83
|
onDownload() {
|
|
83
84
|
this.download.emit();
|
|
84
85
|
}
|
|
85
86
|
statusBadgeClass(status) {
|
|
86
87
|
switch (status.toString()) {
|
|
87
88
|
case 'CREATED':
|
|
88
|
-
return '
|
|
89
|
+
return 'invoice-item__status invoice-item__status--draft';
|
|
89
90
|
case 'ISSUED':
|
|
90
|
-
|
|
91
|
+
case 'SENT':
|
|
92
|
+
case 'PARTIALLY_PAID':
|
|
93
|
+
return 'invoice-item__status invoice-item__status--issued';
|
|
91
94
|
case 'PAID':
|
|
92
|
-
return '
|
|
95
|
+
return 'invoice-item__status invoice-item__status--paid';
|
|
93
96
|
case 'VOID':
|
|
94
|
-
return '
|
|
97
|
+
return 'invoice-item__status invoice-item__status--void';
|
|
95
98
|
default:
|
|
96
|
-
return '
|
|
99
|
+
return 'invoice-item__status invoice-item__status--neutral';
|
|
97
100
|
}
|
|
98
101
|
}
|
|
99
102
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: InvoiceItem, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
100
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.
|
|
103
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", 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" }] }); }
|
|
101
104
|
}
|
|
102
105
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: InvoiceItem, decorators: [{
|
|
103
106
|
type: Component,
|
|
104
|
-
args: [{ selector: 'rolatech-invoice-item', imports: [CommonModule, MatButtonModule, MatIcon, DatePipe, PricePipe], template: "<
|
|
107
|
+
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"] }]
|
|
105
108
|
}], propDecorators: { invoice: [{ type: i0.Input, args: [{ isSignal: true, alias: "invoice", required: true }] }], download: [{ type: i0.Output, args: ["download"] }] } });
|
|
106
109
|
|
|
107
110
|
class InvoiceManageItem {
|
|
@@ -116,24 +119,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
116
119
|
args: [{ selector: 'rolatech-invoice-manage-item', imports: [], template: "<p>invoice-manage-item works!</p>\n<div>{{invoice().id}}</div>\n" }]
|
|
117
120
|
}], propDecorators: { invoice: [{ type: i0.Input, args: [{ isSignal: true, alias: "invoice", required: true }] }] } });
|
|
118
121
|
|
|
119
|
-
class InvoiceHeader {
|
|
120
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: InvoiceHeader, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
121
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.1", type: InvoiceHeader, isStandalone: true, selector: "rolatech-invoice-header", ngImport: i0, template: "<div class=\"flex items-center justify-between font-bold h-11 px-3\">\n <div class=\"flex items-center\">\n <div class=\"font-bold w-[180px] max-w-[180px]\">ID</div>\n </div>\n <div class=\"flex items-center gap-3\">\n <div class=\"font-bold\">Total</div>\n </div>\n <div class=\"flex items-center gap-3\">\n <div class=\"font-bold\">Status</div>\n <div class=\"font-bold w-[100px]\">Date</div>\n <div class=\"font-bold m-w-[80px]\">Download</div>\n </div>\n</div>\n", styles: [""] }); }
|
|
122
|
-
}
|
|
123
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: InvoiceHeader, decorators: [{
|
|
124
|
-
type: Component,
|
|
125
|
-
args: [{ selector: 'rolatech-invoice-header', imports: [], template: "<div class=\"flex items-center justify-between font-bold h-11 px-3\">\n <div class=\"flex items-center\">\n <div class=\"font-bold w-[180px] max-w-[180px]\">ID</div>\n </div>\n <div class=\"flex items-center gap-3\">\n <div class=\"font-bold\">Total</div>\n </div>\n <div class=\"flex items-center gap-3\">\n <div class=\"font-bold\">Status</div>\n <div class=\"font-bold w-[100px]\">Date</div>\n <div class=\"font-bold m-w-[80px]\">Download</div>\n </div>\n</div>\n" }]
|
|
126
|
-
}] });
|
|
127
|
-
|
|
128
|
-
class InvoiceHeaderSkeleton {
|
|
129
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: InvoiceHeaderSkeleton, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
130
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.1", type: InvoiceHeaderSkeleton, isStandalone: true, selector: "rolatech-invoice-header-skeleton", ngImport: i0, template: "<div class=\"grid grid-cols-6 h-11 p-3 gap-3\">\n <rolatech-skeleton class=\"col-span-1\"></rolatech-skeleton>\n <rolatech-skeleton class=\"col-start-3 col-span-2\"></rolatech-skeleton>\n <rolatech-skeleton class=\"col-start-6 col-span-1\"></rolatech-skeleton>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: Skeleton, selector: "rolatech-skeleton" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
131
|
-
}
|
|
132
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: InvoiceHeaderSkeleton, decorators: [{
|
|
133
|
-
type: Component,
|
|
134
|
-
args: [{ selector: 'rolatech-invoice-header-skeleton', imports: [Skeleton], encapsulation: ViewEncapsulation.None, template: "<div class=\"grid grid-cols-6 h-11 p-3 gap-3\">\n <rolatech-skeleton class=\"col-span-1\"></rolatech-skeleton>\n <rolatech-skeleton class=\"col-start-3 col-span-2\"></rolatech-skeleton>\n <rolatech-skeleton class=\"col-start-6 col-span-1\"></rolatech-skeleton>\n</div>\n" }]
|
|
135
|
-
}] });
|
|
136
|
-
|
|
137
122
|
class InvoiceItemSkeleton {
|
|
138
123
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: InvoiceItemSkeleton, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
139
124
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.1", 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 }); }
|
|
@@ -147,7 +132,6 @@ class InvoiceIndexComponent extends BaseComponent {
|
|
|
147
132
|
constructor() {
|
|
148
133
|
super(...arguments);
|
|
149
134
|
this.invoiceService = inject(InvoiceService);
|
|
150
|
-
this.title = inject(Title);
|
|
151
135
|
this.filterOptions = {
|
|
152
136
|
type: '',
|
|
153
137
|
status: '',
|
|
@@ -183,6 +167,21 @@ class InvoiceIndexComponent extends BaseComponent {
|
|
|
183
167
|
this.pageSize = 15;
|
|
184
168
|
this.pageSizeOptions = [5, 10, 25, 100];
|
|
185
169
|
this.pageIndex = signal(0, ...(ngDevMode ? [{ debugName: "pageIndex" }] : []));
|
|
170
|
+
this.pageMeta = {
|
|
171
|
+
baseLink: this.readRouteData('invoiceBaseLink', '/invoices'),
|
|
172
|
+
eyebrow: this.readRouteData('invoiceEyebrow', 'Billing workspace'),
|
|
173
|
+
title: this.readRouteData('invoiceCollectionTitle', 'Invoices'),
|
|
174
|
+
description: this.readRouteData('invoiceCollectionDescription', 'Track invoice status, total value, and due dates in one calm workspace.'),
|
|
175
|
+
};
|
|
176
|
+
this.stats = computed(() => {
|
|
177
|
+
const invoices = this.invoices();
|
|
178
|
+
return [
|
|
179
|
+
{ label: 'Invoices', value: this.length || invoices.length, hint: 'Every invoice in the current scope' },
|
|
180
|
+
{ label: 'Created', value: invoices.filter((item) => item.status === InvoiceStatus.CREATED).length, hint: 'Drafted and waiting to move forward' },
|
|
181
|
+
{ label: 'Issued', value: invoices.filter((item) => item.status === InvoiceStatus.ISSUED || item.status === InvoiceStatus.SENT).length, hint: 'Sent and awaiting payment' },
|
|
182
|
+
{ label: 'Paid', value: invoices.filter((item) => item.status === InvoiceStatus.PAID).length, hint: 'Successfully settled' },
|
|
183
|
+
];
|
|
184
|
+
}, ...(ngDevMode ? [{ debugName: "stats" }] : []));
|
|
186
185
|
}
|
|
187
186
|
ngOnInit() {
|
|
188
187
|
const sub = this.route.queryParamMap
|
|
@@ -190,14 +189,25 @@ class InvoiceIndexComponent extends BaseComponent {
|
|
|
190
189
|
const page = p.get('page') ? Number(p.get('page')) : 1;
|
|
191
190
|
this.pageIndex.set(Math.max(page - 1, 0));
|
|
192
191
|
const status = p.get('status') || undefined;
|
|
193
|
-
|
|
192
|
+
const type = p.get('type') || undefined;
|
|
193
|
+
this.filterOptions = {
|
|
194
|
+
type: type || '',
|
|
195
|
+
status: status || '',
|
|
196
|
+
};
|
|
197
|
+
const filters = [];
|
|
194
198
|
if (status) {
|
|
195
199
|
this.select = this.links.findIndex((item) => item.status === status);
|
|
196
|
-
|
|
200
|
+
filters.push(`status:${status.toUpperCase()}`);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
this.select = 0;
|
|
204
|
+
}
|
|
205
|
+
if (type) {
|
|
206
|
+
filters.push(`type:${type}`);
|
|
197
207
|
}
|
|
198
208
|
return {
|
|
199
209
|
page,
|
|
200
|
-
filter,
|
|
210
|
+
filter: filters.join(','),
|
|
201
211
|
limit: p.get('limit') ? Number(p.get('limit')) : 15,
|
|
202
212
|
sort: p.get('sort') || undefined,
|
|
203
213
|
};
|
|
@@ -223,33 +233,30 @@ class InvoiceIndexComponent extends BaseComponent {
|
|
|
223
233
|
this.destroyRef.onDestroy(() => sub.unsubscribe());
|
|
224
234
|
}
|
|
225
235
|
find() {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
options['filter'] = filterString;
|
|
232
|
-
}
|
|
233
|
-
this.invoiceService.me(options).subscribe({
|
|
234
|
-
next: (res) => {
|
|
235
|
-
this.invoices.set(res.data);
|
|
236
|
+
this.router.navigate([], {
|
|
237
|
+
queryParams: {
|
|
238
|
+
type: this.filterOptions.type || null,
|
|
239
|
+
status: this.filterOptions.status || null,
|
|
240
|
+
page: 1,
|
|
236
241
|
},
|
|
242
|
+
queryParamsHandling: 'merge',
|
|
243
|
+
replaceUrl: true,
|
|
237
244
|
});
|
|
238
245
|
}
|
|
239
246
|
resetFilter() {
|
|
240
247
|
this.filterOptions = {
|
|
241
248
|
type: '',
|
|
249
|
+
status: '',
|
|
242
250
|
};
|
|
243
|
-
this.
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
})
|
|
252
|
-
.join(',');
|
|
251
|
+
this.router.navigate([], {
|
|
252
|
+
queryParams: {
|
|
253
|
+
type: null,
|
|
254
|
+
status: null,
|
|
255
|
+
page: 1,
|
|
256
|
+
},
|
|
257
|
+
queryParamsHandling: 'merge',
|
|
258
|
+
replaceUrl: true,
|
|
259
|
+
});
|
|
253
260
|
}
|
|
254
261
|
statusCompareFn(t1, t2) {
|
|
255
262
|
return t1 === t2;
|
|
@@ -261,36 +268,33 @@ class InvoiceIndexComponent extends BaseComponent {
|
|
|
261
268
|
replaceUrl: true, // optional: avoid stacking history on every page click
|
|
262
269
|
});
|
|
263
270
|
}
|
|
271
|
+
readRouteData(key, fallback) {
|
|
272
|
+
for (const snapshot of [...this.route.snapshot.pathFromRoot].reverse()) {
|
|
273
|
+
const value = snapshot.data?.[key];
|
|
274
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
275
|
+
return value;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return fallback;
|
|
279
|
+
}
|
|
264
280
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: InvoiceIndexComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
265
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: InvoiceIndexComponent, isStandalone: true, selector: "rolatech-invoice-index", usesInheritance: true, ngImport: i0, template: "<rolatech-
|
|
281
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", 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 }); }
|
|
266
282
|
}
|
|
267
283
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: InvoiceIndexComponent, decorators: [{
|
|
268
284
|
type: Component,
|
|
269
285
|
args: [{ selector: 'rolatech-invoice-index', imports: [
|
|
270
|
-
ContainerComponent,
|
|
271
286
|
RouterModule,
|
|
272
|
-
TabsComponent,
|
|
273
|
-
TabComponent,
|
|
274
|
-
ToolbarComponent,
|
|
275
|
-
ListComponent,
|
|
276
287
|
InvoiceItem,
|
|
277
|
-
EmptyComponent,
|
|
278
288
|
MatButtonModule,
|
|
279
289
|
MatIconModule,
|
|
280
290
|
FormsModule,
|
|
281
291
|
MatFormFieldModule,
|
|
282
|
-
MatDatepickerModule,
|
|
283
|
-
MatOptionModule,
|
|
284
|
-
MatInputModule,
|
|
285
292
|
MatSelectModule,
|
|
286
|
-
MatButtonModule,
|
|
287
|
-
FilterComponent,
|
|
288
293
|
KeyValuePipe,
|
|
289
|
-
InvoiceHeader,
|
|
290
294
|
MatPaginatorModule,
|
|
291
|
-
InvoiceHeaderSkeleton,
|
|
292
295
|
InvoiceItemSkeleton,
|
|
293
|
-
|
|
296
|
+
PageCollectionShellComponent,
|
|
297
|
+
], encapsulation: ViewEncapsulation.None, 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"] }]
|
|
294
298
|
}] });
|
|
295
299
|
|
|
296
300
|
class InvoiceUser {
|
|
@@ -378,6 +382,11 @@ class InvoiceDetailComponent extends BaseComponent {
|
|
|
378
382
|
this.status = InvoiceStatus;
|
|
379
383
|
this.paying = false;
|
|
380
384
|
this.loading = signal(true, ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
385
|
+
this.pageMeta = {
|
|
386
|
+
baseLink: this.readRouteData('invoiceBaseLink', '/invoices'),
|
|
387
|
+
collectionTitle: this.readRouteData('invoiceCollectionTitle', 'Invoices'),
|
|
388
|
+
eyebrow: this.readRouteData('invoiceEyebrow', 'Billing workspace'),
|
|
389
|
+
};
|
|
381
390
|
}
|
|
382
391
|
ngOnInit() {
|
|
383
392
|
this.route.params.subscribe((params) => {
|
|
@@ -445,23 +454,47 @@ class InvoiceDetailComponent extends BaseComponent {
|
|
|
445
454
|
},
|
|
446
455
|
});
|
|
447
456
|
}
|
|
457
|
+
statusToneClass(status) {
|
|
458
|
+
switch (status) {
|
|
459
|
+
case InvoiceStatus.PAID:
|
|
460
|
+
return 'invoice-detail-page__status invoice-detail-page__status--paid';
|
|
461
|
+
case InvoiceStatus.ISSUED:
|
|
462
|
+
case InvoiceStatus.SENT:
|
|
463
|
+
case InvoiceStatus.PARTIALLY_PAID:
|
|
464
|
+
return 'invoice-detail-page__status invoice-detail-page__status--issued';
|
|
465
|
+
case InvoiceStatus.VOID:
|
|
466
|
+
return 'invoice-detail-page__status invoice-detail-page__status--void';
|
|
467
|
+
default:
|
|
468
|
+
return 'invoice-detail-page__status invoice-detail-page__status--neutral';
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
readRouteData(key, fallback) {
|
|
472
|
+
for (const snapshot of [...this.route.snapshot.pathFromRoot].reverse()) {
|
|
473
|
+
const value = snapshot.data?.[key];
|
|
474
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
475
|
+
return value;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return fallback;
|
|
479
|
+
}
|
|
448
480
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: InvoiceDetailComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
449
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: InvoiceDetailComponent, isStandalone: true, selector: "rolatech-invoice-detail", usesInheritance: true, ngImport: i0, template: "<
|
|
481
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", 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" }] }); }
|
|
450
482
|
}
|
|
451
483
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: InvoiceDetailComponent, decorators: [{
|
|
452
484
|
type: Component,
|
|
453
485
|
args: [{ selector: 'rolatech-invoice-detail', imports: [
|
|
486
|
+
CommonModule,
|
|
487
|
+
RouterModule,
|
|
454
488
|
MatButtonModule,
|
|
455
489
|
MatIconModule,
|
|
456
|
-
ContainerComponent,
|
|
457
|
-
ToolbarComponent,
|
|
458
490
|
InvoiceUser,
|
|
459
491
|
InvoiceLines,
|
|
460
492
|
InvoiceSummary,
|
|
461
493
|
InvoiceUserSkeleton,
|
|
462
494
|
InvoiceSummarySkeleton,
|
|
463
495
|
InvoiceLinesSkeleton,
|
|
464
|
-
|
|
496
|
+
PricePipe,
|
|
497
|
+
], 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"] }]
|
|
465
498
|
}] });
|
|
466
499
|
|
|
467
500
|
const invoiceRoutes = [
|
|
@@ -584,7 +617,7 @@ class InvoiceManageIndex extends BaseComponent {
|
|
|
584
617
|
}
|
|
585
618
|
}
|
|
586
619
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: InvoiceManageIndex, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
587
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", 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:
|
|
620
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", 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" }] }); }
|
|
588
621
|
}
|
|
589
622
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: InvoiceManageIndex, decorators: [{
|
|
590
623
|
type: Component,
|
|
@@ -682,7 +715,7 @@ class InvoiceManageCreate {
|
|
|
682
715
|
return new Date().toISOString().slice(0, 10);
|
|
683
716
|
}
|
|
684
717
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: InvoiceManageCreate, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
685
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: InvoiceManageCreate, isStandalone: true, selector: "rolatech-invoice-manage-create", ngImport: i0, template: "<rolatech-toolbar title=\"Create Invoice\">\n <button\n mat-flat-button\n color=\"primary\"\n (click)=\"save(invoiceForm)\"\n [disabled]=\"!invoiceForm.form.valid || invoice.lines.length === 0\"\n >\n Save\n </button>\n</rolatech-toolbar>\n<div class=\"p-4 space-y-6\">\n <form #invoiceForm=\"ngForm\" novalidate>\n <mat-card appearance=\"outlined\" class=\"p-4\">\n <div class=\"grid lg:grid-cols-2 gap-4\">\n <mat-form-field appearance=\"fill\">\n <mat-label>Customer name</mat-label>\n <input matInput name=\"customerName\" [(ngModel)]=\"invoice.customerName\" required />\n @if (invoiceForm.submitted && !invoice.customerName) {<mat-error>Name is required</mat-error>}\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Customer email</mat-label>\n <input matInput type=\"email\" name=\"customerEmail\" [(ngModel)]=\"invoice.customerEmail\" #email=\"ngModel\" email />\n @if (email.invalid && (email.dirty || email.touched)) {<mat-error>Invalid email</mat-error>}\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\" class=\"lg:col-span-2\">\n <mat-label>Customer address</mat-label>\n <textarea matInput rows=\"2\" name=\"customerAddress\" [(ngModel)]=\"invoice.customerAddress\"></textarea>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Currency</mat-label>\n <mat-select name=\"currency\" [(ngModel)]=\"invoice.currency\" required>\n <mat-option value=\"GBP\">GBP</mat-option>\n <mat-option value=\"USD\">USD</mat-option>\n <mat-option value=\"EUR\">EUR</mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Issue date</mat-label>\n <input matInput type=\"date\" name=\"issueDate\" [(ngModel)]=\"invoice.issueDate\" required />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Due date</mat-label>\n <input matInput type=\"date\" name=\"dueDate\" [(ngModel)]=\"invoice.dueDate\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\" class=\"lg:col-span-2\">\n <mat-label>Notes</mat-label>\n <textarea\n matInput\n rows=\"2\"\n name=\"notes\"\n [(ngModel)]=\"invoice.notes\"\n placeholder=\"Optional notes shown on the invoice\"\n ></textarea>\n </mat-form-field>\n </div>\n </mat-card>\n\n <mat-card appearance=\"outlined\" class=\"p-4 space-y-4 mt-4\">\n <div class=\"flex items-center justify-between\">\n <h2 class=\"text-xl font-medium\">Line items</h2>\n <button mat-stroked-button type=\"button\" (click)=\"addLine()\">\n <mat-icon>add</mat-icon>\n Add line\n </button>\n </div>\n\n <div class=\"hidden lg:grid grid-cols-12 gap-2 text-sm font-medium px-2\">\n <div class=\"col-span-3\">Type</div>\n <div class=\"col-span-3\">Description</div>\n <div class=\"col-span-1 text-right\">Qty</div>\n <div class=\"col-span-2 text-right\">Unit Price</div>\n <div class=\"col-span-2 text-right\">Tax %</div>\n <div class=\"col-span-1\"></div>\n </div>\n\n <div class=\"space-y-3\">\n @for ( line of invoice.lines; track line; let i = $index) {\n <div class=\"grid grid-cols-1 lg:grid-cols-12 gap-2 items-start\">\n <rolatech-enum-select\n class=\"lg:col-span-3\"\n [resource]=\"'billing'\"\n [enumName]=\"'InvoiceLineType'\"\n [label]=\"'Line type'\"\n [placeholder]=\"'Select\u2026'\"\n [(value)]=\"line.type\"\n ></rolatech-enum-select>\n <mat-form-field class=\"lg:col-span-3\" appearance=\"fill\">\n <mat-label>Description</mat-label>\n <input\n matInput\n [name]=\"'desc'+i\"\n [(ngModel)]=\"line.description\"\n required\n placeholder=\"e.g. Photo editing package\"\n />\n </mat-form-field>\n\n <mat-form-field class=\"lg:col-span-1\" appearance=\"fill\">\n <mat-label>Quantity</mat-label>\n <input matInput type=\"number\" min=\"1\" step=\"1\" [name]=\"'qty'+i\" [(ngModel)]=\"line.quantity\" required />\n </mat-form-field>\n\n <mat-form-field class=\"lg:col-span-2\" appearance=\"fill\">\n <mat-label>Unit Price</mat-label>\n <input matInput type=\"number\" min=\"0\" step=\"0.01\" [name]=\"'unit'+i\" [(ngModel)]=\"line.unitPrice\" required />\n <mat-hint>e.g. 99.99</mat-hint>\n </mat-form-field>\n\n <mat-form-field class=\"lg:col-span-2\" appearance=\"fill\">\n <mat-label>Tax %</mat-label>\n <input matInput type=\"number\" min=\"0\" step=\"0.01\" [name]=\"'tax'+i\" [(ngModel)]=\"line.taxRate\" />\n </mat-form-field>\n\n <div class=\"lg:col-span-1 flex items-center justify-end\">\n <button mat-icon-button color=\"warn\" type=\"button\" (click)=\"removeLine(i)\" aria-label=\"Remove line\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n </div>\n <mat-divider></mat-divider>\n }\n </div>\n\n <div class=\"flex flex-col items-end space-y-1\">\n <div class=\"text-sm\">\n Subtotal: <span class=\"font-medium\">{{ subtotalMajor() | number:'1.2-2' }} {{ invoice.currency }}</span>\n </div>\n <div class=\"text-sm\">\n Tax: <span class=\"font-medium\">{{ taxMajor() | number:'1.2-2' }} {{ invoice.currency }}</span>\n </div>\n <div class=\"text-lg font-semibold\">Total: {{ totalMajor() | number:'1.2-2' }} {{ invoice.currency }}</div>\n </div>\n </mat-card>\n </form>\n\n @if (lastResponse) {\n <mat-card appearance=\"outlined\" class=\"p-4 flex items-center justify-between\">\n <div>\n <div class=\"font-medium\">Created invoice #{{ lastResponse.data.id }}</div>\n <div class=\"text-sm opacity-70\">\n Total: {{ penceToMajor(lastResponse.data.total) | number:'1.2-2' }} {{ invoice.currency }}\n </div>\n </div>\n <div class=\"space-x-2\">\n <button mat-stroked-button (click)=\"resetForm()\">Create another</button>\n <button mat-flat-button color=\"primary\">Download PDF</button>\n </div>\n </mat-card>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i1$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.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: i7.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: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i8$1.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i9.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.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: InvoiceManageCreate, isStandalone: true, selector: "rolatech-invoice-manage-create", ngImport: i0, template: "<rolatech-toolbar title=\"Create Invoice\">\n <button\n mat-flat-button\n color=\"primary\"\n (click)=\"save(invoiceForm)\"\n [disabled]=\"!invoiceForm.form.valid || invoice.lines.length === 0\"\n >\n Save\n </button>\n</rolatech-toolbar>\n<div class=\"p-4 space-y-6\">\n <form #invoiceForm=\"ngForm\" novalidate>\n <mat-card appearance=\"outlined\" class=\"p-4\">\n <div class=\"grid lg:grid-cols-2 gap-4\">\n <mat-form-field appearance=\"fill\">\n <mat-label>Customer name</mat-label>\n <input matInput name=\"customerName\" [(ngModel)]=\"invoice.customerName\" required />\n @if (invoiceForm.submitted && !invoice.customerName) {<mat-error>Name is required</mat-error>}\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Customer email</mat-label>\n <input matInput type=\"email\" name=\"customerEmail\" [(ngModel)]=\"invoice.customerEmail\" #email=\"ngModel\" email />\n @if (email.invalid && (email.dirty || email.touched)) {<mat-error>Invalid email</mat-error>}\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\" class=\"lg:col-span-2\">\n <mat-label>Customer address</mat-label>\n <textarea matInput rows=\"2\" name=\"customerAddress\" [(ngModel)]=\"invoice.customerAddress\"></textarea>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Currency</mat-label>\n <mat-select name=\"currency\" [(ngModel)]=\"invoice.currency\" required>\n <mat-option value=\"GBP\">GBP</mat-option>\n <mat-option value=\"USD\">USD</mat-option>\n <mat-option value=\"EUR\">EUR</mat-option>\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Issue date</mat-label>\n <input matInput type=\"date\" name=\"issueDate\" [(ngModel)]=\"invoice.issueDate\" required />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Due date</mat-label>\n <input matInput type=\"date\" name=\"dueDate\" [(ngModel)]=\"invoice.dueDate\" />\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\" class=\"lg:col-span-2\">\n <mat-label>Notes</mat-label>\n <textarea\n matInput\n rows=\"2\"\n name=\"notes\"\n [(ngModel)]=\"invoice.notes\"\n placeholder=\"Optional notes shown on the invoice\"\n ></textarea>\n </mat-form-field>\n </div>\n </mat-card>\n\n <mat-card appearance=\"outlined\" class=\"p-4 space-y-4 mt-4\">\n <div class=\"flex items-center justify-between\">\n <h2 class=\"text-xl font-medium\">Line items</h2>\n <button mat-stroked-button type=\"button\" (click)=\"addLine()\">\n <mat-icon>add</mat-icon>\n Add line\n </button>\n </div>\n\n <div class=\"hidden lg:grid grid-cols-12 gap-2 text-sm font-medium px-2\">\n <div class=\"col-span-3\">Type</div>\n <div class=\"col-span-3\">Description</div>\n <div class=\"col-span-1 text-right\">Qty</div>\n <div class=\"col-span-2 text-right\">Unit Price</div>\n <div class=\"col-span-2 text-right\">Tax %</div>\n <div class=\"col-span-1\"></div>\n </div>\n\n <div class=\"space-y-3\">\n @for ( line of invoice.lines; track line; let i = $index) {\n <div class=\"grid grid-cols-1 lg:grid-cols-12 gap-2 items-start\">\n <rolatech-enum-select\n class=\"lg:col-span-3\"\n [resource]=\"'billing'\"\n [enumName]=\"'InvoiceLineType'\"\n [label]=\"'Line type'\"\n [placeholder]=\"'Select\u2026'\"\n [(value)]=\"line.type\"\n ></rolatech-enum-select>\n <mat-form-field class=\"lg:col-span-3\" appearance=\"fill\">\n <mat-label>Description</mat-label>\n <input\n matInput\n [name]=\"'desc'+i\"\n [(ngModel)]=\"line.description\"\n required\n placeholder=\"e.g. Photo editing package\"\n />\n </mat-form-field>\n\n <mat-form-field class=\"lg:col-span-1\" appearance=\"fill\">\n <mat-label>Quantity</mat-label>\n <input matInput type=\"number\" min=\"1\" step=\"1\" [name]=\"'qty'+i\" [(ngModel)]=\"line.quantity\" required />\n </mat-form-field>\n\n <mat-form-field class=\"lg:col-span-2\" appearance=\"fill\">\n <mat-label>Unit Price</mat-label>\n <input matInput type=\"number\" min=\"0\" step=\"0.01\" [name]=\"'unit'+i\" [(ngModel)]=\"line.unitPrice\" required />\n <mat-hint>e.g. 99.99</mat-hint>\n </mat-form-field>\n\n <mat-form-field class=\"lg:col-span-2\" appearance=\"fill\">\n <mat-label>Tax %</mat-label>\n <input matInput type=\"number\" min=\"0\" step=\"0.01\" [name]=\"'tax'+i\" [(ngModel)]=\"line.taxRate\" />\n </mat-form-field>\n\n <div class=\"lg:col-span-1 flex items-center justify-end\">\n <button mat-icon-button color=\"warn\" type=\"button\" (click)=\"removeLine(i)\" aria-label=\"Remove line\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n </div>\n <mat-divider></mat-divider>\n }\n </div>\n\n <div class=\"flex flex-col items-end space-y-1\">\n <div class=\"text-sm\">\n Subtotal: <span class=\"font-medium\">{{ subtotalMajor() | number:'1.2-2' }} {{ invoice.currency }}</span>\n </div>\n <div class=\"text-sm\">\n Tax: <span class=\"font-medium\">{{ taxMajor() | number:'1.2-2' }} {{ invoice.currency }}</span>\n </div>\n <div class=\"text-lg font-semibold\">Total: {{ totalMajor() | number:'1.2-2' }} {{ invoice.currency }}</div>\n </div>\n </mat-card>\n </form>\n\n @if (lastResponse) {\n <mat-card appearance=\"outlined\" class=\"p-4 flex items-center justify-between\">\n <div>\n <div class=\"font-medium\">Created invoice #{{ lastResponse.data.id }}</div>\n <div class=\"text-sm opacity-70\">\n Total: {{ penceToMajor(lastResponse.data.total) | number:'1.2-2' }} {{ invoice.currency }}\n </div>\n </div>\n <div class=\"space-x-2\">\n <button mat-stroked-button (click)=\"resetForm()\">Create another</button>\n <button mat-flat-button color=\"primary\">Download PDF</button>\n </div>\n </mat-card>\n }\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i1$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$2.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i4.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i7.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i8.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: EnumSelect, selector: "rolatech-enum-select", inputs: ["resource", "enumName", "label", "placeholder", "disabled", "value"], outputs: ["valueChange"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }] }); }
|
|
686
719
|
}
|
|
687
720
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: InvoiceManageCreate, decorators: [{
|
|
688
721
|
type: Component,
|
|
@@ -730,7 +763,7 @@ class InvoiceEdit extends BaseComponent {
|
|
|
730
763
|
deps: [MAT_DATE_LOCALE],
|
|
731
764
|
},
|
|
732
765
|
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS },
|
|
733
|
-
], 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: i5.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 }] }); }
|
|
766
|
+
], usesInheritance: true, ngImport: i0, template: "<div class=\"flex flex-col gap-2\">\n <div class=\"flex justify-between gap-3\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Firstname</mat-label>\n <input matInput [(ngModel)]=\"invoice().firstName\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Lastname</mat-label>\n <input matInput [(ngModel)]=\"invoice().lastName\" />\n </mat-form-field>\n </div>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Email</mat-label>\n <input matInput [(ngModel)]=\"invoice().email\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Phone</mat-label>\n <input matInput [(ngModel)]=\"invoice().phone\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Discount total</mat-label>\n <input matInput [(ngModel)]=\"invoice().discountAmount\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label>Total</mat-label>\n <input matInput [(ngModel)]=\"invoice().total\" />\n </mat-form-field>\n <!-- <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-label i18n>Move-in date</mat-label>\n <input\n matInput\n placeholder=\"Move-in date\"\n [min]=\"minDate\"\n [matDatepicker]=\"startDatePicker\"\n (focus)=\"startDatePicker.open()\"\n [(ngModel)]=\"invoice().startDate\"\n (dateInput)=\"invoice().startDate = $event.value.format('YYYY-MM-DD')\"\n required\n readonly\n />\n <mat-datepicker-toggle matIconPrefix [for]=\"startDatePicker\"></mat-datepicker-toggle>\n <mat-datepicker #startDatePicker></mat-datepicker>\n </mat-form-field> -->\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "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 }] }); }
|
|
734
767
|
}
|
|
735
768
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: InvoiceEdit, decorators: [{
|
|
736
769
|
type: Component,
|
|
@@ -1103,7 +1136,7 @@ class InvoiceManageLine {
|
|
|
1103
1136
|
}
|
|
1104
1137
|
saveLines() { }
|
|
1105
1138
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: InvoiceManageLine, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1106
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", 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: i5.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 }); }
|
|
1139
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", 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: i5$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "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 }); }
|
|
1107
1140
|
}
|
|
1108
1141
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: InvoiceManageLine, decorators: [{
|
|
1109
1142
|
type: Component,
|
|
@@ -1259,7 +1292,6 @@ class AgentInvoiceIndex extends BaseComponent {
|
|
|
1259
1292
|
constructor() {
|
|
1260
1293
|
super(...arguments);
|
|
1261
1294
|
this.invoiceService = inject(InvoiceService);
|
|
1262
|
-
this.title = inject(Title);
|
|
1263
1295
|
this.filterOptions = {
|
|
1264
1296
|
type: '',
|
|
1265
1297
|
status: '',
|
|
@@ -1276,39 +1308,46 @@ class AgentInvoiceIndex extends BaseComponent {
|
|
|
1276
1308
|
this.links = [
|
|
1277
1309
|
{
|
|
1278
1310
|
name: 'All',
|
|
1279
|
-
|
|
1311
|
+
status: '',
|
|
1280
1312
|
},
|
|
1281
1313
|
{
|
|
1282
1314
|
name: 'Created',
|
|
1283
|
-
icon: 'category',
|
|
1284
1315
|
status: 'created',
|
|
1285
1316
|
},
|
|
1286
1317
|
{
|
|
1287
1318
|
name: 'Issued',
|
|
1288
|
-
icon: 'category',
|
|
1289
1319
|
status: 'issued',
|
|
1290
1320
|
},
|
|
1291
1321
|
{
|
|
1292
1322
|
name: 'Paid',
|
|
1293
|
-
icon: 'category',
|
|
1294
1323
|
status: 'paid',
|
|
1295
1324
|
},
|
|
1296
1325
|
];
|
|
1297
|
-
this.
|
|
1326
|
+
this.pageMeta = {
|
|
1327
|
+
baseLink: this.readRouteData('invoiceBaseLink', '/invoices'),
|
|
1328
|
+
eyebrow: this.readRouteData('invoiceEyebrow', 'Agent workspace'),
|
|
1329
|
+
title: this.readRouteData('invoiceCollectionTitle', 'Listing invoices'),
|
|
1330
|
+
description: this.readRouteData('invoiceCollectionDescription', 'Monitor invoices tied to your listings, with billing status and payment movement visible at a glance.'),
|
|
1331
|
+
};
|
|
1332
|
+
this.stats = computed(() => {
|
|
1333
|
+
const invoices = this.invoices();
|
|
1334
|
+
return [
|
|
1335
|
+
{ label: 'Invoices', value: this.length || invoices.length, hint: 'Billing records in your agent workspace' },
|
|
1336
|
+
{ label: 'Created', value: invoices.filter((item) => item.status === InvoiceStatus.CREATED).length, hint: 'Prepared but not yet settled' },
|
|
1337
|
+
{ label: 'Issued', value: invoices.filter((item) => item.status === InvoiceStatus.ISSUED || item.status === InvoiceStatus.SENT).length, hint: 'Awaiting payment' },
|
|
1338
|
+
{ label: 'Paid', value: invoices.filter((item) => item.status === InvoiceStatus.PAID).length, hint: 'Completed payments' },
|
|
1339
|
+
];
|
|
1340
|
+
}, ...(ngDevMode ? [{ debugName: "stats" }] : []));
|
|
1298
1341
|
}
|
|
1299
1342
|
find() {
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
options['filter'] = filterString;
|
|
1306
|
-
}
|
|
1307
|
-
this.invoiceService.me(options).subscribe({
|
|
1308
|
-
next: (res) => {
|
|
1309
|
-
console.log(res);
|
|
1310
|
-
this.invoices = res.data;
|
|
1343
|
+
this.router.navigate([], {
|
|
1344
|
+
queryParams: {
|
|
1345
|
+
type: this.filterOptions.type || null,
|
|
1346
|
+
status: this.filterOptions.status || null,
|
|
1347
|
+
page: 1,
|
|
1311
1348
|
},
|
|
1349
|
+
queryParamsHandling: 'merge',
|
|
1350
|
+
replaceUrl: true,
|
|
1312
1351
|
});
|
|
1313
1352
|
}
|
|
1314
1353
|
ngOnInit() {
|
|
@@ -1317,14 +1356,25 @@ class AgentInvoiceIndex extends BaseComponent {
|
|
|
1317
1356
|
const page = p.get('page') ? Number(p.get('page')) : 1;
|
|
1318
1357
|
this.pageIndex.set(Math.max(page - 1, 0));
|
|
1319
1358
|
const status = p.get('status') || undefined;
|
|
1320
|
-
|
|
1359
|
+
const type = p.get('type') || undefined;
|
|
1360
|
+
this.filterOptions = {
|
|
1361
|
+
type: type || '',
|
|
1362
|
+
status: status || '',
|
|
1363
|
+
};
|
|
1364
|
+
const filters = [];
|
|
1321
1365
|
if (status) {
|
|
1322
1366
|
this.select = this.links.findIndex((item) => item.status === status);
|
|
1323
|
-
|
|
1367
|
+
filters.push(`status:${status.toUpperCase()}`);
|
|
1368
|
+
}
|
|
1369
|
+
else {
|
|
1370
|
+
this.select = 0;
|
|
1371
|
+
}
|
|
1372
|
+
if (type) {
|
|
1373
|
+
filters.push(`type:${type}`);
|
|
1324
1374
|
}
|
|
1325
1375
|
return {
|
|
1326
1376
|
page,
|
|
1327
|
-
filter,
|
|
1377
|
+
filter: filters.join(','),
|
|
1328
1378
|
limit: p.get('limit') ? Number(p.get('limit')) : 15,
|
|
1329
1379
|
sort: p.get('sort') || undefined,
|
|
1330
1380
|
};
|
|
@@ -1358,63 +1408,46 @@ class AgentInvoiceIndex extends BaseComponent {
|
|
|
1358
1408
|
resetFilter() {
|
|
1359
1409
|
this.filterOptions = {
|
|
1360
1410
|
type: '',
|
|
1411
|
+
status: '',
|
|
1361
1412
|
};
|
|
1362
|
-
this.
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
})
|
|
1371
|
-
.join(',');
|
|
1413
|
+
this.router.navigate([], {
|
|
1414
|
+
queryParams: {
|
|
1415
|
+
type: null,
|
|
1416
|
+
status: null,
|
|
1417
|
+
page: 1,
|
|
1418
|
+
},
|
|
1419
|
+
queryParamsHandling: 'merge',
|
|
1420
|
+
replaceUrl: true,
|
|
1421
|
+
});
|
|
1372
1422
|
}
|
|
1373
1423
|
statusCompareFn(t1, t2) {
|
|
1374
1424
|
return t1 === t2;
|
|
1375
1425
|
}
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
case 'PAID':
|
|
1383
|
-
return 'bg-emerald-100 text-emerald-800';
|
|
1384
|
-
case 'VOID':
|
|
1385
|
-
return 'bg-red-100 text-red-800';
|
|
1386
|
-
default:
|
|
1387
|
-
return 'bg-gray-100 text-gray-700';
|
|
1426
|
+
readRouteData(key, fallback) {
|
|
1427
|
+
for (const snapshot of [...this.route.snapshot.pathFromRoot].reverse()) {
|
|
1428
|
+
const value = snapshot.data?.[key];
|
|
1429
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
1430
|
+
return value;
|
|
1431
|
+
}
|
|
1388
1432
|
}
|
|
1433
|
+
return fallback;
|
|
1389
1434
|
}
|
|
1390
1435
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: AgentInvoiceIndex, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
1391
|
-
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: "<rolatech-container>\n <rolatech-toolbar title=\"Invoices\" large>\n <button mat-button (click)=\"filter = !filter\">\n <span>Filter</span>\n <mat-icon>tune</mat-icon>\n </button>\n </rolatech-toolbar>\n <rolatech-filter>\n <div class=\"collapsed\" [class.expanded]=\"filter\">\n <div\n class=\"min-w-[256px] md:min-w-[320px] px-3 h-full flex flex-row md:flex-col md:h-full items-center md:items-start shadow-inner shadow-light-400 md:shadow-none overflow-x-scroll overflow-y-hidden scrollbar-hide whitespace-pre\"\n >\n <div class=\"flex items-center gap-3 mt-2\">\n <mat-form-field appearance=\"fill\" subscriptSizing=\"dynamic\">\n <mat-select name=\"type\" placeholder=\"Type\" [(ngModel)]=\"filterOptions.type\">\n @for (type of invoiceType | keyvalue; track type) {\n <mat-option [value]=\"type.key\"> {{ type.value }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <mat-form-field subscriptSizing=\"dynamic\">\n <mat-select [compareWith]=\"statusCompareFn\" placeholder=\"Status\" [(ngModel)]=\"filterOptions.status\">\n @for (status of invoiceStatus | keyvalue; track status) {\n <mat-option [value]=\"status.key\"> {{ status.value }} </mat-option>\n }\n </mat-select>\n </mat-form-field>\n <div>\n <button mat-flat-button (click)=\"find()\">Search</button>\n <button mat-stroked-button (click)=\"resetFilter()\" class=\"ml-3\">Reset</button>\n </div>\n </div>\n </div>\n </div>\n </rolatech-filter>\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 <rolatech-list>\n @if (invoices()) {\n <rolatech-invoice-header></rolatech-invoice-header>\n @for (item of invoices(); track item) {\n <rolatech-invoice-item class=\"cursor-pointer\" [routerLink]=\"['./', item.id]\" [invoice]=\"item\"></rolatech-invoice-item>\n } } @else {\n <rolatech-empty></rolatech-empty>\n }\n </rolatech-list>\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</rolatech-container>\n", styles: [".collapsed{max-height:0;overflow:hidden;transition:max-height .5s cubic-bezier(.4,0,.2,1)}.expanded{max-height:1000px}\n"], dependencies: [{ kind: "component", type: ContainerComponent, selector: "rolatech-container" }, { 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: TabsComponent, selector: "rolatech-tabs", inputs: ["select", "loading", "block", "mode"], outputs: ["selectChange"] }, { kind: "component", type: TabComponent, selector: "rolatech-tab", inputs: ["label"], outputs: ["selectRequested"] }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "component", type: ListComponent, selector: "rolatech-list" }, { kind: "component", type: InvoiceItem, selector: "rolatech-invoice-item", inputs: ["invoice"], outputs: ["download"] }, { kind: "component", type: EmptyComponent, selector: "rolatech-empty" }, { 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: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { 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: "ngmodule", type: MatDatepickerModule }, { kind: "ngmodule", type: MatOptionModule }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i7.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: FilterComponent, selector: "rolatech-filter" }, { kind: "component", type: InvoiceHeader, selector: "rolatech-invoice-header" }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i8.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }] }); }
|
|
1436
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: AgentInvoiceIndex, isStandalone: true, selector: "rolatech-agent-invoice-index", usesInheritance: true, ngImport: i0, template: "<section class=\"invoice-index-page\">\n <section class=\"invoice-index-page__hero\">\n <div class=\"invoice-index-page__copy\">\n <span class=\"invoice-index-page__eyebrow\">{{ pageMeta.eyebrow }}</span>\n <h1 class=\"invoice-index-page__title\">{{ pageMeta.title }}</h1>\n <p class=\"invoice-index-page__description\">{{ pageMeta.description }}</p>\n </div>\n\n <div class=\"invoice-index-page__stats\">\n @for (stat of stats(); track stat.label) {\n <article class=\"invoice-index-page__stat\">\n <span class=\"invoice-index-page__stat-value\">{{ stat.value }}</span>\n <span class=\"invoice-index-page__stat-label\">{{ stat.label }}</span>\n <span class=\"invoice-index-page__stat-hint\">{{ stat.hint }}</span>\n </article>\n }\n </div>\n </section>\n\n <section class=\"invoice-index-page__filters\">\n <nav class=\"invoice-index-page__status-nav\" aria-label=\"Invoice status filters\">\n @for (item of links; track item.name; let index = $index) {\n <a\n class=\"invoice-index-page__status-link\"\n [class.invoice-index-page__status-link--active]=\"select === index\"\n routerLink=\"./\"\n [queryParams]=\"item.status ? { status: item.status } : {}\"\n >\n {{ item.name }}\n </a>\n }\n </nav>\n\n <div class=\"invoice-index-page__filter-grid\">\n <mat-form-field appearance=\"fill\">\n <mat-label>Type</mat-label>\n <mat-select name=\"type\" [(ngModel)]=\"filterOptions.type\">\n @for (type of invoiceType | keyvalue; track type.key) {\n <mat-option [value]=\"type.key\">{{ type.value }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <mat-form-field appearance=\"fill\">\n <mat-label>Status</mat-label>\n <mat-select [compareWith]=\"statusCompareFn\" [(ngModel)]=\"filterOptions.status\">\n @for (status of invoiceStatus | keyvalue; track status.key) {\n <mat-option [value]=\"status.key\">{{ status.value }}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n\n <div class=\"invoice-index-page__filter-actions\">\n <button mat-flat-button (click)=\"find()\">Apply filters</button>\n <button mat-stroked-button (click)=\"resetFilter()\">Reset</button>\n </div>\n </div>\n </section>\n\n <section class=\"invoice-index-page__list\">\n @if (loading) {\n @for (dummy of [0, 1, 2, 3]; track dummy) {\n <rolatech-invoice-item-skeleton></rolatech-invoice-item-skeleton>\n }\n } @else if (invoices().length > 0) {\n @for (item of invoices(); track item.id) {\n <rolatech-invoice-item class=\"cursor-pointer\" [routerLink]=\"['./', item.id]\" [invoice]=\"item\"></rolatech-invoice-item>\n }\n } @else {\n <div class=\"invoice-index-page__empty\">\n <h3>No invoices yet</h3>\n <p>The invoices for this workspace will appear here once billing is created.</p>\n </div>\n }\n\n <mat-paginator\n #paginator\n [length]=\"length\"\n [pageSize]=\"pageSize\"\n [pageIndex]=\"pageIndex()\"\n [pageSizeOptions]=\"pageSizeOptions\"\n (page)=\"onPage($event)\"\n hidePageSize\n showFirstLastButtons\n >\n </mat-paginator>\n </section>\n</section>\n", styles: [".collapsed{max-height:0;overflow:hidden;transition:max-height .5s cubic-bezier(.4,0,.2,1)}.expanded{max-height:1000px}:host{display:block;padding:1rem}.invoice-index-page{display:flex;flex-direction:column;gap:1rem}.invoice-index-page__hero,.invoice-index-page__filters,.invoice-index-page__list{border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.5rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 96%,transparent);box-shadow:0 20px 48px -42px color-mix(in srgb,var(--rt-text-primary) 22%,transparent)}.invoice-index-page__hero{display:grid;gap:1rem;padding:1.25rem;background:radial-gradient(circle at top left,color-mix(in srgb,var(--rt-brand-color) 14%,transparent),transparent 48%),linear-gradient(135deg,color-mix(in srgb,var(--rt-raised-background, #ffffff) 94%,transparent),color-mix(in srgb,var(--rt-base-background, #ffffff) 88%,var(--rt-brand-color) 12%)),var(--rt-base-background, #ffffff)}.invoice-index-page__copy{display:flex;flex-direction:column;gap:.75rem}.invoice-index-page__eyebrow{display:inline-flex;align-self:flex-start;border-radius:9999px;padding:.38rem .72rem;background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color);font-size:.74rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}.invoice-index-page__title{margin:0;color:var(--rt-text-primary);font-size:clamp(1.8rem,2.8vw,2.7rem);line-height:1.05;font-weight:800}.invoice-index-page__description{margin:0;color:var(--rt-text-secondary);line-height:1.7}.invoice-index-page__stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.85rem}.invoice-index-page__stat{display:flex;flex-direction:column;gap:.2rem;padding:.95rem 1rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.1rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 88%,var(--rt-brand-color) 12%)}.invoice-index-page__stat-value{color:var(--rt-text-primary);font-size:1.2rem;font-weight:700}.invoice-index-page__stat-label,.invoice-index-page__stat-hint{color:var(--rt-text-secondary);font-size:.84rem}.invoice-index-page__filters,.invoice-index-page__list{display:flex;flex-direction:column;gap:1rem;padding:1rem 1.1rem}.invoice-index-page__status-nav{display:flex;flex-wrap:wrap;gap:.65rem}.invoice-index-page__status-link{display:inline-flex;align-items:center;justify-content:center;min-height:2.75rem;border:1px solid var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:9999px;padding:.55rem .95rem;color:var(--rt-text-secondary);font-weight:600;text-decoration:none}.invoice-index-page__status-link:hover,.invoice-index-page__status-link--active{border-color:color-mix(in srgb,var(--rt-brand-color) 24%,var(--rt-border-color, rgba(15, 23, 42, .08)));background:color-mix(in srgb,var(--rt-brand-color) 12%,transparent);color:var(--rt-brand-color)}.invoice-index-page__filter-grid{display:grid;gap:.85rem}.invoice-index-page__filter-grid mat-form-field{width:100%}.invoice-index-page__filter-actions{display:flex;flex-wrap:wrap;gap:.75rem}.invoice-index-page__empty{display:flex;flex-direction:column;justify-content:center;min-height:10rem;gap:.45rem;padding:1.25rem;border:1px dashed var(--rt-border-color, rgba(15, 23, 42, .08));border-radius:1.35rem;background:color-mix(in srgb,var(--rt-raised-background, #ffffff) 92%,var(--rt-brand-color) 8%)}.invoice-index-page__empty h3{margin:0;color:var(--rt-text-primary);font-size:1.05rem;font-weight:700}.invoice-index-page__empty p{margin:0;color:var(--rt-text-secondary);line-height:1.65}@media(min-width:900px){.invoice-index-page__hero{grid-template-columns:minmax(0,1.6fr) minmax(19rem,.95fr);align-items:start}.invoice-index-page__filter-grid{grid-template-columns:repeat(3,minmax(0,1fr));align-items:start}}@media(min-width:768px){:host{padding:1.25rem}}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: InvoiceItem, selector: "rolatech-invoice-item", inputs: ["invoice"], outputs: ["download"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i6.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "component", type: InvoiceItemSkeleton, selector: "rolatech-invoice-item-skeleton" }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }] }); }
|
|
1392
1437
|
}
|
|
1393
1438
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: AgentInvoiceIndex, decorators: [{
|
|
1394
1439
|
type: Component,
|
|
1395
1440
|
args: [{ selector: 'rolatech-agent-invoice-index', imports: [
|
|
1396
|
-
ContainerComponent,
|
|
1397
1441
|
RouterModule,
|
|
1398
|
-
TabsComponent,
|
|
1399
|
-
TabComponent,
|
|
1400
|
-
ToolbarComponent,
|
|
1401
|
-
ListComponent,
|
|
1402
1442
|
InvoiceItem,
|
|
1403
|
-
EmptyComponent,
|
|
1404
1443
|
MatButtonModule,
|
|
1405
|
-
MatIconModule,
|
|
1406
1444
|
FormsModule,
|
|
1407
1445
|
MatFormFieldModule,
|
|
1408
|
-
MatDatepickerModule,
|
|
1409
|
-
MatOptionModule,
|
|
1410
|
-
MatInputModule,
|
|
1411
1446
|
MatSelectModule,
|
|
1412
|
-
MatButtonModule,
|
|
1413
|
-
FilterComponent,
|
|
1414
1447
|
KeyValuePipe,
|
|
1415
|
-
InvoiceHeader,
|
|
1416
1448
|
MatPaginatorModule,
|
|
1417
|
-
|
|
1449
|
+
InvoiceItemSkeleton,
|
|
1450
|
+
], 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"] }]
|
|
1418
1451
|
}] });
|
|
1419
1452
|
|
|
1420
1453
|
class AgentInvoiceDetail extends BaseComponent {
|
|
@@ -1426,6 +1459,11 @@ class AgentInvoiceDetail extends BaseComponent {
|
|
|
1426
1459
|
this.loading = signal(true, ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
1427
1460
|
this.invoice = signal(null, ...(ngDevMode ? [{ debugName: "invoice" }] : []));
|
|
1428
1461
|
this.status = InvoiceStatus;
|
|
1462
|
+
this.pageMeta = {
|
|
1463
|
+
baseLink: this.readRouteData('invoiceBaseLink', '/invoices'),
|
|
1464
|
+
collectionTitle: this.readRouteData('invoiceCollectionTitle', 'Invoices'),
|
|
1465
|
+
eyebrow: this.readRouteData('invoiceEyebrow', 'Agent workspace'),
|
|
1466
|
+
};
|
|
1429
1467
|
}
|
|
1430
1468
|
ngOnInit() {
|
|
1431
1469
|
this.getInvoice();
|
|
@@ -1476,8 +1514,31 @@ class AgentInvoiceDetail extends BaseComponent {
|
|
|
1476
1514
|
},
|
|
1477
1515
|
});
|
|
1478
1516
|
}
|
|
1517
|
+
statusToneClass(status) {
|
|
1518
|
+
switch (status) {
|
|
1519
|
+
case InvoiceStatus.PAID:
|
|
1520
|
+
return 'invoice-detail-page__status invoice-detail-page__status--paid';
|
|
1521
|
+
case InvoiceStatus.ISSUED:
|
|
1522
|
+
case InvoiceStatus.SENT:
|
|
1523
|
+
case InvoiceStatus.PARTIALLY_PAID:
|
|
1524
|
+
return 'invoice-detail-page__status invoice-detail-page__status--issued';
|
|
1525
|
+
case InvoiceStatus.VOID:
|
|
1526
|
+
return 'invoice-detail-page__status invoice-detail-page__status--void';
|
|
1527
|
+
default:
|
|
1528
|
+
return 'invoice-detail-page__status invoice-detail-page__status--neutral';
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
readRouteData(key, fallback) {
|
|
1532
|
+
for (const snapshot of [...this.route.snapshot.pathFromRoot].reverse()) {
|
|
1533
|
+
const value = snapshot.data?.[key];
|
|
1534
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
1535
|
+
return value;
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
return fallback;
|
|
1539
|
+
}
|
|
1479
1540
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: AgentInvoiceDetail, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
1480
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", type: AgentInvoiceDetail, isStandalone: true, selector: "rolatech-agent-invoice-detail", usesInheritance: true, ngImport: i0, template: "
|
|
1541
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.1", 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" }] }); }
|
|
1481
1542
|
}
|
|
1482
1543
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImport: i0, type: AgentInvoiceDetail, decorators: [{
|
|
1483
1544
|
type: Component,
|
|
@@ -1490,8 +1551,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.1", ngImpor
|
|
|
1490
1551
|
InvoiceSummary,
|
|
1491
1552
|
InvoiceLines,
|
|
1492
1553
|
InvoiceUser,
|
|
1493
|
-
|
|
1494
|
-
], template: "
|
|
1554
|
+
PricePipe,
|
|
1555
|
+
], 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"] }]
|
|
1495
1556
|
}] });
|
|
1496
1557
|
|
|
1497
1558
|
const agentInvoiceRoutes = [
|