@retalia/pos-components 0.0.4 → 0.0.6

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.
Files changed (49) hide show
  1. package/README.md +24 -1
  2. package/fesm2022/retalia-pos-components-services.mjs +171 -0
  3. package/fesm2022/retalia-pos-components-services.mjs.map +1 -0
  4. package/fesm2022/retalia-pos-components.mjs +1089 -0
  5. package/fesm2022/retalia-pos-components.mjs.map +1 -0
  6. package/package.json +43 -17
  7. package/types/retalia-pos-components-services.d.ts +168 -0
  8. package/types/retalia-pos-components.d.ts +300 -0
  9. package/eslint.config.js +0 -32
  10. package/ng-package.json +0 -14
  11. package/src/lib/basket/pos-basket.html +0 -124
  12. package/src/lib/basket/pos-basket.scss +0 -285
  13. package/src/lib/basket/pos-basket.spec.ts +0 -198
  14. package/src/lib/basket/pos-basket.stories.ts +0 -114
  15. package/src/lib/basket/pos-basket.ts +0 -142
  16. package/src/lib/item-entry/pos-item-entry.html +0 -41
  17. package/src/lib/item-entry/pos-item-entry.scss +0 -145
  18. package/src/lib/item-entry/pos-item-entry.spec.ts +0 -123
  19. package/src/lib/item-entry/pos-item-entry.stories.ts +0 -31
  20. package/src/lib/item-entry/pos-item-entry.ts +0 -81
  21. package/src/lib/login/pos-login.html +0 -82
  22. package/src/lib/login/pos-login.scss +0 -238
  23. package/src/lib/login/pos-login.spec.ts +0 -89
  24. package/src/lib/login/pos-login.stories.ts +0 -36
  25. package/src/lib/login/pos-login.ts +0 -150
  26. package/src/lib/receipt/pos-receipt.html +0 -116
  27. package/src/lib/receipt/pos-receipt.scss +0 -367
  28. package/src/lib/receipt/pos-receipt.spec.ts +0 -188
  29. package/src/lib/receipt/pos-receipt.stories.ts +0 -163
  30. package/src/lib/receipt/pos-receipt.ts +0 -129
  31. package/src/lib/tender/pos-tender.html +0 -79
  32. package/src/lib/tender/pos-tender.scss +0 -318
  33. package/src/lib/tender/pos-tender.spec.ts +0 -150
  34. package/src/lib/tender/pos-tender.stories.ts +0 -47
  35. package/src/lib/tender/pos-tender.ts +0 -154
  36. package/src/lib/tokens/pos-theme-css.spec.ts +0 -75
  37. package/src/lib/tokens/pos-theme-css.ts +0 -123
  38. package/src/lib/tokens/pos-token-catalog.ts +0 -514
  39. package/src/lib/tokens/pos-tokens.html +0 -351
  40. package/src/lib/tokens/pos-tokens.scss +0 -437
  41. package/src/lib/tokens/pos-tokens.spec.ts +0 -164
  42. package/src/lib/tokens/pos-tokens.stories.ts +0 -22
  43. package/src/lib/tokens/pos-tokens.ts +0 -208
  44. package/src/public-api.ts +0 -37
  45. package/tsconfig.lib.json +0 -14
  46. package/tsconfig.lib.prod.json +0 -11
  47. package/tsconfig.spec.json +0 -11
  48. /package/{src/styles → styles}/styles.css +0 -0
  49. /package/{src/styles → styles}/tokens.css +0 -0
@@ -0,0 +1,300 @@
1
+ import * as _angular_core from '@angular/core';
2
+
3
+ type PosLoginIntent = {
4
+ readonly type: 'login.key';
5
+ readonly key: string;
6
+ } | {
7
+ readonly type: 'login.backspace';
8
+ } | {
9
+ readonly type: 'login.next';
10
+ readonly clerkNumber: string;
11
+ } | {
12
+ readonly type: 'login.submit';
13
+ readonly clerkNumber: string;
14
+ readonly password: string;
15
+ } | {
16
+ readonly type: 'login.back';
17
+ };
18
+ /** Display-only login/session state from the host envelope — not an API DTO. */
19
+ type PosLoginSessionStatus = 'signedOut' | 'busy' | 'signedIn';
20
+ interface PosLoginSessionState {
21
+ readonly status: PosLoginSessionStatus;
22
+ readonly displayName?: string;
23
+ }
24
+ type LoginStage = 'clerkNumber' | 'password';
25
+ declare class PosLogin {
26
+ readonly errorMessage: _angular_core.InputSignal<string>;
27
+ readonly session: _angular_core.InputSignal<PosLoginSessionState>;
28
+ readonly titleClerkNumber: _angular_core.InputSignal<string>;
29
+ readonly titleClerkPassword: _angular_core.InputSignal<string>;
30
+ readonly nextLabel: _angular_core.InputSignal<string>;
31
+ readonly loginLabel: _angular_core.InputSignal<string>;
32
+ readonly action: _angular_core.OutputEmitterRef<PosLoginIntent>;
33
+ protected readonly keypadRows: readonly (readonly string[])[];
34
+ protected readonly inputValue: _angular_core.WritableSignal<string>;
35
+ protected readonly stage: _angular_core.WritableSignal<LoginStage>;
36
+ private readonly clerkNumber;
37
+ protected readonly title: _angular_core.Signal<string>;
38
+ protected readonly maskInput: _angular_core.Signal<boolean>;
39
+ protected readonly showBack: _angular_core.Signal<boolean>;
40
+ protected readonly primaryLabel: _angular_core.Signal<string>;
41
+ protected readonly primaryEnabled: _angular_core.Signal<boolean>;
42
+ protected readonly interactionLocked: _angular_core.Signal<boolean>;
43
+ protected readonly sessionStatusText: _angular_core.Signal<string>;
44
+ protected onKeypad(key: string): void;
45
+ protected onPrimary(): void;
46
+ protected onBack(): void;
47
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<PosLogin, never>;
48
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<PosLogin, "pos-login", never, { "errorMessage": { "alias": "errorMessage"; "required": false; "isSignal": true; }; "session": { "alias": "session"; "required": false; "isSignal": true; }; "titleClerkNumber": { "alias": "titleClerkNumber"; "required": false; "isSignal": true; }; "titleClerkPassword": { "alias": "titleClerkPassword"; "required": false; "isSignal": true; }; "nextLabel": { "alias": "nextLabel"; "required": false; "isSignal": true; }; "loginLabel": { "alias": "loginLabel"; "required": false; "isSignal": true; }; }, { "action": "action"; }, never, never, true, never>;
49
+ }
50
+
51
+ type PosBasketIntent = {
52
+ readonly type: 'basket.changeQuantity';
53
+ readonly lineId: number;
54
+ readonly qty: number;
55
+ } | {
56
+ readonly type: 'basket.voidLine';
57
+ readonly lineId: number;
58
+ } | {
59
+ readonly type: 'basket.clear';
60
+ };
61
+ /** Display-only VAT from the host envelope — not calculated here. */
62
+ interface PosBasketVat {
63
+ readonly rate: number;
64
+ readonly amount: number;
65
+ readonly included: boolean;
66
+ }
67
+ /** Display-only basket line from the host envelope — not an API DTO. */
68
+ interface PosBasketLine {
69
+ readonly lineId: number;
70
+ readonly sku: string;
71
+ readonly name: string;
72
+ readonly qty: number;
73
+ readonly unitPrice: number;
74
+ readonly lineTotal: number;
75
+ readonly vat: PosBasketVat;
76
+ readonly struckThrough: boolean;
77
+ }
78
+ /** Display-only totals from the host envelope — not calculated here. */
79
+ interface PosBasketTotals {
80
+ readonly gross: number;
81
+ readonly net: number;
82
+ readonly vat: number;
83
+ readonly discount: number;
84
+ readonly due: number;
85
+ readonly currency: string;
86
+ }
87
+ declare class PosBasket {
88
+ readonly lines: _angular_core.InputSignal<readonly PosBasketLine[]>;
89
+ readonly totals: _angular_core.InputSignal<PosBasketTotals>;
90
+ readonly disabled: _angular_core.InputSignal<boolean>;
91
+ readonly title: _angular_core.InputSignal<string>;
92
+ readonly emptyMessage: _angular_core.InputSignal<string>;
93
+ readonly clearLabel: _angular_core.InputSignal<string>;
94
+ readonly action: _angular_core.OutputEmitterRef<PosBasketIntent>;
95
+ protected readonly hasLines: _angular_core.Signal<boolean>;
96
+ protected readonly canClear: _angular_core.Signal<boolean>;
97
+ protected formatMoney(amount: number): string;
98
+ protected canChangeQuantity(line: PosBasketLine): boolean;
99
+ protected canDecrement(line: PosBasketLine): boolean;
100
+ protected canVoid(line: PosBasketLine): boolean;
101
+ protected onIncrement(line: PosBasketLine): void;
102
+ protected onDecrement(line: PosBasketLine): void;
103
+ protected onVoid(line: PosBasketLine): void;
104
+ protected onClear(): void;
105
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<PosBasket, never>;
106
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<PosBasket, "pos-basket", never, { "lines": { "alias": "lines"; "required": false; "isSignal": true; }; "totals": { "alias": "totals"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "emptyMessage": { "alias": "emptyMessage"; "required": false; "isSignal": true; }; "clearLabel": { "alias": "clearLabel"; "required": false; "isSignal": true; }; }, { "action": "action"; }, never, never, true, never>;
107
+ }
108
+
109
+ type PosItemEntryIntent = {
110
+ readonly type: 'itemEntry.submit';
111
+ readonly reference: string;
112
+ };
113
+ declare class PosItemEntry {
114
+ readonly disabled: _angular_core.InputSignal<boolean>;
115
+ readonly errorMessage: _angular_core.InputSignal<string>;
116
+ readonly autofocus: _angular_core.InputSignal<boolean>;
117
+ readonly label: _angular_core.InputSignal<string>;
118
+ readonly placeholder: _angular_core.InputSignal<string>;
119
+ readonly addLabel: _angular_core.InputSignal<string>;
120
+ readonly action: _angular_core.OutputEmitterRef<{
121
+ readonly type: "itemEntry.submit";
122
+ readonly reference: string;
123
+ }>;
124
+ private readonly itemInput;
125
+ protected readonly reference: _angular_core.WritableSignal<string>;
126
+ protected readonly canSubmit: _angular_core.Signal<boolean>;
127
+ constructor();
128
+ protected onInput(event: Event): void;
129
+ protected onSubmit(event: Event): void;
130
+ private focusInput;
131
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<PosItemEntry, never>;
132
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<PosItemEntry, "pos-item-entry", never, { "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "errorMessage": { "alias": "errorMessage"; "required": false; "isSignal": true; }; "autofocus": { "alias": "autofocus"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "addLabel": { "alias": "addLabel"; "required": false; "isSignal": true; }; }, { "action": "action"; }, never, never, true, never>;
133
+ }
134
+
135
+ type PosTenderIntent = {
136
+ readonly type: 'tender.cash';
137
+ readonly amount: number;
138
+ };
139
+ declare class PosTender {
140
+ /** Display-only amount due from the host envelope — not calculated here. */
141
+ readonly amountDue: _angular_core.InputSignal<number>;
142
+ /** Display-only change due from the host envelope — not calculated here. */
143
+ readonly changeDue: _angular_core.InputSignal<number | null>;
144
+ readonly currency: _angular_core.InputSignal<string>;
145
+ readonly disabled: _angular_core.InputSignal<boolean>;
146
+ readonly errorMessage: _angular_core.InputSignal<string>;
147
+ readonly title: _angular_core.InputSignal<string>;
148
+ readonly amountDueLabel: _angular_core.InputSignal<string>;
149
+ readonly tenderedLabel: _angular_core.InputSignal<string>;
150
+ readonly changeDueLabel: _angular_core.InputSignal<string>;
151
+ readonly tenderLabel: _angular_core.InputSignal<string>;
152
+ readonly processingLabel: _angular_core.InputSignal<string>;
153
+ readonly action: _angular_core.OutputEmitterRef<{
154
+ readonly type: "tender.cash";
155
+ readonly amount: number;
156
+ }>;
157
+ protected readonly keypadRows: readonly (readonly string[])[];
158
+ protected readonly amountInput: _angular_core.WritableSignal<string>;
159
+ protected readonly parsedAmount: _angular_core.Signal<number | null>;
160
+ protected readonly canTender: _angular_core.Signal<boolean>;
161
+ protected readonly hasChangeDue: _angular_core.Signal<boolean>;
162
+ protected readonly displayTendered: _angular_core.Signal<string>;
163
+ protected readonly statusText: _angular_core.Signal<string>;
164
+ protected formatMoney(amount: number): string;
165
+ protected onKeypad(key: string): void;
166
+ protected onTender(): void;
167
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<PosTender, never>;
168
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<PosTender, "pos-tender", never, { "amountDue": { "alias": "amountDue"; "required": false; "isSignal": true; }; "changeDue": { "alias": "changeDue"; "required": false; "isSignal": true; }; "currency": { "alias": "currency"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "errorMessage": { "alias": "errorMessage"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "amountDueLabel": { "alias": "amountDueLabel"; "required": false; "isSignal": true; }; "tenderedLabel": { "alias": "tenderedLabel"; "required": false; "isSignal": true; }; "changeDueLabel": { "alias": "changeDueLabel"; "required": false; "isSignal": true; }; "tenderLabel": { "alias": "tenderLabel"; "required": false; "isSignal": true; }; "processingLabel": { "alias": "processingLabel"; "required": false; "isSignal": true; }; }, { "action": "action"; }, never, never, true, never>;
169
+ }
170
+
171
+ interface PosReceiptIntent {
172
+ readonly type: 'receipt.newSale';
173
+ }
174
+ /** Display-only VAT from the host envelope — not calculated here. */
175
+ interface PosReceiptVat {
176
+ readonly rate: number;
177
+ readonly amount: number;
178
+ readonly included: boolean;
179
+ }
180
+ /** Display-only receipt line from the host envelope — not an API DTO. */
181
+ interface PosReceiptLine {
182
+ readonly lineId: number;
183
+ readonly sku: string;
184
+ readonly name: string;
185
+ readonly qty: number;
186
+ readonly unitPrice: number;
187
+ readonly lineTotal: number;
188
+ readonly vat: PosReceiptVat;
189
+ readonly struckThrough: boolean;
190
+ }
191
+ /** Display-only totals from the host envelope — not calculated here. */
192
+ interface PosReceiptTotals {
193
+ readonly gross: number;
194
+ readonly net: number;
195
+ readonly vat: number;
196
+ readonly discount: number;
197
+ readonly due: number;
198
+ readonly currency: string;
199
+ }
200
+ /** Display-only tender from the host envelope — not calculated here. */
201
+ interface PosReceiptPayment {
202
+ readonly tender: string;
203
+ readonly amount: number;
204
+ readonly tendered: number;
205
+ readonly change: number;
206
+ readonly state: string;
207
+ }
208
+ /** Display-only document metadata from the host envelope. */
209
+ interface PosReceiptDocument {
210
+ readonly number: string;
211
+ readonly receiptReady: boolean;
212
+ }
213
+ declare class PosReceipt {
214
+ readonly lines: _angular_core.InputSignal<readonly PosReceiptLine[]>;
215
+ readonly totals: _angular_core.InputSignal<PosReceiptTotals>;
216
+ readonly payments: _angular_core.InputSignal<readonly PosReceiptPayment[]>;
217
+ readonly document: _angular_core.InputSignal<PosReceiptDocument>;
218
+ readonly disabled: _angular_core.InputSignal<boolean>;
219
+ readonly title: _angular_core.InputSignal<string>;
220
+ readonly emptyMessage: _angular_core.InputSignal<string>;
221
+ readonly preparingLabel: _angular_core.InputSignal<string>;
222
+ readonly readyLabel: _angular_core.InputSignal<string>;
223
+ readonly newSaleLabel: _angular_core.InputSignal<string>;
224
+ readonly tenderLabel: _angular_core.InputSignal<string>;
225
+ readonly amountLabel: _angular_core.InputSignal<string>;
226
+ readonly tenderedLabel: _angular_core.InputSignal<string>;
227
+ readonly changeLabel: _angular_core.InputSignal<string>;
228
+ readonly action: _angular_core.OutputEmitterRef<PosReceiptIntent>;
229
+ protected readonly hasLines: _angular_core.Signal<boolean>;
230
+ protected readonly hasPayments: _angular_core.Signal<boolean>;
231
+ protected readonly hasDocumentNumber: _angular_core.Signal<boolean>;
232
+ protected readonly statusText: _angular_core.Signal<string>;
233
+ protected readonly canStartNewSale: _angular_core.Signal<boolean>;
234
+ protected formatMoney(amount: number): string;
235
+ protected onNewSale(): void;
236
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<PosReceipt, never>;
237
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<PosReceipt, "pos-receipt", never, { "lines": { "alias": "lines"; "required": false; "isSignal": true; }; "totals": { "alias": "totals"; "required": false; "isSignal": true; }; "payments": { "alias": "payments"; "required": false; "isSignal": true; }; "document": { "alias": "document"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "emptyMessage": { "alias": "emptyMessage"; "required": false; "isSignal": true; }; "preparingLabel": { "alias": "preparingLabel"; "required": false; "isSignal": true; }; "readyLabel": { "alias": "readyLabel"; "required": false; "isSignal": true; }; "newSaleLabel": { "alias": "newSaleLabel"; "required": false; "isSignal": true; }; "tenderLabel": { "alias": "tenderLabel"; "required": false; "isSignal": true; }; "amountLabel": { "alias": "amountLabel"; "required": false; "isSignal": true; }; "tenderedLabel": { "alias": "tenderedLabel"; "required": false; "isSignal": true; }; "changeLabel": { "alias": "changeLabel"; "required": false; "isSignal": true; }; }, { "action": "action"; }, never, never, true, never>;
238
+ }
239
+
240
+ /**
241
+ * Theme-lab catalog. Default values must stay in sync with `styles/tokens.css`.
242
+ * The lab never writes that file; edits are live overrides only.
243
+ */
244
+ type PosTokenKind = 'color' | 'length' | 'font-family' | 'font-weight' | 'other';
245
+ type PosTokenGroup = 'color' | 'radius' | 'space' | 'type' | 'control';
246
+ type PosTokenPreview = 'color' | 'radius' | 'space' | 'font-size' | 'font-family' | 'font-weight' | 'letter-spacing' | 'size';
247
+ interface PosTokenSwatch {
248
+ readonly name: string;
249
+ readonly variable: string;
250
+ }
251
+ interface PosTokenDefinition extends PosTokenSwatch {
252
+ readonly kind: PosTokenKind;
253
+ readonly group: PosTokenGroup;
254
+ readonly preview: PosTokenPreview;
255
+ readonly defaultValue: string;
256
+ }
257
+
258
+ interface PosTokenSelection {
259
+ readonly id: string;
260
+ readonly title: string;
261
+ readonly variables: readonly string[];
262
+ }
263
+ declare class PosTokens {
264
+ protected readonly colorTokens: readonly PosTokenDefinition[];
265
+ protected readonly radiusTokens: readonly PosTokenDefinition[];
266
+ protected readonly spaceTokens: readonly PosTokenDefinition[];
267
+ protected readonly typeTokens: readonly PosTokenDefinition[];
268
+ protected readonly controlTokens: readonly PosTokenDefinition[];
269
+ private readonly overrides;
270
+ protected readonly selection: _angular_core.WritableSignal<PosTokenSelection | null>;
271
+ protected readonly statusMessage: _angular_core.WritableSignal<string>;
272
+ protected readonly selectedTokens: _angular_core.Signal<readonly PosTokenDefinition[]>;
273
+ protected readonly hasOverrides: _angular_core.Signal<boolean>;
274
+ protected readonly themeStyle: _angular_core.Signal<string>;
275
+ /** Complete :root CSS for the consuming POS. Package defaults are not written. */
276
+ themeCss(): string;
277
+ /** Load a previously exported theme as live overrides. Does not mutate defaults. */
278
+ loadThemeCss(css: string): void;
279
+ resetTheme(): void;
280
+ protected exportThemeFile(): void;
281
+ protected onImportFile(event: Event): Promise<void>;
282
+ protected selectToken(token: PosTokenDefinition): void;
283
+ protected selectSample(sampleId: string): void;
284
+ protected clearSelection(): void;
285
+ protected isTokenSelected(variable: string): boolean;
286
+ protected isSampleSelected(sampleId: string): boolean;
287
+ protected isModified(variable: string): boolean;
288
+ protected currentValue(variable: string): string;
289
+ protected defaultValue(variable: string): string;
290
+ protected colorInputValue(variable: string): string | null;
291
+ protected tokenFieldId(variable: string): string;
292
+ protected onTokenInput(variable: string, event: Event): void;
293
+ protected resetToken(variable: string): void;
294
+ private setTokenValue;
295
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<PosTokens, never>;
296
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<PosTokens, "pos-tokens", never, {}, {}, never, never, true, never>;
297
+ }
298
+
299
+ export { PosBasket, PosItemEntry, PosLogin, PosReceipt, PosTender, PosTokens };
300
+ export type { PosBasketIntent, PosBasketLine, PosBasketTotals, PosBasketVat, PosItemEntryIntent, PosLoginIntent, PosLoginSessionState, PosLoginSessionStatus, PosReceiptDocument, PosReceiptIntent, PosReceiptLine, PosReceiptPayment, PosReceiptTotals, PosReceiptVat, PosTenderIntent, PosTokenSwatch };
package/eslint.config.js DELETED
@@ -1,32 +0,0 @@
1
- // @ts-check
2
- const { defineConfig } = require('eslint/config');
3
- const rootConfig = require('../../eslint.config.js');
4
-
5
- module.exports = defineConfig([
6
- ...rootConfig,
7
- {
8
- files: ['**/*.ts'],
9
- rules: {
10
- '@angular-eslint/directive-selector': [
11
- 'error',
12
- {
13
- type: 'attribute',
14
- prefix: 'pos',
15
- style: 'camelCase',
16
- },
17
- ],
18
- '@angular-eslint/component-selector': [
19
- 'error',
20
- {
21
- type: 'element',
22
- prefix: 'pos',
23
- style: 'kebab-case',
24
- },
25
- ],
26
- },
27
- },
28
- {
29
- files: ['**/*.html'],
30
- rules: {},
31
- },
32
- ]);
package/ng-package.json DELETED
@@ -1,14 +0,0 @@
1
- {
2
- "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3
- "dest": "../../dist/pos-components",
4
- "lib": {
5
- "entryFile": "src/public-api.ts"
6
- },
7
- "assets": [
8
- {
9
- "glob": "**/*",
10
- "input": "src/styles",
11
- "output": "styles"
12
- }
13
- ]
14
- }
@@ -1,124 +0,0 @@
1
- <section
2
- class="pos-basket__panel"
3
- [attr.aria-busy]="disabled() ? true : null"
4
- >
5
- <header class="pos-basket__header">
6
- <h2 class="pos-basket__title">{{ title() }}</h2>
7
- <p class="pos-basket__count">
8
- {{ lines().length }}
9
- {{ lines().length === 1 ? 'line' : 'lines' }}
10
- </p>
11
- </header>
12
-
13
- <ul class="pos-basket__lines" aria-label="Basket lines">
14
- @if (!hasLines()) {
15
- <li class="pos-basket__empty">{{ emptyMessage() }}</li>
16
- } @else {
17
- @for (line of lines(); track line.lineId) {
18
- <li>
19
- <article
20
- class="pos-basket__line"
21
- [class.pos-basket__line--voided]="line.struckThrough"
22
- >
23
- <div class="pos-basket__line-info">
24
- <h3 class="pos-basket__line-name">{{ line.name }}</h3>
25
- <p class="pos-basket__line-sku">{{ line.sku }}</p>
26
- @if (line.struckThrough) {
27
- <p class="pos-basket__line-voided">Voided</p>
28
- }
29
- </div>
30
-
31
- <div
32
- class="pos-basket__stepper"
33
- role="group"
34
- [attr.aria-label]="'Quantity for ' + line.name"
35
- >
36
- <button
37
- type="button"
38
- class="pos-basket__qty-btn"
39
- aria-label="Decrease quantity"
40
- [disabled]="!canDecrement(line)"
41
- (click)="onDecrement(line)"
42
- >
43
-
44
- </button>
45
- <span class="pos-basket__qty" aria-live="polite">{{ line.qty }}</span>
46
- <button
47
- type="button"
48
- class="pos-basket__qty-btn"
49
- aria-label="Increase quantity"
50
- [disabled]="!canChangeQuantity(line)"
51
- (click)="onIncrement(line)"
52
- >
53
- +
54
- </button>
55
- </div>
56
-
57
- <div class="pos-basket__line-prices">
58
- <p class="pos-basket__unit-price">{{ formatMoney(line.unitPrice) }}</p>
59
- <p class="pos-basket__line-total">{{ formatMoney(line.lineTotal) }}</p>
60
- </div>
61
-
62
- <button
63
- type="button"
64
- class="pos-basket__void"
65
- [attr.aria-label]="'Void ' + line.name"
66
- [disabled]="!canVoid(line)"
67
- (click)="onVoid(line)"
68
- >
69
- <svg
70
- xmlns="http://www.w3.org/2000/svg"
71
- width="18"
72
- height="18"
73
- viewBox="0 0 24 24"
74
- fill="none"
75
- aria-hidden="true"
76
- >
77
- <path
78
- d="M6 6l12 12M18 6 6 18"
79
- stroke="currentColor"
80
- stroke-width="2"
81
- stroke-linecap="round"
82
- />
83
- </svg>
84
- </button>
85
- </article>
86
- </li>
87
- }
88
- }
89
- </ul>
90
-
91
- <footer class="pos-basket__footer">
92
- <dl class="pos-basket__totals" aria-live="polite">
93
- <div class="pos-basket__total-row">
94
- <dt>Gross</dt>
95
- <dd>{{ formatMoney(totals().gross) }}</dd>
96
- </div>
97
- <div class="pos-basket__total-row">
98
- <dt>Net</dt>
99
- <dd>{{ formatMoney(totals().net) }}</dd>
100
- </div>
101
- <div class="pos-basket__total-row">
102
- <dt>VAT</dt>
103
- <dd>{{ formatMoney(totals().vat) }}</dd>
104
- </div>
105
- <div class="pos-basket__total-row">
106
- <dt>Discount</dt>
107
- <dd>{{ formatMoney(totals().discount) }}</dd>
108
- </div>
109
- <div class="pos-basket__total-row pos-basket__total-row--due">
110
- <dt>Due</dt>
111
- <dd>{{ formatMoney(totals().due) }}</dd>
112
- </div>
113
- </dl>
114
-
115
- <button
116
- type="button"
117
- class="pos-basket__clear"
118
- [disabled]="!canClear()"
119
- (click)="onClear()"
120
- >
121
- {{ clearLabel() }}
122
- </button>
123
- </footer>
124
- </section>