@rolatech/angular-billing 20.2.6-beta.1 → 20.2.7-beta.1
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,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, output, Component, ViewEncapsulation, inject, signal, model } from '@angular/core';
|
|
2
|
+
import { input, output, Component, ViewEncapsulation, inject, signal, model, computed, 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';
|
|
@@ -19,7 +19,7 @@ import * as i7 from '@angular/material/select';
|
|
|
19
19
|
import { MatSelectModule } from '@angular/material/select';
|
|
20
20
|
import * as i1$1 from '@angular/router';
|
|
21
21
|
import { RouterModule, RouterLink } from '@angular/router';
|
|
22
|
-
import { Skeleton, BaseComponent, ContainerComponent, TabsComponent, TabComponent, ToolbarComponent, ListComponent, EmptyComponent, FilterComponent, MaterialModule } from '@rolatech/angular-components';
|
|
22
|
+
import { Skeleton, BaseComponent, ContainerComponent, TabsComponent, TabComponent, ToolbarComponent, ListComponent, EmptyComponent, FilterComponent, EnumSelect, ConfirmationDialogComponent, MaterialModule } from '@rolatech/angular-components';
|
|
23
23
|
import { PricePipe } from '@rolatech/angular-common';
|
|
24
24
|
import { InvoiceService, PaymentService } from '@rolatech/angular-services';
|
|
25
25
|
import { Title } from '@angular/platform-browser';
|
|
@@ -30,12 +30,32 @@ import * as i9 from '@angular/material/card';
|
|
|
30
30
|
import { MatCardModule } from '@angular/material/card';
|
|
31
31
|
import * as i8$1 from '@angular/material/divider';
|
|
32
32
|
import { MatDividerModule } from '@angular/material/divider';
|
|
33
|
-
import { MomentDateAdapter } from '@angular/material-moment-adapter';
|
|
34
33
|
import * as i3$1 from '@angular/material/menu';
|
|
35
34
|
import { MatMenuModule } from '@angular/material/menu';
|
|
35
|
+
import { MomentDateAdapter } from '@angular/material-moment-adapter';
|
|
36
|
+
import { finalize as finalize$1 } from 'rxjs/operators';
|
|
37
|
+
import * as i1$3 from '@angular/cdk/drag-drop';
|
|
38
|
+
import { DragDropModule } from '@angular/cdk/drag-drop';
|
|
39
|
+
import { MatDialog } from '@angular/material/dialog';
|
|
36
40
|
|
|
37
41
|
const billingRoutes = [];
|
|
38
42
|
|
|
43
|
+
var InvoiceVatMode;
|
|
44
|
+
(function (InvoiceVatMode) {
|
|
45
|
+
InvoiceVatMode["EXCLUSIVE"] = "EXCLUSIVE";
|
|
46
|
+
InvoiceVatMode["INCLUSIVE"] = "INCLUSIVE";
|
|
47
|
+
})(InvoiceVatMode || (InvoiceVatMode = {}));
|
|
48
|
+
var InvoiceLineType;
|
|
49
|
+
(function (InvoiceLineType) {
|
|
50
|
+
InvoiceLineType["PROPERTY_RENT"] = "PROPERTY_RENT";
|
|
51
|
+
InvoiceLineType["PROPERTY_SALE"] = "PROPERTY_SALE";
|
|
52
|
+
InvoiceLineType["HOLDING_DEPOSIT"] = "HOLDING_DEPOSIT";
|
|
53
|
+
InvoiceLineType["SECURITY_DEPOSIT"] = "SECURITY_DEPOSIT";
|
|
54
|
+
InvoiceLineType["DISCOUNT"] = "DISCOUNT";
|
|
55
|
+
InvoiceLineType["FEE"] = "FEE";
|
|
56
|
+
InvoiceLineType["ADJUSTMENT"] = "ADJUSTMENT";
|
|
57
|
+
InvoiceLineType["CUSTOM"] = "CUSTOM";
|
|
58
|
+
})(InvoiceLineType || (InvoiceLineType = {}));
|
|
39
59
|
var InvoiceType;
|
|
40
60
|
(function (InvoiceType) {
|
|
41
61
|
InvoiceType["FIRST_PAYMENT"] = "First Payment";
|
|
@@ -45,6 +65,7 @@ var InvoiceType;
|
|
|
45
65
|
var InvoiceStatus;
|
|
46
66
|
(function (InvoiceStatus) {
|
|
47
67
|
InvoiceStatus["DRAFT"] = "Draft";
|
|
68
|
+
InvoiceStatus["SENT"] = "Sent";
|
|
48
69
|
InvoiceStatus["CREATED"] = "Created";
|
|
49
70
|
InvoiceStatus["ISSUED"] = "Issued";
|
|
50
71
|
InvoiceStatus["PARTIALLY_PAID"] = "Partially paid";
|
|
@@ -75,10 +96,10 @@ class InvoiceItem {
|
|
|
75
96
|
return 'bg-gray-100 text-gray-700';
|
|
76
97
|
}
|
|
77
98
|
}
|
|
78
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
79
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.
|
|
99
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceItem, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
100
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", 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: "<div class=\"flex w-full justify-between p-3 cursor-pointer hover:bg-[--rt-raised-background]\">\n <div class=\"flex items-center\">\n <div class=\"w-[180px] max-w-[180px]\">{{invoice().id}}</div>\n </div>\n <div class=\"flex items-center gap-3\">\n <div>{{invoice().total | price}}</div>\n </div>\n <div class=\"flex items-center gap-3\">\n <div\n class=\"inline-flex items-center rounded-full px-3 py-1 text-xs font-medium\"\n [ngClass]=\"statusBadgeClass(invoice().status)\"\n >\n {{status[invoice().status]}}\n </div>\n <div class=\"w-[100px]\">{{invoice().createdAt | date:'dd/MM/yyyy':'Europe/London'}}</div>\n <button mat-icon-button class=\"px-2 min-w-[80px]\" (click)=\"onDownload();$event.stopPropagation()\">\n <mat-icon>download</mat-icon>\n </button>\n </div>\n</div>\n", styles: [".scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: MatButtonModule }, { 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: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }] }); }
|
|
80
101
|
}
|
|
81
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
102
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceItem, decorators: [{
|
|
82
103
|
type: Component,
|
|
83
104
|
args: [{ selector: 'rolatech-invoice-item', imports: [CommonModule, MatButtonModule, MatIcon, DatePipe, PricePipe], template: "<div class=\"flex w-full justify-between p-3 cursor-pointer hover:bg-[--rt-raised-background]\">\n <div class=\"flex items-center\">\n <div class=\"w-[180px] max-w-[180px]\">{{invoice().id}}</div>\n </div>\n <div class=\"flex items-center gap-3\">\n <div>{{invoice().total | price}}</div>\n </div>\n <div class=\"flex items-center gap-3\">\n <div\n class=\"inline-flex items-center rounded-full px-3 py-1 text-xs font-medium\"\n [ngClass]=\"statusBadgeClass(invoice().status)\"\n >\n {{status[invoice().status]}}\n </div>\n <div class=\"w-[100px]\">{{invoice().createdAt | date:'dd/MM/yyyy':'Europe/London'}}</div>\n <button mat-icon-button class=\"px-2 min-w-[80px]\" (click)=\"onDownload();$event.stopPropagation()\">\n <mat-icon>download</mat-icon>\n </button>\n </div>\n</div>\n", styles: [".scrollbar-hide::-webkit-scrollbar{display:none}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}\n"] }]
|
|
84
105
|
}], propDecorators: { invoice: [{ type: i0.Input, args: [{ isSignal: true, alias: "invoice", required: true }] }], download: [{ type: i0.Output, args: ["download"] }] } });
|
|
@@ -87,37 +108,37 @@ class InvoiceManageItem {
|
|
|
87
108
|
constructor() {
|
|
88
109
|
this.invoice = input.required(...(ngDevMode ? [{ debugName: "invoice" }] : []));
|
|
89
110
|
}
|
|
90
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
91
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.
|
|
111
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceManageItem, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
112
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: InvoiceManageItem, isStandalone: true, selector: "rolatech-invoice-manage-item", inputs: { invoice: { classPropertyName: "invoice", publicName: "invoice", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<p>invoice-manage-item works!</p>\n<div>{{invoice().id}}</div>\n", styles: [""] }); }
|
|
92
113
|
}
|
|
93
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
114
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceManageItem, decorators: [{
|
|
94
115
|
type: Component,
|
|
95
116
|
args: [{ selector: 'rolatech-invoice-manage-item', imports: [], template: "<p>invoice-manage-item works!</p>\n<div>{{invoice().id}}</div>\n" }]
|
|
96
117
|
}], propDecorators: { invoice: [{ type: i0.Input, args: [{ isSignal: true, alias: "invoice", required: true }] }] } });
|
|
97
118
|
|
|
98
119
|
class InvoiceHeader {
|
|
99
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
100
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.
|
|
120
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceHeader, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
121
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", 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: [""] }); }
|
|
101
122
|
}
|
|
102
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
123
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceHeader, decorators: [{
|
|
103
124
|
type: Component,
|
|
104
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" }]
|
|
105
126
|
}] });
|
|
106
127
|
|
|
107
128
|
class InvoiceHeaderSkeleton {
|
|
108
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
109
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.
|
|
129
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceHeaderSkeleton, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
130
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", 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 }); }
|
|
110
131
|
}
|
|
111
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
132
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceHeaderSkeleton, decorators: [{
|
|
112
133
|
type: Component,
|
|
113
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" }]
|
|
114
135
|
}] });
|
|
115
136
|
|
|
116
137
|
class InvoiceItemSkeleton {
|
|
117
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
118
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.
|
|
138
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceItemSkeleton, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
139
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", 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 }); }
|
|
119
140
|
}
|
|
120
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
141
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceItemSkeleton, decorators: [{
|
|
121
142
|
type: Component,
|
|
122
143
|
args: [{ selector: 'rolatech-invoice-item-skeleton', imports: [Skeleton], encapsulation: ViewEncapsulation.None, template: "<div class=\"grid grid-cols-6 place-content-center h-16 p-3 gap-3\">\n <rolatech-skeleton class=\"col-span-1 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"col-start-3 col-span-2 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"col-start-6 col-span-1 h-4\"></rolatech-skeleton>\n</div>\n" }]
|
|
123
144
|
}] });
|
|
@@ -240,10 +261,10 @@ class InvoiceIndexComponent extends BaseComponent {
|
|
|
240
261
|
replaceUrl: true, // optional: avoid stacking history on every page click
|
|
241
262
|
});
|
|
242
263
|
}
|
|
243
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
244
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.
|
|
264
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceIndexComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
265
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: InvoiceIndexComponent, isStandalone: true, selector: "rolatech-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\">\n {{ type.value }}\n </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\">\n {{ status.value }}\n </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) {\n @if (item.status) {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\"></rolatech-tab>\n }\n }\n </rolatech-tabs>\n <rolatech-list>\n @if (loading) {\n <rolatech-invoice-header-skeleton></rolatech-invoice-header-skeleton>\n @for (dummy of [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; track dummy) {\n <rolatech-invoice-item-skeleton></rolatech-invoice-item-skeleton>\n }\n } @else {\n @if (invoices()) {\n <rolatech-invoice-header></rolatech-invoice-header>\n @for (item of invoices(); track item) {\n <rolatech-invoice-item [routerLink]=\"['./', item.id]\" [invoice]=\"item\"></rolatech-invoice-item>\n }\n } @else {\n <rolatech-empty></rolatech-empty>\n }\n }\n </rolatech-list>\n @if (!loading) {\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 }\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"], outputs: ["selectChange"] }, { kind: "component", type: TabComponent, selector: "rolatech-tab", inputs: ["label"] }, { 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: "component", type: InvoiceHeaderSkeleton, selector: "rolatech-invoice-header-skeleton" }, { kind: "component", type: InvoiceItemSkeleton, selector: "rolatech-invoice-item-skeleton" }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
245
266
|
}
|
|
246
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
267
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceIndexComponent, decorators: [{
|
|
247
268
|
type: Component,
|
|
248
269
|
args: [{ selector: 'rolatech-invoice-index', imports: [
|
|
249
270
|
ContainerComponent,
|
|
@@ -279,10 +300,10 @@ class InvoiceUser {
|
|
|
279
300
|
this.email = input.required(...(ngDevMode ? [{ debugName: "email" }] : []));
|
|
280
301
|
this.phone = input.required(...(ngDevMode ? [{ debugName: "phone" }] : []));
|
|
281
302
|
}
|
|
282
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
283
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.
|
|
303
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceUser, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
304
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: InvoiceUser, isStandalone: true, selector: "rolatech-invoice-user", inputs: { firstName: { classPropertyName: "firstName", publicName: "firstName", isSignal: true, isRequired: true, transformFunction: null }, lastName: { classPropertyName: "lastName", publicName: "lastName", isSignal: true, isRequired: true, transformFunction: null }, email: { classPropertyName: "email", publicName: "email", isSignal: true, isRequired: true, transformFunction: null }, phone: { classPropertyName: "phone", publicName: "phone", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "id": "rolatech-invoice-user" }, classAttribute: "rolatech-invoice-user" }, ngImport: i0, template: "<div class=\"rounded-xl border border-[--rt-border-color] p-4 grid space-y-3\">\n <h2 class=\"text-sm font-semibold\">User</h2>\n <div class=\"text-sm\">\n <div class=\"font-medium\">{{ firstName() }}, {{ lastName() }}</div>\n <div class=\"font-medium\">{{ phone() }}</div>\n <div>{{ email() }}</div>\n </div>\n</div>\n", styles: [""], encapsulation: i0.ViewEncapsulation.None }); }
|
|
284
305
|
}
|
|
285
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
306
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceUser, decorators: [{
|
|
286
307
|
type: Component,
|
|
287
308
|
args: [{ selector: 'rolatech-invoice-user', imports: [], encapsulation: ViewEncapsulation.None, host: {
|
|
288
309
|
id: 'rolatech-invoice-user',
|
|
@@ -294,15 +315,13 @@ class InvoiceLines {
|
|
|
294
315
|
constructor() {
|
|
295
316
|
this.lines = input([], ...(ngDevMode ? [{ debugName: "lines" }] : []));
|
|
296
317
|
}
|
|
297
|
-
|
|
298
|
-
static { this.ɵ
|
|
318
|
+
add() { }
|
|
319
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceLines, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
320
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: InvoiceLines, isStandalone: true, selector: "rolatech-invoice-lines", inputs: { lines: { classPropertyName: "lines", publicName: "lines", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"rounded-xl border border-[--rt-border-color] p-4 grid space-y-3\">\n <div class=\"flex justify-between\">\n <h2 class=\"text-sm font-semibold\">Invoice lines</h2>\n </div>\n\n <div class=\"overflow-x-auto\">\n <table class=\"min-w-full text-sm border border-[--rt-border-color] rounded-lg\">\n <thead class=\"bg-[--rt-raised-background]\">\n <tr>\n <th class=\"px-3 py-2 text-left font-medium border-b border-[--rt-border-color]\">Title</th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-[--rt-border-color] w-28\">Unit (\u00A3)</th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-[--rt-border-color] w-20\">VAT %</th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-[--rt-border-color] w-28\">VAT (\u00A3)</th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-[--rt-border-color] w-32\">Total (\u00A3)</th>\n </tr>\n </thead>\n\n <tbody class=\"[&>tr:nth-child(even)]:bg-[--rt-raised-background]\">\n @if (lines().length > 0) { @for (line of lines(); track line.id) {\n <tr class=\"hover:bg-[--rt-raised-background] cursor-pointer\">\n <!-- Title -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color]\">\n <div class=\"font-medium\">{{ line.title }}</div>\n </td>\n\n <!-- Unit price -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-right text-[--rt-text-secondary]\">\n {{ line.unitPrice | price }}\n </td>\n\n <!-- VAT rate -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-right text-[--rt-text-secondary]\">\n {{ line.vatRate }}%\n </td>\n\n <!-- VAT amount -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-right text-[--rt-text-secondary]\">\n {{ line.vatAmount | price }}\n </td>\n\n <!-- Total -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-right font-semibold text-[--rt-text-secondary]\">\n {{ line.lineTotal | price }}\n </td>\n </tr>\n } } @else {\n <tr>\n <td [attr.colspan]=\"6\" class=\"px-3 py-4 text-center text-[--rt-text-secondary] text-sm\">No invoice lines</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n</div>\n", styles: [".cdk-drag-preview{box-shadow:0 10px 20px #00000026;border-radius:12px}.cdk-drag-placeholder{opacity:.3}\n"], dependencies: [{ kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
299
321
|
}
|
|
300
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
322
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceLines, decorators: [{
|
|
301
323
|
type: Component,
|
|
302
|
-
args: [{ selector: 'rolatech-invoice-lines', imports: [PricePipe], encapsulation: ViewEncapsulation.None,
|
|
303
|
-
id: 'rolatech-invoice-lines',
|
|
304
|
-
class: 'rolatech-invoice-lines',
|
|
305
|
-
}, template: "<div class=\"rounded-xl border border-[--rt-border-color] p-4 grid space-y-3\">\n <h2 class=\"text-sm font-semibold\">Invoice lines</h2>\n\n <div class=\"overflow-x-auto\">\n <table class=\"min-w-full text-sm border border-[--rt-border-color] rounded-lg\">\n <thead class=\"bg-[--rt-raised-background]\">\n <tr>\n <th class=\"px-3 py-2 text-left font-medium border-b border-[--rt-border-color]\">Item</th>\n <th class=\"px-3 py-2 text-center font-medium border-b border-[--rt-border-color] w-20\">Qty</th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-[--rt-border-color] w-28\">Unit (\u00A3)</th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-[--rt-border-color] w-20\">VAT %</th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-[--rt-border-color] w-28\">VAT (\u00A3)</th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-[--rt-border-color] w-32\">Total (\u00A3)</th>\n </tr>\n </thead>\n\n <tbody class=\"[&>tr:nth-child(even)]:bg-[--rt-raised-background]\">\n @if (lines().length > 0) { @for (line of lines(); track line.id) {\n <tr class=\"hover:bg-[--rt-raised-background] cursor-pointer\">\n <!-- Title -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color]\">\n <div class=\"font-medium\">{{ line.title }}</div>\n\n @if (line.subtitle) {\n <div class=\"text-xs\">{{ line.subtitle }}</div>\n }\n </td>\n\n <!-- Qty -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-center text-[--rt-text-secondary]\">\n {{ line.quantity }}\n </td>\n\n <!-- Unit price -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-right text-[--rt-text-secondary]\">\n {{ line.unitPrice | price }}\n </td>\n\n <!-- VAT rate -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-right text-[--rt-text-secondary]\">\n {{ line.vatRate }}%\n </td>\n\n <!-- VAT amount -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-right text-[--rt-text-secondary]\">\n {{ line.vatAmount | price }}\n </td>\n\n <!-- Total -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-right font-semibold text-[--rt-text-secondary]\">\n {{ line.lineTotal | price }}\n </td>\n </tr>\n } } @else {\n <tr>\n <td [attr.colspan]=\"6\" class=\"px-3 py-4 text-center text-[--rt-text-secondary] text-sm\">No invoice lines</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n</div>\n" }]
|
|
324
|
+
args: [{ selector: 'rolatech-invoice-lines', imports: [PricePipe], encapsulation: ViewEncapsulation.None, template: "<div class=\"rounded-xl border border-[--rt-border-color] p-4 grid space-y-3\">\n <div class=\"flex justify-between\">\n <h2 class=\"text-sm font-semibold\">Invoice lines</h2>\n </div>\n\n <div class=\"overflow-x-auto\">\n <table class=\"min-w-full text-sm border border-[--rt-border-color] rounded-lg\">\n <thead class=\"bg-[--rt-raised-background]\">\n <tr>\n <th class=\"px-3 py-2 text-left font-medium border-b border-[--rt-border-color]\">Title</th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-[--rt-border-color] w-28\">Unit (\u00A3)</th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-[--rt-border-color] w-20\">VAT %</th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-[--rt-border-color] w-28\">VAT (\u00A3)</th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-[--rt-border-color] w-32\">Total (\u00A3)</th>\n </tr>\n </thead>\n\n <tbody class=\"[&>tr:nth-child(even)]:bg-[--rt-raised-background]\">\n @if (lines().length > 0) { @for (line of lines(); track line.id) {\n <tr class=\"hover:bg-[--rt-raised-background] cursor-pointer\">\n <!-- Title -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color]\">\n <div class=\"font-medium\">{{ line.title }}</div>\n </td>\n\n <!-- Unit price -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-right text-[--rt-text-secondary]\">\n {{ line.unitPrice | price }}\n </td>\n\n <!-- VAT rate -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-right text-[--rt-text-secondary]\">\n {{ line.vatRate }}%\n </td>\n\n <!-- VAT amount -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-right text-[--rt-text-secondary]\">\n {{ line.vatAmount | price }}\n </td>\n\n <!-- Total -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-right font-semibold text-[--rt-text-secondary]\">\n {{ line.lineTotal | price }}\n </td>\n </tr>\n } } @else {\n <tr>\n <td [attr.colspan]=\"6\" class=\"px-3 py-4 text-center text-[--rt-text-secondary] text-sm\">No invoice lines</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n</div>\n", styles: [".cdk-drag-preview{box-shadow:0 10px 20px #00000026;border-radius:12px}.cdk-drag-placeholder{opacity:.3}\n"] }]
|
|
306
325
|
}], propDecorators: { lines: [{ type: i0.Input, args: [{ isSignal: true, alias: "lines", required: false }] }] } });
|
|
307
326
|
|
|
308
327
|
class InvoiceSummary {
|
|
@@ -312,10 +331,10 @@ class InvoiceSummary {
|
|
|
312
331
|
this.subtotal = input.required(...(ngDevMode ? [{ debugName: "subtotal" }] : []));
|
|
313
332
|
this.total = input.required(...(ngDevMode ? [{ debugName: "total" }] : []));
|
|
314
333
|
}
|
|
315
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
316
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.
|
|
334
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceSummary, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
335
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: InvoiceSummary, isStandalone: true, selector: "rolatech-invoice-summary", inputs: { tax: { classPropertyName: "tax", publicName: "tax", isSignal: true, isRequired: true, transformFunction: null }, credit: { classPropertyName: "credit", publicName: "credit", isSignal: true, isRequired: false, transformFunction: null }, subtotal: { classPropertyName: "subtotal", publicName: "subtotal", isSignal: true, isRequired: true, transformFunction: null }, total: { classPropertyName: "total", publicName: "total", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "id": "rolatech-invoice-summary" }, classAttribute: "rolatech-invoice-summary" }, ngImport: i0, template: "<div class=\"rounded-xl border border-[--rt-border-color] p-4 grid space-y-3\">\n <h2 class=\"text-sm font-semibold text-[--rt-text-secondary]\">Totals</h2>\n\n <dl class=\"space-y-2 text-sm\">\n <div class=\"flex justify-between\">\n <dt class=\"text-[--rt-text-secondary]\">Subtotal</dt>\n <dd class=\"font-medium\">{{ subtotal() | price }}</dd>\n </div>\n\n <div class=\"flex justify-between\">\n <dt class=\"text-[--rt-text-secondary]\">VAT</dt>\n <dd class=\"font-medium\">{{ tax() | price}}</dd>\n </div>\n\n @if (credit() > 0) {\n <div class=\"flex justify-between\">\n <dt class=\"text-[--rt-text-secondary]\">Less holding deposit</dt>\n <dd class=\"font-medium text-red-600\">\u2212{{ credit() | price }}</dd>\n </div>\n }\n\n <div class=\"border-t border-dashed border-[--rt-border-color] my-2\"></div>\n\n <div class=\"flex justify-between items-center\">\n <dt class=\"text-[--rt-text-secondary] font-semibold\">Total</dt>\n <dd class=\"text-lg font-semibold\">{{ total() | price }}</dd>\n </div>\n </dl>\n</div>\n", styles: [""], dependencies: [{ kind: "pipe", type: PricePipe, name: "price" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
317
336
|
}
|
|
318
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
337
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceSummary, decorators: [{
|
|
319
338
|
type: Component,
|
|
320
339
|
args: [{ selector: 'rolatech-invoice-summary', imports: [PricePipe], encapsulation: ViewEncapsulation.None, host: {
|
|
321
340
|
id: 'rolatech-invoice-summary',
|
|
@@ -324,28 +343,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
|
|
|
324
343
|
}], propDecorators: { tax: [{ type: i0.Input, args: [{ isSignal: true, alias: "tax", required: true }] }], credit: [{ type: i0.Input, args: [{ isSignal: true, alias: "credit", required: false }] }], subtotal: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtotal", required: true }] }], total: [{ type: i0.Input, args: [{ isSignal: true, alias: "total", required: true }] }] } });
|
|
325
344
|
|
|
326
345
|
class InvoiceUserSkeleton {
|
|
327
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
328
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.
|
|
346
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceUserSkeleton, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
347
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: InvoiceUserSkeleton, isStandalone: true, selector: "rolatech-invoice-user-skeleton", ngImport: i0, template: "<div class=\"rounded-xl border border-[--rt-border-color] p-4 grid space-y-3\">\n <rolatech-skeleton class=\"w-12 h-6\"></rolatech-skeleton>\n <div class=\"flex flex-col gap-2\">\n <rolatech-skeleton class=\"w-28 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-32 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-36 h-4\"></rolatech-skeleton>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: Skeleton, selector: "rolatech-skeleton" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
329
348
|
}
|
|
330
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
349
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceUserSkeleton, decorators: [{
|
|
331
350
|
type: Component,
|
|
332
351
|
args: [{ selector: 'rolatech-invoice-user-skeleton', imports: [Skeleton], encapsulation: ViewEncapsulation.None, template: "<div class=\"rounded-xl border border-[--rt-border-color] p-4 grid space-y-3\">\n <rolatech-skeleton class=\"w-12 h-6\"></rolatech-skeleton>\n <div class=\"flex flex-col gap-2\">\n <rolatech-skeleton class=\"w-28 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-32 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-36 h-4\"></rolatech-skeleton>\n </div>\n</div>\n" }]
|
|
333
352
|
}] });
|
|
334
353
|
|
|
335
354
|
class InvoiceSummarySkeleton {
|
|
336
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
337
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.
|
|
355
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceSummarySkeleton, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
356
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: InvoiceSummarySkeleton, isStandalone: true, selector: "rolatech-invoice-summary-skeleton", ngImport: i0, template: "<div class=\"rounded-xl border border-[--rt-border-color] p-4 grid space-y-3\">\n <rolatech-skeleton class=\"w-12 h-6\"></rolatech-skeleton>\n\n <dl class=\"space-y-2 text-sm\">\n <div class=\"flex justify-between\">\n <rolatech-skeleton class=\"w-28 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-11 h-4\"></rolatech-skeleton>\n </div>\n <div class=\"flex justify-between\">\n <rolatech-skeleton class=\"w-28 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-11 h-4\"></rolatech-skeleton>\n </div>\n <div class=\"flex justify-between\">\n <rolatech-skeleton class=\"w-32 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-11 h-4\"></rolatech-skeleton>\n </div>\n\n <div class=\"border-t border-dashed border-[--rt-border-color] my-2\"></div>\n\n <div class=\"flex justify-between\">\n <rolatech-skeleton class=\"w-11 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-28 h-4\"></rolatech-skeleton>\n </div>\n </dl>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: Skeleton, selector: "rolatech-skeleton" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
338
357
|
}
|
|
339
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
358
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceSummarySkeleton, decorators: [{
|
|
340
359
|
type: Component,
|
|
341
360
|
args: [{ selector: 'rolatech-invoice-summary-skeleton', imports: [Skeleton], encapsulation: ViewEncapsulation.None, template: "<div class=\"rounded-xl border border-[--rt-border-color] p-4 grid space-y-3\">\n <rolatech-skeleton class=\"w-12 h-6\"></rolatech-skeleton>\n\n <dl class=\"space-y-2 text-sm\">\n <div class=\"flex justify-between\">\n <rolatech-skeleton class=\"w-28 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-11 h-4\"></rolatech-skeleton>\n </div>\n <div class=\"flex justify-between\">\n <rolatech-skeleton class=\"w-28 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-11 h-4\"></rolatech-skeleton>\n </div>\n <div class=\"flex justify-between\">\n <rolatech-skeleton class=\"w-32 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-11 h-4\"></rolatech-skeleton>\n </div>\n\n <div class=\"border-t border-dashed border-[--rt-border-color] my-2\"></div>\n\n <div class=\"flex justify-between\">\n <rolatech-skeleton class=\"w-11 h-4\"></rolatech-skeleton>\n <rolatech-skeleton class=\"w-28 h-4\"></rolatech-skeleton>\n </div>\n </dl>\n</div>\n" }]
|
|
342
361
|
}] });
|
|
343
362
|
|
|
344
363
|
class InvoiceLinesSkeleton {
|
|
345
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
346
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.
|
|
364
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceLinesSkeleton, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
365
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: InvoiceLinesSkeleton, isStandalone: true, selector: "rolatech-invoice-lines-skeleton", ngImport: i0, template: "<div class=\"rounded-xl border border-[--rt-border-color] p-4 grid space-y-3\">\n <rolatech-skeleton class=\"w-20 h-6\"></rolatech-skeleton>\n\n <div class=\"overflow-x-auto\">\n <table class=\"min-w-full text-sm border border-[--rt-border-color] rounded-lg\">\n <thead class=\"bg-[--rt-raised-background]\">\n <tr>\n <th class=\"px-3 py-2 text-left font-medium border-b border-[--rt-border-color]\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n <th class=\"px-3 py-2 text-center font-medium border-b border-[--rt-border-color] w-20\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-[--rt-border-color] w-28\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-[--rt-border-color] w-20\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-[--rt-border-color] w-28\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-[--rt-border-color] w-32\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n </tr>\n </thead>\n\n <tbody class=\"[&>tr:nth-child(even)]:bg-[--rt-raised-background]\">\n @for (dummy of [0, 1, 2, 3]; track dummy) {\n <tr>\n <!-- Title -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color]\">\n <rolatech-skeleton class=\"h-6\"></rolatech-skeleton>\n </td>\n\n <!-- Qty -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-center text-[--rt-text-secondary]\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </td>\n\n <!-- Unit price -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-right text-[--rt-text-secondary]\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </td>\n\n <!-- VAT rate -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-right text-[--rt-text-secondary]\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </td>\n <!-- VAT amount -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-right text-[--rt-text-secondary]\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </td>\n\n <!-- Total -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-right font-semibold text-[--rt-text-secondary]\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: Skeleton, selector: "rolatech-skeleton" }], encapsulation: i0.ViewEncapsulation.None }); }
|
|
347
366
|
}
|
|
348
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
367
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceLinesSkeleton, decorators: [{
|
|
349
368
|
type: Component,
|
|
350
369
|
args: [{ selector: 'rolatech-invoice-lines-skeleton', imports: [Skeleton], encapsulation: ViewEncapsulation.None, template: "<div class=\"rounded-xl border border-[--rt-border-color] p-4 grid space-y-3\">\n <rolatech-skeleton class=\"w-20 h-6\"></rolatech-skeleton>\n\n <div class=\"overflow-x-auto\">\n <table class=\"min-w-full text-sm border border-[--rt-border-color] rounded-lg\">\n <thead class=\"bg-[--rt-raised-background]\">\n <tr>\n <th class=\"px-3 py-2 text-left font-medium border-b border-[--rt-border-color]\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n <th class=\"px-3 py-2 text-center font-medium border-b border-[--rt-border-color] w-20\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-[--rt-border-color] w-28\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-[--rt-border-color] w-20\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-[--rt-border-color] w-28\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n <th class=\"px-3 py-2 text-right font-medium border-b border-[--rt-border-color] w-32\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </th>\n </tr>\n </thead>\n\n <tbody class=\"[&>tr:nth-child(even)]:bg-[--rt-raised-background]\">\n @for (dummy of [0, 1, 2, 3]; track dummy) {\n <tr>\n <!-- Title -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color]\">\n <rolatech-skeleton class=\"h-6\"></rolatech-skeleton>\n </td>\n\n <!-- Qty -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-center text-[--rt-text-secondary]\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </td>\n\n <!-- Unit price -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-right text-[--rt-text-secondary]\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </td>\n\n <!-- VAT rate -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-right text-[--rt-text-secondary]\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </td>\n <!-- VAT amount -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-right text-[--rt-text-secondary]\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </td>\n\n <!-- Total -->\n <td class=\"px-3 py-2 border-t border-[--rt-border-color] text-right font-semibold text-[--rt-text-secondary]\">\n <rolatech-skeleton class=\"h-4\"></rolatech-skeleton>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n</div>\n" }]
|
|
351
370
|
}] });
|
|
@@ -425,10 +444,10 @@ class InvoiceDetailComponent extends BaseComponent {
|
|
|
425
444
|
},
|
|
426
445
|
});
|
|
427
446
|
}
|
|
428
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
429
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.
|
|
447
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceDetailComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
448
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: InvoiceDetailComponent, isStandalone: true, selector: "rolatech-invoice-detail", usesInheritance: true, ngImport: i0, template: "<rolatech-container>\n <rolatech-toolbar [title]=\"invoice() ? status[invoice()!.status] : ''\" large link=\"../\"> </rolatech-toolbar>\n @if (loading()) {\n <div class=\"grid grid-cols-1 lg:grid-cols-3 gap-4\">\n <div class=\"grid lg:col-span-2 space-y-4\">\n <rolatech-invoice-user-skeleton></rolatech-invoice-user-skeleton>\n <rolatech-invoice-lines-skeleton></rolatech-invoice-lines-skeleton>\n </div>\n <div class=\"space-y-4\">\n <rolatech-invoice-summary-skeleton></rolatech-invoice-summary-skeleton>\n </div>\n </div>\n } @else {\n @if (invoice(); as invoice) {\n <div class=\"grid grid-cols-1 lg:grid-cols-3 gap-4\">\n <div class=\"grid lg:col-span-2 space-y-4\">\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 @if (invoice?.lines; as lines) {\n <rolatech-invoice-lines [lines]=\"lines\"></rolatech-invoice-lines>\n }\n </div>\n <div class=\"space-y-4\">\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 <div class=\"px-2\">\n <div class=\"flex items-baseline justify-between py-1\">\n <span class=\"font-medium min-w-20\" i18n>Note</span>\n <span class=\"text-sm\">{{ invoice.note || 'Null' }}</span>\n </div>\n @if (invoice.status.toString() === 'CREATED' || invoice.status.toString() === 'PAID') {\n <div class=\"flex items-center justify-between py-1\">\n <span class=\"font-medium\" i18n>Payment method</span>\n <span class=\"text-sm\"> Stripe</span>\n </div>\n }\n @if (invoice.status.toString() === 'ISSUED' || invoice.status.toString() === 'SENT') {\n <div class=\"py-3\">\n <div class=\"flex items-center justify-end\">\n <button mat-flat-button [disabled]=\"paying\" class=\"w-full min-h-11\" (click)=\"pay()\" i18n>\n {{ paying ? 'Processing' : 'Pay' }}\n </button>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <div>\n <!-- safe area -->\n <div class=\"pb-16 sm:pb-3\"></div>\n </div>\n }\n }\n</rolatech-container>\n", styles: [""], dependencies: [{ 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: ContainerComponent, selector: "rolatech-container" }, { kind: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { 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" }] }); }
|
|
430
449
|
}
|
|
431
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
450
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceDetailComponent, decorators: [{
|
|
432
451
|
type: Component,
|
|
433
452
|
args: [{ selector: 'rolatech-invoice-detail', imports: [
|
|
434
453
|
MatButtonModule,
|
|
@@ -441,7 +460,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
|
|
|
441
460
|
InvoiceUserSkeleton,
|
|
442
461
|
InvoiceSummarySkeleton,
|
|
443
462
|
InvoiceLinesSkeleton,
|
|
444
|
-
], template: "<rolatech-container>\n <rolatech-toolbar [title]=\"invoice() ? status[invoice()!.status] : ''\" large link=\"../\"> </rolatech-toolbar>\n @if (loading()) {\n <div class=\"grid grid-cols-1 lg:grid-cols-3 gap-4\">\n <div class=\"grid lg:col-span-2 space-y-4\">\n <rolatech-invoice-user-skeleton></rolatech-invoice-user-skeleton>\n <rolatech-invoice-lines-skeleton></rolatech-invoice-lines-skeleton>\n </div>\n <div class=\"space-y-4\">\n <rolatech-invoice-summary-skeleton></rolatech-invoice-summary-skeleton>\n </div>\n </div>\n } @else {\n @if (invoice(); as invoice) {\n <div class=\"grid grid-cols-1 lg:grid-cols-3 gap-4\">\n <div class=\"grid lg:col-span-2 space-y-4\">\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 @if (invoice?.lines; as lines) {\n <rolatech-invoice-lines [lines]=\"lines\"></rolatech-invoice-lines>\n }\n </div>\n <div class=\"space-y-4\">\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 <div class=\"px-2\">\n <div class=\"flex items-baseline justify-between py-1\">\n <span class=\"font-medium min-w-20\" i18n>Note</span>\n <span class=\"text-sm\">{{ invoice.note || 'Null' }}</span>\n </div>\n @if (invoice.status.toString() === 'CREATED' || invoice.status.toString() === 'PAID') {\n <div class=\"flex items-center justify-between py-1\">\n <span class=\"font-medium\" i18n>Payment method</span>\n <span class=\"text-sm\"> Stripe</span>\n </div>\n }\n @if (invoice.status.toString() === '
|
|
463
|
+
], template: "<rolatech-container>\n <rolatech-toolbar [title]=\"invoice() ? status[invoice()!.status] : ''\" large link=\"../\"> </rolatech-toolbar>\n @if (loading()) {\n <div class=\"grid grid-cols-1 lg:grid-cols-3 gap-4\">\n <div class=\"grid lg:col-span-2 space-y-4\">\n <rolatech-invoice-user-skeleton></rolatech-invoice-user-skeleton>\n <rolatech-invoice-lines-skeleton></rolatech-invoice-lines-skeleton>\n </div>\n <div class=\"space-y-4\">\n <rolatech-invoice-summary-skeleton></rolatech-invoice-summary-skeleton>\n </div>\n </div>\n } @else {\n @if (invoice(); as invoice) {\n <div class=\"grid grid-cols-1 lg:grid-cols-3 gap-4\">\n <div class=\"grid lg:col-span-2 space-y-4\">\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 @if (invoice?.lines; as lines) {\n <rolatech-invoice-lines [lines]=\"lines\"></rolatech-invoice-lines>\n }\n </div>\n <div class=\"space-y-4\">\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 <div class=\"px-2\">\n <div class=\"flex items-baseline justify-between py-1\">\n <span class=\"font-medium min-w-20\" i18n>Note</span>\n <span class=\"text-sm\">{{ invoice.note || 'Null' }}</span>\n </div>\n @if (invoice.status.toString() === 'CREATED' || invoice.status.toString() === 'PAID') {\n <div class=\"flex items-center justify-between py-1\">\n <span class=\"font-medium\" i18n>Payment method</span>\n <span class=\"text-sm\"> Stripe</span>\n </div>\n }\n @if (invoice.status.toString() === 'ISSUED' || invoice.status.toString() === 'SENT') {\n <div class=\"py-3\">\n <div class=\"flex items-center justify-end\">\n <button mat-flat-button [disabled]=\"paying\" class=\"w-full min-h-11\" (click)=\"pay()\" i18n>\n {{ paying ? 'Processing' : 'Pay' }}\n </button>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n <div>\n <!-- safe area -->\n <div class=\"pb-16 sm:pb-3\"></div>\n </div>\n }\n }\n</rolatech-container>\n" }]
|
|
445
464
|
}] });
|
|
446
465
|
|
|
447
466
|
const invoiceRoutes = [
|
|
@@ -509,6 +528,7 @@ class InvoiceManageIndex extends BaseComponent {
|
|
|
509
528
|
// Cheap deep compare via JSON to avoid spam calls when nothing changed
|
|
510
529
|
map((o) => JSON.stringify(o)), distinctUntilChanged(), map((s) => JSON.parse(s)), switchMap((params) => {
|
|
511
530
|
this.loading = true;
|
|
531
|
+
params['sort'] = 'updatedAt desc';
|
|
512
532
|
return this.invoiceService.findByManager(params).pipe(finalize(() => (this.loading = false)));
|
|
513
533
|
}))
|
|
514
534
|
.subscribe({
|
|
@@ -546,10 +566,10 @@ class InvoiceManageIndex extends BaseComponent {
|
|
|
546
566
|
return 'bg-gray-100 text-gray-700';
|
|
547
567
|
}
|
|
548
568
|
}
|
|
549
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
550
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.
|
|
569
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceManageIndex, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
570
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", 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 [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab [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 text-left\">#ID</th>\n <th class=\"p-3 border-b text-left\">Status</th>\n <th class=\"p-3 border-b text-left\">Email</th>\n <th class=\"p-3 border-b text-left\">Profile</th>\n <th class=\"p-3 border-b 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\" [routerLink]=\"['./', item.id]\">\n <td class=\"p-3 border-b\">{{item.id}}</td>\n <td class=\"p-3 border-b\">\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\">{{item.email}}</td>\n <td class=\"p-3 border-b\">{{item.firstName}},{{item.lastName}}</td>\n <td class=\"p-3 border-b\">{{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 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 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"], outputs: ["selectChange"] }, { kind: "component", type: TabComponent, selector: "rolatech-tab", inputs: ["label"] }, { 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: PricePipe, name: "price" }] }); }
|
|
551
571
|
}
|
|
552
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
572
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceManageIndex, decorators: [{
|
|
553
573
|
type: Component,
|
|
554
574
|
args: [{ selector: 'rolatech-invoice-manage-index', imports: [CommonModule, RouterLink, ToolbarComponent, TabsComponent, TabComponent, MatPaginatorModule, PricePipe], template: "<rolatech-toolbar title=\"Invoices\">\n <div class=\"flex items-center gap-2\"></div>\n</rolatech-toolbar>\n<rolatech-tabs [select]=\"select\">\n @for (item of links; track item) { @if (item.status) {\n <rolatech-tab [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab [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 text-left\">#ID</th>\n <th class=\"p-3 border-b text-left\">Status</th>\n <th class=\"p-3 border-b text-left\">Email</th>\n <th class=\"p-3 border-b text-left\">Profile</th>\n <th class=\"p-3 border-b 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\" [routerLink]=\"['./', item.id]\">\n <td class=\"p-3 border-b\">{{item.id}}</td>\n <td class=\"p-3 border-b\">\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\">{{item.email}}</td>\n <td class=\"p-3 border-b\">{{item.firstName}},{{item.lastName}}</td>\n <td class=\"p-3 border-b\">{{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 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 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" }]
|
|
555
575
|
}] });
|
|
@@ -566,12 +586,12 @@ class InvoiceManageCreate {
|
|
|
566
586
|
issueDate: this.todayIso(),
|
|
567
587
|
dueDate: '',
|
|
568
588
|
notes: '',
|
|
569
|
-
lines: [{ description: '', quantity: 1, unitPrice: 0, taxRate: 0 }],
|
|
589
|
+
lines: [{ type: '', description: '', quantity: 1, unitPrice: 0, taxRate: 0 }],
|
|
570
590
|
};
|
|
571
591
|
this.lastResponse = null;
|
|
572
592
|
}
|
|
573
593
|
addLine() {
|
|
574
|
-
this.invoice.lines.push({ description: '', quantity: 1, unitPrice: 0, taxRate: 0 });
|
|
594
|
+
this.invoice.lines.push({ type: '', description: '', quantity: 1, unitPrice: 0, taxRate: 0 });
|
|
575
595
|
}
|
|
576
596
|
removeLine(i) {
|
|
577
597
|
this.invoice.lines.splice(i, 1);
|
|
@@ -634,7 +654,7 @@ class InvoiceManageCreate {
|
|
|
634
654
|
issueDate: this.todayIso(),
|
|
635
655
|
dueDate: '',
|
|
636
656
|
notes: '',
|
|
637
|
-
lines: [{ description: '', quantity: 1, unitPrice: 0, taxRate: 0 }],
|
|
657
|
+
lines: [{ type: '', description: '', quantity: 1, unitPrice: 0, taxRate: 0 }],
|
|
638
658
|
};
|
|
639
659
|
this.lastResponse = null;
|
|
640
660
|
}
|
|
@@ -644,10 +664,10 @@ class InvoiceManageCreate {
|
|
|
644
664
|
todayIso() {
|
|
645
665
|
return new Date().toISOString().slice(0, 10);
|
|
646
666
|
}
|
|
647
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
648
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.0", type: InvoiceManageCreate, isStandalone: true, selector: "rolatech-invoice-manage-create", ngImport: i0, template: "<rolatech-toolbar title=\"Create Invoice\">\n <button mat-stroked-button (click)=\"addLine()\">\n <mat-icon>add</mat-icon>\n Add line\n </button>\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 md:p-8 max-w-6xl mx-auto space-y-6\">\n <form #invoiceForm=\"ngForm\" novalidate>\n <mat-card appearance=\"outlined\" class=\"p-4\">\n <div class=\"grid md: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=\"md: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=\"md: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>playlist_add</mat-icon>\n Add line\n </button>\n </div>\n\n <div class=\"hidden md:grid grid-cols-12 gap-2 text-sm font-medium px-2\">\n <div class=\"col-span-5\">Description</div>\n <div class=\"col-span-2 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 md:grid-cols-12 gap-2 items-start\">\n <mat-form-field class=\"md:col-span-5\" 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=\"md:col-span-2\" 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=\"md:col-span-2\" appearance=\"fill\">\n <mat-label>Unit Price (major unit)</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=\"md: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=\"md: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: "pipe", type: i1.DecimalPipe, name: "number" }] }); }
|
|
667
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceManageCreate, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
668
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", 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" }] }); }
|
|
649
669
|
}
|
|
650
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
670
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceManageCreate, decorators: [{
|
|
651
671
|
type: Component,
|
|
652
672
|
args: [{ selector: 'rolatech-invoice-manage-create', imports: [
|
|
653
673
|
CommonModule,
|
|
@@ -660,7 +680,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
|
|
|
660
680
|
MatDividerModule,
|
|
661
681
|
MatCardModule,
|
|
662
682
|
ToolbarComponent,
|
|
663
|
-
|
|
683
|
+
EnumSelect,
|
|
684
|
+
], 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" }]
|
|
664
685
|
}] });
|
|
665
686
|
|
|
666
687
|
const MY_FORMATS = {
|
|
@@ -684,8 +705,8 @@ class InvoiceEdit extends BaseComponent {
|
|
|
684
705
|
ngDoCheck() {
|
|
685
706
|
this.output.emit(this.invoice());
|
|
686
707
|
}
|
|
687
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
688
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.
|
|
708
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceEdit, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
709
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.6", type: InvoiceEdit, isStandalone: true, selector: "rolatech-invoice-edit", inputs: { invoice: { classPropertyName: "invoice", publicName: "invoice", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { invoice: "invoiceChange", output: "output" }, providers: [
|
|
689
710
|
{
|
|
690
711
|
provide: DateAdapter,
|
|
691
712
|
useClass: MomentDateAdapter,
|
|
@@ -694,7 +715,7 @@ class InvoiceEdit extends BaseComponent {
|
|
|
694
715
|
{ provide: MAT_DATE_FORMATS, useValue: MY_FORMATS },
|
|
695
716
|
], 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 }] }); }
|
|
696
717
|
}
|
|
697
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
718
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceEdit, decorators: [{
|
|
698
719
|
type: Component,
|
|
699
720
|
args: [{ selector: 'rolatech-invoice-edit', imports: [CommonModule, FormsModule, MatInputModule, MatDatepickerModule, MatSelectModule, MatOptionModule], providers: [
|
|
700
721
|
{
|
|
@@ -706,32 +727,394 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
|
|
|
706
727
|
], 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" }]
|
|
707
728
|
}], propDecorators: { invoice: [{ type: i0.Input, args: [{ isSignal: true, alias: "invoice", required: true }] }, { type: i0.Output, args: ["invoiceChange"] }], output: [{ type: i0.Output, args: ["output"] }] } });
|
|
708
729
|
|
|
730
|
+
class InvoiceStore {
|
|
731
|
+
constructor() {
|
|
732
|
+
this.api = inject(InvoiceService);
|
|
733
|
+
this._lines = signal([], ...(ngDevMode ? [{ debugName: "_lines" }] : []));
|
|
734
|
+
// --- state ---
|
|
735
|
+
this.invoiceId = signal(null, ...(ngDevMode ? [{ debugName: "invoiceId" }] : []));
|
|
736
|
+
this.invoice = signal(null, ...(ngDevMode ? [{ debugName: "invoice" }] : []));
|
|
737
|
+
/** last server-saved snapshot */
|
|
738
|
+
this.pristine = signal(null, ...(ngDevMode ? [{ debugName: "pristine" }] : []));
|
|
739
|
+
this.status = signal('idle', ...(ngDevMode ? [{ debugName: "status" }] : []));
|
|
740
|
+
this.error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
|
|
741
|
+
// editor UI state
|
|
742
|
+
this.editorOpen = signal(false, ...(ngDevMode ? [{ debugName: "editorOpen" }] : []));
|
|
743
|
+
this.editingLineId = signal(null, ...(ngDevMode ? [{ debugName: "editingLineId" }] : []));
|
|
744
|
+
// --- derived ---
|
|
745
|
+
this.lines = computed(() => this.invoice()?.lines ?? [], ...(ngDevMode ? [{ debugName: "lines" }] : []));
|
|
746
|
+
this.currency = computed(() => this.invoice()?.currency ?? 'GBP', ...(ngDevMode ? [{ debugName: "currency" }] : []));
|
|
747
|
+
this.vatMode = computed(() => this.invoice()?.vatMode ?? InvoiceVatMode.EXCLUSIVE, ...(ngDevMode ? [{ debugName: "vatMode" }] : []));
|
|
748
|
+
this.totals = computed(() => {
|
|
749
|
+
const inv = this.invoice();
|
|
750
|
+
if (!inv) {
|
|
751
|
+
return { subtotal: 0, vatTotal: 0, total: 0 };
|
|
752
|
+
}
|
|
753
|
+
const lines = inv.lines ?? [];
|
|
754
|
+
const subtotal = lines.reduce((s, l) => s + (l.amount ?? 0), 0);
|
|
755
|
+
const vatTotal = lines.reduce((s, l) => s + (l.vatAmount ?? 0), 0);
|
|
756
|
+
const total = lines.reduce((s, l) => s + (l.lineTotal ?? 0), 0);
|
|
757
|
+
return { subtotal, vatTotal, total };
|
|
758
|
+
}, ...(ngDevMode ? [{ debugName: "totals" }] : []));
|
|
759
|
+
/** 🔴 DIRTY FLAG */
|
|
760
|
+
this.dirty = computed(() => {
|
|
761
|
+
const a = this.invoice();
|
|
762
|
+
const b = this.pristine();
|
|
763
|
+
if (!a || !b)
|
|
764
|
+
return false;
|
|
765
|
+
return JSON.stringify(a) !== JSON.stringify(b);
|
|
766
|
+
}, ...(ngDevMode ? [{ debugName: "dirty" }] : []));
|
|
767
|
+
}
|
|
768
|
+
// --- API: load / refresh ---
|
|
769
|
+
load(id) {
|
|
770
|
+
this.invoiceId.set(id);
|
|
771
|
+
this.status.set('loading');
|
|
772
|
+
this.error.set(null);
|
|
773
|
+
// ✅ Adapt to your actual API signature:
|
|
774
|
+
// e.g. this.api.get(id) or this.api.getInvoice(id)
|
|
775
|
+
this.api
|
|
776
|
+
.get(id)
|
|
777
|
+
.pipe(finalize$1(() => this.status.set('idle')))
|
|
778
|
+
.subscribe({
|
|
779
|
+
next: (inv) => {
|
|
780
|
+
const normalized = this.normalizeInvoice(inv.data);
|
|
781
|
+
this.invoice.set(normalized);
|
|
782
|
+
this.pristine.set(structuredClone(normalized));
|
|
783
|
+
},
|
|
784
|
+
error: (e) => {
|
|
785
|
+
this.status.set('error');
|
|
786
|
+
this.error.set(this.errMsg(e));
|
|
787
|
+
},
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
refresh() {
|
|
791
|
+
const id = this.invoiceId();
|
|
792
|
+
if (!id)
|
|
793
|
+
return;
|
|
794
|
+
this.load(id);
|
|
795
|
+
}
|
|
796
|
+
// --- API: save ---
|
|
797
|
+
saveInvoice() {
|
|
798
|
+
const id = this.invoiceId();
|
|
799
|
+
const inv = this.invoice();
|
|
800
|
+
if (!id || !inv)
|
|
801
|
+
return;
|
|
802
|
+
this.status.set('saving');
|
|
803
|
+
this.error.set(null);
|
|
804
|
+
// ✅ Adapt: this.api.update(id, dto)
|
|
805
|
+
this.api
|
|
806
|
+
.update(id, inv)
|
|
807
|
+
.pipe(finalize$1(() => this.status.set('idle')))
|
|
808
|
+
.subscribe({
|
|
809
|
+
next: (saved) => this.invoice.set(this.normalizeInvoice(saved)),
|
|
810
|
+
error: (e) => {
|
|
811
|
+
this.status.set('error');
|
|
812
|
+
this.error.set(this.errMsg(e));
|
|
813
|
+
},
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
saveLines() {
|
|
817
|
+
const id = this.invoiceId();
|
|
818
|
+
const inv = this.invoice();
|
|
819
|
+
if (!id || !inv)
|
|
820
|
+
return;
|
|
821
|
+
this.status.set('saving');
|
|
822
|
+
this.error.set(null);
|
|
823
|
+
const payload = (inv.lines ?? []).map((l) => this.toLinePayload(l));
|
|
824
|
+
// ✅ Adapt: this.api.updateLines(id, payload)
|
|
825
|
+
// If your backend updates invoice lines via PATCH /invoices/{id}/lines
|
|
826
|
+
this.api
|
|
827
|
+
.updateLines(id, payload)
|
|
828
|
+
.pipe(finalize$1(() => this.status.set('idle')))
|
|
829
|
+
.subscribe({
|
|
830
|
+
next: (saved) => {
|
|
831
|
+
const normalized = this.normalizeInvoice(saved.data);
|
|
832
|
+
this.invoice.set(normalized);
|
|
833
|
+
this.pristine.set(structuredClone(normalized));
|
|
834
|
+
},
|
|
835
|
+
error: (e) => {
|
|
836
|
+
this.status.set('error');
|
|
837
|
+
this.error.set(this.errMsg(e));
|
|
838
|
+
},
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
// ─────────────────────────────────────────────
|
|
842
|
+
// Editor lifecycle
|
|
843
|
+
// ─────────────────────────────────────────────
|
|
844
|
+
openLineEditor(lineId) {
|
|
845
|
+
this.editorOpen.set(true);
|
|
846
|
+
this.editingLineId.set(lineId ?? null);
|
|
847
|
+
}
|
|
848
|
+
closeLineEditor() {
|
|
849
|
+
this.editorOpen.set(false);
|
|
850
|
+
this.editingLineId.set(null);
|
|
851
|
+
}
|
|
852
|
+
getLineById(lineId) {
|
|
853
|
+
return this.lines().find((l) => l.id === lineId) ?? null;
|
|
854
|
+
}
|
|
855
|
+
// ─────────────────────────────────────────────
|
|
856
|
+
// Line operations (ALL mark dirty automatically)
|
|
857
|
+
// ─────────────────────────────────────────────
|
|
858
|
+
addLineAndEdit() {
|
|
859
|
+
this.invoice.update((inv) => {
|
|
860
|
+
if (!inv)
|
|
861
|
+
return inv;
|
|
862
|
+
const newLine = this.recalcLine(this.createEmptyLine(inv.currency), inv.vatMode);
|
|
863
|
+
queueMicrotask(() => this.openLineEditor(newLine.id));
|
|
864
|
+
return { ...inv, lines: [...inv.lines, newLine] };
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
updateLineById(lineId, patch) {
|
|
868
|
+
this.invoice.update((inv) => {
|
|
869
|
+
if (!inv)
|
|
870
|
+
return inv;
|
|
871
|
+
const mode = inv.vatMode;
|
|
872
|
+
const lines = (inv.lines ?? []).map((l) => {
|
|
873
|
+
if (l.id !== lineId)
|
|
874
|
+
return l;
|
|
875
|
+
const merged = { ...l, ...patch };
|
|
876
|
+
return this.recalcLine(merged, mode);
|
|
877
|
+
});
|
|
878
|
+
return { ...inv, lines };
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
removeLineById(lineId) {
|
|
882
|
+
this.invoice.update((inv) => {
|
|
883
|
+
if (!inv)
|
|
884
|
+
return inv;
|
|
885
|
+
if (this.editingLineId() === lineId)
|
|
886
|
+
this.closeLineEditor();
|
|
887
|
+
return { ...inv, lines: inv.lines.filter((l) => l.id !== lineId) };
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
// If vatMode changes, recalc all lines
|
|
891
|
+
setVatMode(mode) {
|
|
892
|
+
this.invoice.update((inv) => {
|
|
893
|
+
if (!inv)
|
|
894
|
+
return inv;
|
|
895
|
+
const lines = (inv.lines ?? []).map((l) => this.recalcLine(l, mode));
|
|
896
|
+
return { ...inv, vatMode: mode, lines };
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
reorderLines(fromIndex, toIndex) {
|
|
900
|
+
if (fromIndex === toIndex)
|
|
901
|
+
return;
|
|
902
|
+
this.invoice.update((inv) => {
|
|
903
|
+
if (!inv)
|
|
904
|
+
return inv;
|
|
905
|
+
const lines = [...(inv.lines ?? [])];
|
|
906
|
+
const [moved] = lines.splice(fromIndex, 1);
|
|
907
|
+
lines.splice(toIndex, 0, moved);
|
|
908
|
+
// re-index positions (1..n)
|
|
909
|
+
const rePos = lines.map((l, idx) => ({ ...l, position: idx + 1 }));
|
|
910
|
+
return { ...inv, lines: rePos };
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
setLines(lines) {
|
|
914
|
+
this._lines.set(lines);
|
|
915
|
+
}
|
|
916
|
+
updateLine(id, patch) {
|
|
917
|
+
this._lines.update((lines) => lines.map((l) => (l.id === id ? { ...l, ...patch } : l)));
|
|
918
|
+
}
|
|
919
|
+
// --- helpers: normalize / compute ---
|
|
920
|
+
normalizeInvoice(inv) {
|
|
921
|
+
const mode = inv.vatMode ?? InvoiceVatMode.EXCLUSIVE;
|
|
922
|
+
const sorted = [...(inv.lines ?? [])]
|
|
923
|
+
.sort((a, b) => (a.position ?? 0) - (b.position ?? 0))
|
|
924
|
+
.map((l, idx) => this.recalcLine({ ...l, position: idx + 1 }, mode));
|
|
925
|
+
return { ...inv, lines: sorted };
|
|
926
|
+
}
|
|
927
|
+
recalcLine(line, mode) {
|
|
928
|
+
const qty = this.num(line.quantity, 1);
|
|
929
|
+
const unit = this.num(line.unitPrice, 0);
|
|
930
|
+
const rate = this.num(line.vatRate, 0);
|
|
931
|
+
const grossUnit = unit;
|
|
932
|
+
const netUnit = mode === InvoiceVatMode.INCLUSIVE ? grossUnit / (1 + rate / 100) : grossUnit;
|
|
933
|
+
const net = netUnit * qty;
|
|
934
|
+
const vat = net * (rate / 100);
|
|
935
|
+
const total = mode === InvoiceVatMode.INCLUSIVE ? grossUnit * qty : net + vat;
|
|
936
|
+
return {
|
|
937
|
+
...line,
|
|
938
|
+
quantity: qty,
|
|
939
|
+
unitPrice: unit,
|
|
940
|
+
vatRate: rate,
|
|
941
|
+
amount: this.round2(net),
|
|
942
|
+
vatAmount: this.round2(mode === InvoiceVatMode.INCLUSIVE ? total - net : vat),
|
|
943
|
+
lineTotal: this.round2(total),
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
createEmptyLine(currency) {
|
|
947
|
+
return {
|
|
948
|
+
id: this.newId(),
|
|
949
|
+
code: '',
|
|
950
|
+
title: 'New item',
|
|
951
|
+
description: '',
|
|
952
|
+
imageUrl: '',
|
|
953
|
+
amount: 0,
|
|
954
|
+
unitPrice: 0,
|
|
955
|
+
vatRate: 0,
|
|
956
|
+
vatAmount: 0,
|
|
957
|
+
lineTotal: 0,
|
|
958
|
+
quantity: 1,
|
|
959
|
+
currency,
|
|
960
|
+
refId: '',
|
|
961
|
+
type: InvoiceLineType.CUSTOM,
|
|
962
|
+
position: 0,
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
toLinePayload(l) {
|
|
966
|
+
return {
|
|
967
|
+
id: l.id,
|
|
968
|
+
position: l.position, // ✅ persist order
|
|
969
|
+
type: l.type,
|
|
970
|
+
title: l.title,
|
|
971
|
+
description: l.description,
|
|
972
|
+
quantity: l.quantity,
|
|
973
|
+
unitPrice: l.unitPrice,
|
|
974
|
+
vatRate: l.vatRate,
|
|
975
|
+
currency: l.currency,
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
newId() {
|
|
979
|
+
return typeof crypto !== 'undefined' && 'randomUUID' in crypto
|
|
980
|
+
? crypto.randomUUID()
|
|
981
|
+
: `tmp_${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
|
982
|
+
}
|
|
983
|
+
num(v, def) {
|
|
984
|
+
const n = Number(v);
|
|
985
|
+
return Number.isFinite(n) ? n : def;
|
|
986
|
+
}
|
|
987
|
+
round2(n) {
|
|
988
|
+
return Math.round((n + Number.EPSILON) * 100) / 100;
|
|
989
|
+
}
|
|
990
|
+
errMsg(e) {
|
|
991
|
+
return e?.error?.message || e?.message || 'Request failed';
|
|
992
|
+
}
|
|
993
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
994
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceStore, providedIn: 'root' }); }
|
|
995
|
+
}
|
|
996
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceStore, decorators: [{
|
|
997
|
+
type: Injectable,
|
|
998
|
+
args: [{ providedIn: 'root' }]
|
|
999
|
+
}] });
|
|
1000
|
+
|
|
1001
|
+
class InvoiceLinePropertySelector {
|
|
1002
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceLinePropertySelector, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1003
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: InvoiceLinePropertySelector, isStandalone: true, selector: "rolatech-invoice-line-property-selector", ngImport: i0, template: "<p>invoice-line-property-selector works!</p>\n", styles: [""] }); }
|
|
1004
|
+
}
|
|
1005
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceLinePropertySelector, decorators: [{
|
|
1006
|
+
type: Component,
|
|
1007
|
+
args: [{ selector: 'rolatech-invoice-line-property-selector', imports: [], template: "<p>invoice-line-property-selector works!</p>\n" }]
|
|
1008
|
+
}] });
|
|
1009
|
+
|
|
1010
|
+
class InvoiceManageLine {
|
|
1011
|
+
constructor() {
|
|
1012
|
+
this.store = inject(InvoiceStore);
|
|
1013
|
+
this.dialog = inject(MatDialog);
|
|
1014
|
+
this.lines = this.store.lines;
|
|
1015
|
+
this.vatMode = this.store.vatMode;
|
|
1016
|
+
this.editMode = signal(false, ...(ngDevMode ? [{ debugName: "editMode" }] : []));
|
|
1017
|
+
this.openingForLine = new Set();
|
|
1018
|
+
this.isVatExclusive = () => this.vatMode() === 'EXCLUSIVE';
|
|
1019
|
+
}
|
|
1020
|
+
toggleEdit() {
|
|
1021
|
+
this.editMode.update((v) => !v);
|
|
1022
|
+
}
|
|
1023
|
+
add() {
|
|
1024
|
+
this.store.addLineAndEdit(); // or addLine()
|
|
1025
|
+
}
|
|
1026
|
+
remove(id) {
|
|
1027
|
+
this.store.removeLineById(id);
|
|
1028
|
+
}
|
|
1029
|
+
update(id, patch) {
|
|
1030
|
+
this.store.updateLineById(id, patch);
|
|
1031
|
+
}
|
|
1032
|
+
drop(event) {
|
|
1033
|
+
if (!this.editMode())
|
|
1034
|
+
return; // ⛔ prevent reorder when not editing
|
|
1035
|
+
this.store.reorderLines(event.previousIndex, event.currentIndex);
|
|
1036
|
+
}
|
|
1037
|
+
isPropertyLine(type) {
|
|
1038
|
+
return type === 'PROPERTY_RENT' || type === 'PROPERTY_SALE';
|
|
1039
|
+
}
|
|
1040
|
+
onTitleFocus(line) {
|
|
1041
|
+
if (!this.isPropertyLine(line.type))
|
|
1042
|
+
return;
|
|
1043
|
+
// guard: focusing can fire repeatedly (click, focus, programmatic)
|
|
1044
|
+
if (this.openingForLine.has(line.id))
|
|
1045
|
+
return;
|
|
1046
|
+
this.openingForLine.add(line.id);
|
|
1047
|
+
this.openPropertyDialog(line).finally(() => {
|
|
1048
|
+
// release guard a tick later so re-focus works after dialog closes
|
|
1049
|
+
queueMicrotask(() => this.openingForLine.delete(line.id));
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
onTitleKeydown(ev, line) {
|
|
1053
|
+
if (!this.isPropertyLine(line.type))
|
|
1054
|
+
return;
|
|
1055
|
+
// Block manual typing when it’s a property picker
|
|
1056
|
+
const allow = ['Tab', 'Shift', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape', 'Enter'];
|
|
1057
|
+
if (!allow.includes(ev.key)) {
|
|
1058
|
+
ev.preventDefault();
|
|
1059
|
+
}
|
|
1060
|
+
// Optional: Enter opens dialog too
|
|
1061
|
+
if (ev.key === 'Enter') {
|
|
1062
|
+
ev.preventDefault();
|
|
1063
|
+
this.onTitleFocus(line);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
async openPropertyDialog(line) {
|
|
1067
|
+
if (!this.isPropertyLine(line.type))
|
|
1068
|
+
return;
|
|
1069
|
+
const ref = this.dialog.open(InvoiceLinePropertySelector, {
|
|
1070
|
+
data: {
|
|
1071
|
+
title: 'Select property',
|
|
1072
|
+
initialQuery: line.title ?? '',
|
|
1073
|
+
},
|
|
1074
|
+
autoFocus: false,
|
|
1075
|
+
restoreFocus: false,
|
|
1076
|
+
});
|
|
1077
|
+
const selected = await ref.afterClosed().toPromise();
|
|
1078
|
+
if (!selected)
|
|
1079
|
+
return;
|
|
1080
|
+
// ✅ update with title + refId(propertyId)
|
|
1081
|
+
this.store.updateLine(line.id, {
|
|
1082
|
+
title: selected.title,
|
|
1083
|
+
refId: selected.id,
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
saveLines() { }
|
|
1087
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceManageLine, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1088
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", 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 }); }
|
|
1089
|
+
}
|
|
1090
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceManageLine, decorators: [{
|
|
1091
|
+
type: Component,
|
|
1092
|
+
args: [{ selector: 'rolatech-invoice-manage-line', imports: [DragDropModule, MatButtonModule, MatIcon, MatFormFieldModule, MatInputModule, PricePipe, EnumSelect], encapsulation: ViewEncapsulation.None, template: "<!-- invoice-lines.html -->\n<div class=\"rounded-xl border border-[--rt-border-color] p-4 grid space-y-3\">\n <!-- Header -->\n <div class=\"flex items-center justify-between\">\n <h2 class=\"text-sm font-semibold\">Invoice lines</h2>\n\n <div class=\"flex items-center gap-2\">\n <button mat-icon-button type=\"button\" aria-label=\"Toggle edit\" (click)=\"toggleEdit()\">\n <mat-icon> {{ editMode() ? 'done' : 'edit' }} </mat-icon>\n </button>\n <button mat-stroked-button type=\"button\" [disabled]=\"store.dirty() || store.status() === 'saving'\" (click)=\"saveLines()\">\n @if (store.status() === 'saving') { Saving\u2026 } @else { Save lines }\n </button>\n </div>\n </div>\n\n <!-- Column labels (desktop only) -->\n <div class=\"hidden lg:grid grid-cols-12 gap-2 text-sm font-medium px-2 text-[--rt-text-secondary]\">\n <!-- Drag column -->\n @if (editMode()) {\n <div class=\"col-span-1\"></div>\n }\n\n <!-- Type -->\n <div [class]=\"editMode() ? 'col-span-3' : 'col-span-3'\">Type</div>\n\n <!-- Description (take extra space when not editing) -->\n <div [class]=\"editMode() ? 'col-span-4' : 'col-span-5'\">Title</div>\n\n <!-- Unit Price -->\n <div class=\"col-span-2 text-right\">Unit Price</div>\n\n <!-- Tax % (optional) -->\n @if (!isVatExclusive()) {\n <div class=\"col-span-1 text-right\">Tax %</div>\n }\n\n <!-- Amount (take extra space when not editing) -->\n <div [class]=\"editMode() ? 'col-span-1' : 'col-span-2'\" class=\"text-right\">Amount</div>\n\n <!-- Delete column -->\n @if (editMode()) {\n <div class=\"col-span-1\"></div>\n }\n </div>\n\n <!-- invoice-lines.html (rows section) -->\n\n <div cdkDropList (cdkDropListDropped)=\"drop($event)\" [cdkDropListDisabled]=\"!editMode()\" class=\"space-y-2\">\n @if (lines().length > 0) { @for (line of lines(); track line.id; let i = $index) {\n\n <div\n cdkDrag\n class=\"grid grid-cols-12 gap-2 items-start rounded-xl border border-[--rt-border-color] bg-[--rt-background] p-2 hover:bg-[--rt-raised-background]\"\n [class.opacity-70]=\"!editMode()\"\n >\n <!-- Drag handle column (only in edit mode) -->\n @if (editMode()) {\n <div class=\"col-span-1 flex items-center justify-center\">\n <button mat-icon-button type=\"button\" cdkDragHandle aria-label=\"Reorder line\" (click)=\"$event.stopPropagation()\">\n <mat-icon>drag_indicator</mat-icon>\n </button>\n </div>\n }\n\n <!-- Type -->\n <div class=\"col-span-12 lg:col-span-3\">\n <rolatech-enum-select\n [resource]=\"'billing'\"\n [enumName]=\"'InvoiceLineType'\"\n [label]=\"'Line type'\"\n [placeholder]=\"'Select\u2026'\"\n [value]=\"line.type\"\n (valueChange)=\"editMode() && update(line.id!, { type: $event })\"\n ></rolatech-enum-select>\n </div>\n\n <!-- Title (expands when NOT editMode) -->\n <div [class]=\"editMode() ? 'col-span-12 lg:col-span-4' : 'col-span-12 lg:col-span-5'\">\n <mat-form-field appearance=\"fill\" class=\"w-full\" subscriptSizing=\"dynamic\">\n <mat-label>Title</mat-label>\n <input\n matInput\n placeholder=\"e.g. Photo editing package\"\n [readonly]=\"!editMode()\"\n [readonly]=\"isPropertyLine(line.type)\"\n (focus)=\"onTitleFocus(line)\"\n (click)=\"onTitleFocus(line)\"\n (keydown)=\"onTitleKeydown($event, line)\"\n [value]=\"line.title\"\n (input)=\"editMode() && update(line.id!, { title: $any($event.target).value })\"\n />\n </mat-form-field>\n </div>\n\n <!-- Unit Price -->\n <div class=\"col-span-6 lg:col-span-2\">\n <mat-form-field appearance=\"fill\" class=\"w-full\" subscriptSizing=\"dynamic\">\n <mat-label>Unit</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n class=\"text-right\"\n [readonly]=\"!editMode()\"\n [value]=\"line.unitPrice\"\n (input)=\"editMode() && update(line.id!, { unitPrice: +$any($event.target).value })\"\n />\n </mat-form-field>\n </div>\n\n <!-- Tax % (optional, hidden if VAT exclusive) -->\n @if (!isVatExclusive()) {\n <div class=\"col-span-6 lg:col-span-1\">\n <mat-form-field appearance=\"fill\" class=\"w-full\" subscriptSizing=\"dynamic\">\n <mat-label>Tax %</mat-label>\n <input\n matInput\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n class=\"text-right\"\n [readonly]=\"!editMode()\"\n [value]=\"line.vatRate\"\n (input)=\"editMode() && update(line.id!, { vatRate: +$any($event.target).value })\"\n />\n </mat-form-field>\n </div>\n }\n\n <!-- Amount (expands when NOT editMode) -->\n <div\n [class]=\"editMode() ? 'col-span-6 lg:col-span-1' : 'col-span-6 lg:col-span-2'\"\n class=\"flex items-center justify-end px-1 lg:pt-3\"\n >\n <div class=\"text-right font-semibold text-[--rt-text-secondary]\">{{ line.lineTotal | price }}</div>\n </div>\n\n <!-- Delete column (only in edit mode) -->\n @if (editMode()) {\n <div class=\"col-span-6 lg:col-span-1 flex items-center justify-end lg:pt-1\">\n <button mat-icon-button color=\"warn\" type=\"button\" aria-label=\"Delete line\" (click)=\"remove(line.id!)\">\n <mat-icon>delete</mat-icon>\n </button>\n </div>\n }\n </div>\n } } @else {\n <div class=\"py-8 text-center text-sm text-[--rt-text-secondary]\">No invoice lines</div>\n }\n </div>\n\n <!-- Footer -->\n <div>\n <button mat-stroked-button (click)=\"add()\">Add item</button>\n </div>\n</div>\n", styles: [".cdk-drag-preview{box-shadow:0 10px 20px #00000026;border-radius:12px}.cdk-drag-placeholder{opacity:.3}\n"] }]
|
|
1093
|
+
}] });
|
|
1094
|
+
|
|
709
1095
|
class InvoiceManageDetail extends BaseComponent {
|
|
710
1096
|
constructor() {
|
|
711
1097
|
super(...arguments);
|
|
712
1098
|
this.invoiceService = inject(InvoiceService);
|
|
713
|
-
this.
|
|
714
|
-
|
|
715
|
-
this.invoice =
|
|
1099
|
+
this.store = inject(InvoiceStore);
|
|
1100
|
+
// expose store signals
|
|
1101
|
+
this.invoice = this.store.invoice;
|
|
716
1102
|
this.status = InvoiceStatus;
|
|
1103
|
+
// convenience derived values for template
|
|
1104
|
+
this.loading = computed(() => this.store.status() === 'loading', ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
1105
|
+
this.pdfUrl = computed(() => this.store.invoice()?.pdfUrl ?? '', ...(ngDevMode ? [{ debugName: "pdfUrl" }] : []));
|
|
717
1106
|
}
|
|
718
1107
|
ngOnInit() {
|
|
719
|
-
this.
|
|
720
|
-
}
|
|
721
|
-
getInvoice() {
|
|
722
|
-
this.invoiceService.getInvoice(this.id).subscribe({
|
|
723
|
-
next: (res) => {
|
|
724
|
-
this.invoice.set(res.data);
|
|
725
|
-
this.loading.set(false);
|
|
726
|
-
this.pdfUrl = res.data.pdfUrl;
|
|
727
|
-
},
|
|
728
|
-
});
|
|
1108
|
+
this.store.load(this.id);
|
|
729
1109
|
}
|
|
730
1110
|
openPdf() {
|
|
731
|
-
|
|
1111
|
+
const url = this.pdfUrl();
|
|
1112
|
+
if (!url)
|
|
1113
|
+
return;
|
|
1114
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
732
1115
|
}
|
|
733
1116
|
statusBadgeClass(status) {
|
|
734
|
-
switch (status
|
|
1117
|
+
switch (String(status)) {
|
|
735
1118
|
case 'CREATED':
|
|
736
1119
|
return 'bg-yellow-100 text-yellow-800';
|
|
737
1120
|
case 'ISSUED':
|
|
@@ -745,61 +1128,92 @@ class InvoiceManageDetail extends BaseComponent {
|
|
|
745
1128
|
}
|
|
746
1129
|
}
|
|
747
1130
|
edit() {
|
|
748
|
-
const
|
|
1131
|
+
const inv = this.invoice();
|
|
1132
|
+
if (!inv)
|
|
1133
|
+
return;
|
|
1134
|
+
this.dialogService.open({
|
|
749
1135
|
title: 'Update invoice',
|
|
750
1136
|
cancelText: 'Cancel',
|
|
751
1137
|
confirmText: 'Confirm',
|
|
752
|
-
data: {
|
|
753
|
-
invoice: this.invoice(),
|
|
754
|
-
},
|
|
1138
|
+
data: { invoice: inv },
|
|
755
1139
|
component: InvoiceEdit,
|
|
756
|
-
};
|
|
757
|
-
|
|
758
|
-
this.dialogService.confirmed().subscribe({
|
|
1140
|
+
});
|
|
1141
|
+
// IMPORTANT: avoid stacking subscriptions if user opens dialog repeatedly
|
|
1142
|
+
const sub = this.dialogService.confirmed().subscribe({
|
|
759
1143
|
next: (res) => {
|
|
760
|
-
if (res)
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
1144
|
+
if (!res)
|
|
1145
|
+
return;
|
|
1146
|
+
const payload = {
|
|
1147
|
+
firstName: res.firstName,
|
|
1148
|
+
lastName: res.lastName,
|
|
1149
|
+
email: res.email,
|
|
1150
|
+
phone: res.phone,
|
|
1151
|
+
vatTotal: res.vatTotal,
|
|
1152
|
+
total: res.total,
|
|
1153
|
+
};
|
|
1154
|
+
this.invoiceService.updateInvoice(this.id, payload).subscribe({
|
|
1155
|
+
next: () => {
|
|
1156
|
+
this.snackBarService.open('Invoice updated');
|
|
1157
|
+
this.store.refresh(); // reload from API so UI stays consistent
|
|
1158
|
+
},
|
|
1159
|
+
error: (err) => this.snackBarService.open(err?.message ?? 'Update failed'),
|
|
1160
|
+
});
|
|
775
1161
|
},
|
|
776
|
-
error: (error) =>
|
|
777
|
-
|
|
1162
|
+
error: (error) => this.snackBarService.open(error?.message ?? 'Dialog error'),
|
|
1163
|
+
complete: () => sub.unsubscribe(),
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
addLine() {
|
|
1167
|
+
// add and open editor (recommended)
|
|
1168
|
+
this.store.addLineAndEdit();
|
|
1169
|
+
// OR if you just want to add without opening editor:
|
|
1170
|
+
// this.store.addLine();
|
|
1171
|
+
}
|
|
1172
|
+
// Optional: persist changes
|
|
1173
|
+
saveLines() {
|
|
1174
|
+
this.store.saveLines();
|
|
1175
|
+
}
|
|
1176
|
+
editCustomer() { }
|
|
1177
|
+
sendEmail() {
|
|
1178
|
+
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
|
|
1179
|
+
width: '400px',
|
|
1180
|
+
data: {
|
|
1181
|
+
title: 'Send invoice',
|
|
1182
|
+
message: 'Send this invoice to the customer’s email address?',
|
|
778
1183
|
},
|
|
779
1184
|
});
|
|
1185
|
+
dialogRef.afterClosed().subscribe((result) => {
|
|
1186
|
+
if (result) {
|
|
1187
|
+
this.invoiceService.sendInvoice(this.id).subscribe({
|
|
1188
|
+
next: (res) => {
|
|
1189
|
+
this.invoice.update((inv) => {
|
|
1190
|
+
if (!inv)
|
|
1191
|
+
return inv;
|
|
1192
|
+
inv.status = InvoiceStatus.SENT;
|
|
1193
|
+
return inv;
|
|
1194
|
+
});
|
|
1195
|
+
this.snackBarService.open('Invoice sent successfully.');
|
|
1196
|
+
},
|
|
1197
|
+
error: (e) => {
|
|
1198
|
+
this.snackBarService.open(e.message);
|
|
1199
|
+
},
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
1202
|
+
});
|
|
780
1203
|
}
|
|
781
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
782
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.
|
|
1204
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceManageDetail, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
1205
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: InvoiceManageDetail, isStandalone: true, selector: "rolatech-invoice-manage-detail", usesInheritance: true, ngImport: i0, template: "@if (store.status() === 'loading') {\n<div class=\"flex justify-center py-16\">Loading invoice\u2026</div>\n} @else if (!store.invoice()) {\n<div class=\"flex justify-center py-16\">Invoice not found.</div>\n} @else { @if (store.invoice(); as inv) {\n\n<!-- Sticky header -->\n<div class=\"sticky top-0 z-10 border-b border-[--rt-border-color] bg-[--rt-background]\">\n <div class=\"px-4 py-3 flex items-start justify-between gap-4\">\n <div class=\"min-w-0\">\n <div class=\"flex items-center gap-2\">\n <h1 class=\"text-lg font-semibold truncate\">Invoice {{ inv.invoiceNumber || inv.id }}</h1>\n\n <span class=\"px-2 py-0.5 rounded-full text-xs font-semibold\" [class]=\"statusBadgeClass(inv.status)\">\n {{ inv.status }}\n </span>\n\n @if (store.dirty() ) {\n <span class=\"text-xs text-[--rt-text-secondary]\">\u2022 Unsaved changes</span>\n }\n </div>\n\n <div class=\"mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-sm text-[--rt-text-secondary]\">\n <span class=\"truncate\"> {{ inv.firstName }} {{ inv.lastName }} \u2022 {{ inv.email }} </span>\n <span>Created {{ inv.createdAt | date:'dd/MM/yyyy':'Europe/London' }}</span>\n @if (inv.dueAt) { <span>Due {{ inv.dueAt | date:'dd/MM/yyyy':'Europe/London' }}</span> }\n </div>\n </div>\n\n <!-- Desktop actions -->\n\n <div class=\"hidden lg:flex items-center gap-2 shrink-0\">\n @if (inv.pdfUrl) {\n <button mat-stroked-button type=\"button\" (click)=\"openPdf()\">Preview PDF</button>\n } @if (inv.status.toString() === 'ISSUED') {\n <button mat-flat-button type=\"button\" (click)=\"sendEmail()\">Send email</button>\n } @if (inv.status.toString() !== 'SENT' && inv.status.toString() !== 'PAID') {\n <button mat-flat-button (click)=\"edit()\">Edit</button>\n }\n </div>\n\n <!-- Mobile actions -->\n <div class=\"lg:hidden shrink-0\">\n <button mat-icon-button type=\"button\" [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n </div>\n</div>\n\n<!-- Body -->\n<div class=\"p-4 space-y-4\">\n <div class=\"grid grid-cols-1 lg:grid-cols-3 gap-4\">\n <!-- Left column -->\n <div class=\"grid lg:col-span-2 space-y-4\">\n <!-- Customer -->\n <div class=\"rounded-xl border border-[--rt-border-color] bg-[--rt-background] p-4\">\n <div class=\"flex items-center justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Customer</div>\n <div class=\"text-sm text-[--rt-text-secondary]\">{{ inv.firstName }} {{ inv.lastName }} \u2022 {{ inv.email }}</div>\n </div>\n </div>\n\n <div class=\"mt-3 grid grid-cols-1 sm:grid-cols-2 gap-3 text-sm\">\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Phone</div>\n <div class=\"font-medium\">{{ inv.phone || '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Currency</div>\n <div class=\"font-medium\">{{ inv.currency }}</div>\n </div>\n </div>\n </div>\n\n <!-- Lines -->\n\n @if (inv.status.toString() !== 'SENT' && inv.status.toString() !== 'PAID') {\n <rolatech-invoice-manage-line></rolatech-invoice-manage-line>\n } @else {\n <rolatech-invoice-lines [lines]=\"store.lines()\"></rolatech-invoice-lines>\n }\n\n <!-- Notes (optional) -->\n @if (inv.note) {\n <div class=\"rounded-xl border border-[--rt-border-color] p-4\">\n <div class=\"text-sm font-semibold\">Notes</div>\n <div class=\"mt-2 text-sm text-[--rt-text-secondary] whitespace-pre-wrap\">{{ inv.note }}</div>\n </div>\n }\n </div>\n\n <!-- Right rail -->\n <div class=\"space-y-4\">\n <div class=\"rounded-xl border border-[--rt-border-color] bg-[--rt-background] p-4\">\n <div class=\"text-sm font-semibold\">Summary</div>\n\n @if (store.totals(); as t) {\n <div class=\"mt-3 space-y-2 text-sm\">\n <div class=\"flex justify-between\">\n <span class=\"text-[--rt-text-secondary]\">Subtotal</span>\n <span class=\"font-medium\">{{ t.subtotal | price }}</span>\n </div>\n\n <div class=\"flex justify-between\">\n <span class=\"text-[--rt-text-secondary]\">VAT</span>\n <span class=\"font-medium\">{{ t.vatTotal | price }}</span>\n </div>\n\n @if (inv.holdingDepositApplied) {\n <div class=\"flex justify-between\">\n <span class=\"text-[--rt-text-secondary]\">Credit</span>\n <span class=\"font-medium\">-{{ inv.holdingDepositApplied | price }}</span>\n </div>\n }\n\n <div class=\"pt-2 mt-2 border-t border-[--rt-border-color] flex justify-between text-base\">\n <span class=\"font-semibold\">Total</span>\n <span class=\"font-semibold\">{{ t.total | price }}</span>\n </div>\n </div>\n }\n </div>\n <div class=\"rounded-xl border border-[--rt-border-color] bg-[--rt-background] p-4\">\n <div class=\"text-sm font-semibold\">Payment</div>\n <div class=\"mt-2 text-sm text-[--rt-text-secondary]\">\n Status:\n <span class=\"font-medium text-[--rt-text-primary]\">{{ inv.status.toString() === 'PAID' ? 'Paid': 'Unpaid' }}</span>\n </div>\n @if (inv.paidAt) {\n <div class=\"mt-1 text-sm text-[--rt-text-secondary]\">\n Paid: <span class=\"font-medium text-[--rt-text-primary]\">{{ inv.paidAt | date:'dd/MM/yyyy':'Europe/London' }}</span>\n </div>\n }\n </div>\n </div>\n </div>\n</div>\n\n<mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n @if (inv.pdfUrl) {\n <button mat-menu-item type=\"button\" (click)=\"openPdf()\">Preview PDF</button>\n } @if (inv.status.toString() === 'ISSUED') {\n <button mat-menu-item type=\"button\" (click)=\"sendEmail()\">Send email</button>\n } @if (inv.status.toString() !== 'SENT' && inv.status.toString() !== 'PAID') {\n <button mat-menu-item type=\"button\" (click)=\"edit()\">Edit</button>\n\n }\n</mat-menu>\n} }\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MaterialModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i3$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i3$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i3$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: InvoiceLines, selector: "rolatech-invoice-lines", inputs: ["lines"] }, { kind: "component", type: InvoiceManageLine, selector: "rolatech-invoice-manage-line" }, { kind: "pipe", type: i1.DatePipe, name: "date" }, { kind: "pipe", type: PricePipe, name: "price" }] }); }
|
|
783
1206
|
}
|
|
784
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
1207
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceManageDetail, decorators: [{
|
|
785
1208
|
type: Component,
|
|
786
|
-
args: [{ selector: 'rolatech-invoice-manage-detail', imports: [
|
|
787
|
-
CommonModule,
|
|
788
|
-
RouterLink,
|
|
789
|
-
MaterialModule,
|
|
790
|
-
MatMenuModule,
|
|
791
|
-
InvoiceSummary,
|
|
792
|
-
InvoiceLines,
|
|
793
|
-
InvoiceUser,
|
|
794
|
-
ToolbarComponent,
|
|
795
|
-
], template: "<!-- Content -->\n@if (loading()) {\n<div class=\"flex justify-center py-16\">Loading invoice\u2026</div>\n} @else if (!invoice()) {\n<div class=\"flex justify-center py-16\">Invoice not found.</div>\n} @else() { @if (invoice(); as invoice) {\n<rolatech-toolbar [title]=\"status[invoice.status]\" large link=\"../\">\n <div class=\"hidden items-center gap-2 lg:flex\">\n @if (invoice.pdfUrl) {\n <button mat-stroked-button (click)=\"openPdf()\">Preview PDF</button>\n }\n <button mat-flat-button>Send Email</button>\n <button mat-flat-button (click)=\"edit()\">Edit</button>\n </div>\n <div class=\"block lg:hidden\">\n <button mat-icon-button [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n</rolatech-toolbar>\n<div class=\"px-4\">\n <!-- Breadcrumb / header -->\n <div class=\"flex items-start justify-between gap-4\">\n <div class=\"space-y-1\">\n <nav class=\"flex items-center gap-1\">\n <a routerLink=\"/invoices\" class=\"hover:underline hover:cursor-pointer\">Invoices</a>\n <span>/</span>\n <span>{{invoice?.invoiceNumber ?? invoice!.id}}</span>\n </nav>\n\n <div class=\"flex items-center gap-2 text-sm\">\n <span>Created: {{ invoice.createdAt | date:'dd/MM/yyyy':'Europe/London'}}</span>\n </div>\n </div>\n </div>\n</div>\n<div class=\"p-4 lg:p-4 space-y-6\">\n <div class=\"grid grid-cols-1 lg:grid-cols-3 gap-4\">\n <div class=\"grid lg:col-span-2 space-y-4\">\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 @if (invoice?.lines; as lines) {\n <rolatech-invoice-lines [lines]=\"lines\"></rolatech-invoice-lines>\n }\n </div>\n <div class=\"space-y-4\">\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 </div>\n </div>\n</div>\n<mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n @if (invoice?.pdfUrl) {\n <button mat-menu-item (click)=\"openPdf()\">Preview PDF</button>\n }\n <button mat-menu-item>Send Email</button>\n <button mat-menu-item (click)=\"edit()\">Edit</button>\n</mat-menu>\n\n} }\n" }]
|
|
1209
|
+
args: [{ selector: 'rolatech-invoice-manage-detail', standalone: true, imports: [CommonModule, MaterialModule, MatMenuModule, InvoiceLines, InvoiceManageLine, PricePipe], template: "@if (store.status() === 'loading') {\n<div class=\"flex justify-center py-16\">Loading invoice\u2026</div>\n} @else if (!store.invoice()) {\n<div class=\"flex justify-center py-16\">Invoice not found.</div>\n} @else { @if (store.invoice(); as inv) {\n\n<!-- Sticky header -->\n<div class=\"sticky top-0 z-10 border-b border-[--rt-border-color] bg-[--rt-background]\">\n <div class=\"px-4 py-3 flex items-start justify-between gap-4\">\n <div class=\"min-w-0\">\n <div class=\"flex items-center gap-2\">\n <h1 class=\"text-lg font-semibold truncate\">Invoice {{ inv.invoiceNumber || inv.id }}</h1>\n\n <span class=\"px-2 py-0.5 rounded-full text-xs font-semibold\" [class]=\"statusBadgeClass(inv.status)\">\n {{ inv.status }}\n </span>\n\n @if (store.dirty() ) {\n <span class=\"text-xs text-[--rt-text-secondary]\">\u2022 Unsaved changes</span>\n }\n </div>\n\n <div class=\"mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-sm text-[--rt-text-secondary]\">\n <span class=\"truncate\"> {{ inv.firstName }} {{ inv.lastName }} \u2022 {{ inv.email }} </span>\n <span>Created {{ inv.createdAt | date:'dd/MM/yyyy':'Europe/London' }}</span>\n @if (inv.dueAt) { <span>Due {{ inv.dueAt | date:'dd/MM/yyyy':'Europe/London' }}</span> }\n </div>\n </div>\n\n <!-- Desktop actions -->\n\n <div class=\"hidden lg:flex items-center gap-2 shrink-0\">\n @if (inv.pdfUrl) {\n <button mat-stroked-button type=\"button\" (click)=\"openPdf()\">Preview PDF</button>\n } @if (inv.status.toString() === 'ISSUED') {\n <button mat-flat-button type=\"button\" (click)=\"sendEmail()\">Send email</button>\n } @if (inv.status.toString() !== 'SENT' && inv.status.toString() !== 'PAID') {\n <button mat-flat-button (click)=\"edit()\">Edit</button>\n }\n </div>\n\n <!-- Mobile actions -->\n <div class=\"lg:hidden shrink-0\">\n <button mat-icon-button type=\"button\" [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n </div>\n</div>\n\n<!-- Body -->\n<div class=\"p-4 space-y-4\">\n <div class=\"grid grid-cols-1 lg:grid-cols-3 gap-4\">\n <!-- Left column -->\n <div class=\"grid lg:col-span-2 space-y-4\">\n <!-- Customer -->\n <div class=\"rounded-xl border border-[--rt-border-color] bg-[--rt-background] p-4\">\n <div class=\"flex items-center justify-between\">\n <div>\n <div class=\"text-sm font-semibold\">Customer</div>\n <div class=\"text-sm text-[--rt-text-secondary]\">{{ inv.firstName }} {{ inv.lastName }} \u2022 {{ inv.email }}</div>\n </div>\n </div>\n\n <div class=\"mt-3 grid grid-cols-1 sm:grid-cols-2 gap-3 text-sm\">\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Phone</div>\n <div class=\"font-medium\">{{ inv.phone || '\u2014' }}</div>\n </div>\n <div>\n <div class=\"text-xs text-[--rt-text-secondary]\">Currency</div>\n <div class=\"font-medium\">{{ inv.currency }}</div>\n </div>\n </div>\n </div>\n\n <!-- Lines -->\n\n @if (inv.status.toString() !== 'SENT' && inv.status.toString() !== 'PAID') {\n <rolatech-invoice-manage-line></rolatech-invoice-manage-line>\n } @else {\n <rolatech-invoice-lines [lines]=\"store.lines()\"></rolatech-invoice-lines>\n }\n\n <!-- Notes (optional) -->\n @if (inv.note) {\n <div class=\"rounded-xl border border-[--rt-border-color] p-4\">\n <div class=\"text-sm font-semibold\">Notes</div>\n <div class=\"mt-2 text-sm text-[--rt-text-secondary] whitespace-pre-wrap\">{{ inv.note }}</div>\n </div>\n }\n </div>\n\n <!-- Right rail -->\n <div class=\"space-y-4\">\n <div class=\"rounded-xl border border-[--rt-border-color] bg-[--rt-background] p-4\">\n <div class=\"text-sm font-semibold\">Summary</div>\n\n @if (store.totals(); as t) {\n <div class=\"mt-3 space-y-2 text-sm\">\n <div class=\"flex justify-between\">\n <span class=\"text-[--rt-text-secondary]\">Subtotal</span>\n <span class=\"font-medium\">{{ t.subtotal | price }}</span>\n </div>\n\n <div class=\"flex justify-between\">\n <span class=\"text-[--rt-text-secondary]\">VAT</span>\n <span class=\"font-medium\">{{ t.vatTotal | price }}</span>\n </div>\n\n @if (inv.holdingDepositApplied) {\n <div class=\"flex justify-between\">\n <span class=\"text-[--rt-text-secondary]\">Credit</span>\n <span class=\"font-medium\">-{{ inv.holdingDepositApplied | price }}</span>\n </div>\n }\n\n <div class=\"pt-2 mt-2 border-t border-[--rt-border-color] flex justify-between text-base\">\n <span class=\"font-semibold\">Total</span>\n <span class=\"font-semibold\">{{ t.total | price }}</span>\n </div>\n </div>\n }\n </div>\n <div class=\"rounded-xl border border-[--rt-border-color] bg-[--rt-background] p-4\">\n <div class=\"text-sm font-semibold\">Payment</div>\n <div class=\"mt-2 text-sm text-[--rt-text-secondary]\">\n Status:\n <span class=\"font-medium text-[--rt-text-primary]\">{{ inv.status.toString() === 'PAID' ? 'Paid': 'Unpaid' }}</span>\n </div>\n @if (inv.paidAt) {\n <div class=\"mt-1 text-sm text-[--rt-text-secondary]\">\n Paid: <span class=\"font-medium text-[--rt-text-primary]\">{{ inv.paidAt | date:'dd/MM/yyyy':'Europe/London' }}</span>\n </div>\n }\n </div>\n </div>\n </div>\n</div>\n\n<mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n @if (inv.pdfUrl) {\n <button mat-menu-item type=\"button\" (click)=\"openPdf()\">Preview PDF</button>\n } @if (inv.status.toString() === 'ISSUED') {\n <button mat-menu-item type=\"button\" (click)=\"sendEmail()\">Send email</button>\n } @if (inv.status.toString() !== 'SENT' && inv.status.toString() !== 'PAID') {\n <button mat-menu-item type=\"button\" (click)=\"edit()\">Edit</button>\n\n }\n</mat-menu>\n} }\n" }]
|
|
796
1210
|
}] });
|
|
797
1211
|
|
|
798
1212
|
class InvoiceManageDownload {
|
|
799
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
800
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.
|
|
1213
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceManageDownload, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1214
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: InvoiceManageDownload, isStandalone: true, selector: "rolatech-invoice-manage-download", ngImport: i0, template: "<p>invoice-manage-download works!</p>\n", styles: [""] }); }
|
|
801
1215
|
}
|
|
802
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
1216
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: InvoiceManageDownload, decorators: [{
|
|
803
1217
|
type: Component,
|
|
804
1218
|
args: [{ selector: 'rolatech-invoice-manage-download', imports: [], template: "<p>invoice-manage-download works!</p>\n" }]
|
|
805
1219
|
}] });
|
|
@@ -955,10 +1369,10 @@ class AgentInvoiceIndex extends BaseComponent {
|
|
|
955
1369
|
return 'bg-gray-100 text-gray-700';
|
|
956
1370
|
}
|
|
957
1371
|
}
|
|
958
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
959
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.
|
|
1372
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AgentInvoiceIndex, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
1373
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", 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 [label]=\"item.name\" routerLink=\"./\" [queryParams]=\"{ status: item.status }\"></rolatech-tab>\n } @else {\n <rolatech-tab [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 [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"], outputs: ["selectChange"] }, { kind: "component", type: TabComponent, selector: "rolatech-tab", inputs: ["label"] }, { 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" }] }); }
|
|
960
1374
|
}
|
|
961
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
1375
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AgentInvoiceIndex, decorators: [{
|
|
962
1376
|
type: Component,
|
|
963
1377
|
args: [{ selector: 'rolatech-agent-invoice-index', imports: [
|
|
964
1378
|
ContainerComponent,
|
|
@@ -1044,10 +1458,10 @@ class AgentInvoiceDetail extends BaseComponent {
|
|
|
1044
1458
|
},
|
|
1045
1459
|
});
|
|
1046
1460
|
}
|
|
1047
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.
|
|
1048
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.
|
|
1461
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AgentInvoiceDetail, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
1462
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: AgentInvoiceDetail, isStandalone: true, selector: "rolatech-agent-invoice-detail", usesInheritance: true, ngImport: i0, template: "<!-- Content -->\n@if (loading()) {\n<div class=\"flex justify-center py-16\">Loading invoice\u2026</div>\n} @else if (!invoice()) {\n<div class=\"flex justify-center py-16\">Invoice not found.</div>\n} @else() { @if (invoice(); as invoice) {\n<rolatech-toolbar [title]=\"status[invoice.status]\" large link=\"../\">\n <div class=\"hidden items-center gap-2 lg:flex\">\n @if (invoice.pdfUrl) {\n <button mat-stroked-button (click)=\"openPdf()\">Preview PDF</button>\n }\n <button mat-flat-button>Send Email</button>\n <button mat-flat-button (click)=\"edit()\">Edit</button>\n </div>\n <div class=\"block lg:hidden\">\n <button mat-icon-button [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n</rolatech-toolbar>\n<div class=\"px-4\">\n <!-- Breadcrumb / header -->\n <div class=\"flex items-start justify-between gap-4\">\n <div class=\"space-y-1\">\n <nav class=\"flex items-center gap-1\">\n <a routerLink=\"/invoices\" class=\"hover:underline hover:cursor-pointer\">Invoices</a>\n <span>/</span>\n <span>{{invoice?.invoiceNumber ?? invoice!.id}}</span>\n </nav>\n\n <div class=\"flex items-center gap-2 text-sm\">\n <span>Created: {{ invoice.createdAt | date:'dd/MM/yyyy':'Europe/London'}}</span>\n </div>\n </div>\n </div>\n</div>\n<div class=\"p-4\">\n <div class=\"grid grid-cols-1 lg:grid-cols-3 gap-4\">\n <div class=\"grid lg:col-span-2 space-y-4\">\n @if (invoice.status.toString() === 'PAID') {\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 <rolatech-invoice-lines></rolatech-invoice-lines>\n\n } @if (invoice?.lines; as lines) {\n <!-- <rolatech-invoice-lines [lines]=\"lines\"></rolatech-invoice-lines> -->\n }\n </div>\n <div class=\"space-y-4\">\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 </div>\n </div>\n</div>\n<mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n @if (invoice?.pdfUrl) {\n <button mat-menu-item (click)=\"openPdf()\">Preview PDF</button>\n }\n <button mat-menu-item>Send Email</button>\n <button mat-menu-item (click)=\"edit()\">Edit</button>\n</mat-menu>\n\n} }\n", styles: [""], 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: "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: MatMenuModule }, { kind: "component", type: i3$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i3$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i3$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "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: "component", type: ToolbarComponent, selector: "rolatech-toolbar", inputs: ["title", "subtitle", "back", "link", "large", "divider"] }, { kind: "pipe", type: i1.DatePipe, name: "date" }] }); }
|
|
1049
1463
|
}
|
|
1050
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.
|
|
1464
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: AgentInvoiceDetail, decorators: [{
|
|
1051
1465
|
type: Component,
|
|
1052
1466
|
args: [{ selector: 'rolatech-agent-invoice-detail', imports: [
|
|
1053
1467
|
CommonModule,
|
|
@@ -1059,7 +1473,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImpor
|
|
|
1059
1473
|
InvoiceLines,
|
|
1060
1474
|
InvoiceUser,
|
|
1061
1475
|
ToolbarComponent,
|
|
1062
|
-
], template: "<!-- Content -->\n@if (loading()) {\n<div class=\"flex justify-center py-16\">Loading invoice\u2026</div>\n} @else if (!invoice()) {\n<div class=\"flex justify-center py-16\">Invoice not found.</div>\n} @else() { @if (invoice(); as invoice) {\n<rolatech-toolbar [title]=\"status[invoice.status]\" large link=\"../\">\n <div class=\"hidden items-center gap-2 lg:flex\">\n @if (invoice.pdfUrl) {\n <button mat-stroked-button (click)=\"openPdf()\">Preview PDF</button>\n }\n <button mat-flat-button>Send Email</button>\n <button mat-flat-button (click)=\"edit()\">Edit</button>\n </div>\n <div class=\"block lg:hidden\">\n <button mat-icon-button [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n</rolatech-toolbar>\n<div class=\"px-4\">\n <!-- Breadcrumb / header -->\n <div class=\"flex items-start justify-between gap-4\">\n <div class=\"space-y-1\">\n <nav class=\"flex items-center gap-1\">\n <a routerLink=\"/invoices\" class=\"hover:underline hover:cursor-pointer\">Invoices</a>\n <span>/</span>\n <span>{{invoice?.invoiceNumber ?? invoice!.id}}</span>\n </nav>\n\n <div class=\"flex items-center gap-2 text-sm\">\n <span>Created: {{ invoice.createdAt | date:'dd/MM/yyyy':'Europe/London'}}</span>\n </div>\n </div>\n </div>\n</div>\n<div class=\"p-4\">\n <div class=\"grid grid-cols-1 lg:grid-cols-3 gap-4\">\n <div class=\"grid lg:col-span-2 space-y-4\">\n @if (invoice.status.toString() === 'PAID') {\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 } @if (invoice?.lines; as lines) {\n <rolatech-invoice-lines [lines]=\"lines\"></rolatech-invoice-lines
|
|
1476
|
+
], template: "<!-- Content -->\n@if (loading()) {\n<div class=\"flex justify-center py-16\">Loading invoice\u2026</div>\n} @else if (!invoice()) {\n<div class=\"flex justify-center py-16\">Invoice not found.</div>\n} @else() { @if (invoice(); as invoice) {\n<rolatech-toolbar [title]=\"status[invoice.status]\" large link=\"../\">\n <div class=\"hidden items-center gap-2 lg:flex\">\n @if (invoice.pdfUrl) {\n <button mat-stroked-button (click)=\"openPdf()\">Preview PDF</button>\n }\n <button mat-flat-button>Send Email</button>\n <button mat-flat-button (click)=\"edit()\">Edit</button>\n </div>\n <div class=\"block lg:hidden\">\n <button mat-icon-button [matMenuTriggerFor]=\"moreMenu\">\n <mat-icon>more_vert</mat-icon>\n </button>\n </div>\n</rolatech-toolbar>\n<div class=\"px-4\">\n <!-- Breadcrumb / header -->\n <div class=\"flex items-start justify-between gap-4\">\n <div class=\"space-y-1\">\n <nav class=\"flex items-center gap-1\">\n <a routerLink=\"/invoices\" class=\"hover:underline hover:cursor-pointer\">Invoices</a>\n <span>/</span>\n <span>{{invoice?.invoiceNumber ?? invoice!.id}}</span>\n </nav>\n\n <div class=\"flex items-center gap-2 text-sm\">\n <span>Created: {{ invoice.createdAt | date:'dd/MM/yyyy':'Europe/London'}}</span>\n </div>\n </div>\n </div>\n</div>\n<div class=\"p-4\">\n <div class=\"grid grid-cols-1 lg:grid-cols-3 gap-4\">\n <div class=\"grid lg:col-span-2 space-y-4\">\n @if (invoice.status.toString() === 'PAID') {\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 <rolatech-invoice-lines></rolatech-invoice-lines>\n\n } @if (invoice?.lines; as lines) {\n <!-- <rolatech-invoice-lines [lines]=\"lines\"></rolatech-invoice-lines> -->\n }\n </div>\n <div class=\"space-y-4\">\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 </div>\n </div>\n</div>\n<mat-menu #moreMenu=\"matMenu\" xPosition=\"after\" class=\"divide-y divide-light-900\">\n @if (invoice?.pdfUrl) {\n <button mat-menu-item (click)=\"openPdf()\">Preview PDF</button>\n }\n <button mat-menu-item>Send Email</button>\n <button mat-menu-item (click)=\"edit()\">Edit</button>\n</mat-menu>\n\n} }\n" }]
|
|
1063
1477
|
}] });
|
|
1064
1478
|
|
|
1065
1479
|
const agentInvoiceRoutes = [
|