@retalia/pos-components 0.0.3 → 0.0.4

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 (45) hide show
  1. package/eslint.config.js +32 -0
  2. package/ng-package.json +14 -0
  3. package/package.json +17 -31
  4. package/src/lib/basket/pos-basket.html +124 -0
  5. package/src/lib/basket/pos-basket.scss +285 -0
  6. package/src/lib/basket/pos-basket.spec.ts +198 -0
  7. package/src/lib/basket/pos-basket.stories.ts +114 -0
  8. package/src/lib/basket/pos-basket.ts +142 -0
  9. package/src/lib/item-entry/pos-item-entry.html +41 -0
  10. package/src/lib/item-entry/pos-item-entry.scss +145 -0
  11. package/src/lib/item-entry/pos-item-entry.spec.ts +123 -0
  12. package/src/lib/item-entry/pos-item-entry.stories.ts +31 -0
  13. package/src/lib/item-entry/pos-item-entry.ts +81 -0
  14. package/src/lib/login/pos-login.html +82 -0
  15. package/src/lib/login/pos-login.scss +238 -0
  16. package/src/lib/login/pos-login.spec.ts +89 -0
  17. package/src/lib/login/pos-login.stories.ts +36 -0
  18. package/src/lib/login/pos-login.ts +150 -0
  19. package/src/lib/receipt/pos-receipt.html +116 -0
  20. package/src/lib/receipt/pos-receipt.scss +367 -0
  21. package/src/lib/receipt/pos-receipt.spec.ts +188 -0
  22. package/src/lib/receipt/pos-receipt.stories.ts +163 -0
  23. package/src/lib/receipt/pos-receipt.ts +129 -0
  24. package/src/lib/tender/pos-tender.html +79 -0
  25. package/src/lib/tender/pos-tender.scss +318 -0
  26. package/src/lib/tender/pos-tender.spec.ts +150 -0
  27. package/src/lib/tender/pos-tender.stories.ts +47 -0
  28. package/src/lib/tender/pos-tender.ts +154 -0
  29. package/src/lib/tokens/pos-theme-css.spec.ts +75 -0
  30. package/src/lib/tokens/pos-theme-css.ts +123 -0
  31. package/src/lib/tokens/pos-token-catalog.ts +514 -0
  32. package/src/lib/tokens/pos-tokens.html +351 -0
  33. package/src/lib/tokens/pos-tokens.scss +437 -0
  34. package/src/lib/tokens/pos-tokens.spec.ts +164 -0
  35. package/src/lib/tokens/pos-tokens.stories.ts +22 -0
  36. package/src/lib/tokens/pos-tokens.ts +208 -0
  37. package/src/public-api.ts +37 -0
  38. package/tsconfig.lib.json +14 -0
  39. package/tsconfig.lib.prod.json +11 -0
  40. package/tsconfig.spec.json +11 -0
  41. package/fesm2022/retalia-pos-components.mjs +0 -1089
  42. package/fesm2022/retalia-pos-components.mjs.map +0 -1
  43. package/types/retalia-pos-components.d.ts +0 -300
  44. /package/{styles → src/styles}/styles.css +0 -0
  45. /package/{styles → src/styles}/tokens.css +0 -0
@@ -0,0 +1,367 @@
1
+ :host {
2
+ display: flex;
3
+ flex-direction: column;
4
+ min-height: 0;
5
+ width: 100%;
6
+ height: 100%;
7
+ color: var(--pos-color-text);
8
+ font-family: var(--pos-font-family);
9
+ font-size: var(--pos-font-size-md);
10
+ }
11
+
12
+ .pos-receipt__panel {
13
+ display: flex;
14
+ flex-direction: column;
15
+ min-height: 0;
16
+ height: 100%;
17
+ padding: var(--pos-space-lg);
18
+ border: 1px solid var(--pos-color-border);
19
+ border-radius: var(--pos-radius-lg);
20
+ background:
21
+ radial-gradient(120% 80% at 100% 0%, rgb(0 194 178 / 0.08), transparent 55%),
22
+ var(--pos-color-panel);
23
+ }
24
+
25
+ .pos-receipt__header {
26
+ display: flex;
27
+ align-items: flex-start;
28
+ justify-content: space-between;
29
+ gap: var(--pos-space-md);
30
+ }
31
+
32
+ .pos-receipt__heading {
33
+ display: flex;
34
+ flex-direction: column;
35
+ gap: var(--pos-space-sm);
36
+ min-width: 0;
37
+ }
38
+
39
+ .pos-receipt__kicker {
40
+ margin: 0;
41
+ color: var(--pos-color-primary);
42
+ font-size: var(--pos-font-size-sm);
43
+ font-weight: var(--pos-font-weight-bold);
44
+ letter-spacing: 0.14em;
45
+ text-transform: uppercase;
46
+ }
47
+
48
+ .pos-receipt__title {
49
+ margin: 0;
50
+ font-family: var(--pos-font-family-display);
51
+ font-size: var(--pos-font-size-xl);
52
+ font-weight: var(--pos-font-weight-bold);
53
+ letter-spacing: -0.03em;
54
+ }
55
+
56
+ .pos-receipt__number {
57
+ margin: 0;
58
+ color: var(--pos-color-text-muted);
59
+ font-family: var(--pos-font-family-display);
60
+ font-size: var(--pos-font-size-sm);
61
+ font-weight: var(--pos-font-weight-semibold);
62
+ overflow-wrap: anywhere;
63
+ text-align: right;
64
+ }
65
+
66
+ .pos-receipt__status {
67
+ margin: var(--pos-space-md) 0 0;
68
+ color: var(--pos-color-text-muted);
69
+ font-size: var(--pos-font-size-sm);
70
+ font-weight: var(--pos-font-weight-semibold);
71
+ }
72
+
73
+ .pos-receipt__lines {
74
+ display: flex;
75
+ flex: 1 1 auto;
76
+ flex-direction: column;
77
+ gap: var(--pos-space-sm);
78
+ min-height: 8rem;
79
+ margin: var(--pos-space-md) 0 0;
80
+ padding: 0;
81
+ overflow-y: auto;
82
+ list-style: none;
83
+ }
84
+
85
+ .pos-receipt__empty {
86
+ margin: auto 0;
87
+ padding: var(--pos-space-2xl) var(--pos-space-md);
88
+ color: var(--pos-color-text-muted);
89
+ text-align: center;
90
+ }
91
+
92
+ .pos-receipt__line {
93
+ display: grid;
94
+ grid-template-columns: minmax(0, 1fr) auto;
95
+ grid-template-areas:
96
+ 'info qty'
97
+ 'prices prices';
98
+ align-items: center;
99
+ gap: var(--pos-space-xs) var(--pos-space-md);
100
+ padding: var(--pos-space-md);
101
+ border: 1px solid var(--pos-color-border);
102
+ border-radius: var(--pos-radius-md);
103
+ background: var(--pos-color-panel-elevated);
104
+ }
105
+
106
+ .pos-receipt__line-info {
107
+ grid-area: info;
108
+ min-width: 0;
109
+ }
110
+
111
+ .pos-receipt__line-name {
112
+ margin: 0;
113
+ font-size: var(--pos-font-size-md);
114
+ font-weight: var(--pos-font-weight-semibold);
115
+ overflow: hidden;
116
+ text-overflow: ellipsis;
117
+ white-space: nowrap;
118
+ }
119
+
120
+ .pos-receipt__line-sku,
121
+ .pos-receipt__line-voided {
122
+ margin: var(--pos-space-xs) 0 0;
123
+ font-size: var(--pos-font-size-sm);
124
+ }
125
+
126
+ .pos-receipt__line-sku {
127
+ color: var(--pos-color-text-muted);
128
+ }
129
+
130
+ .pos-receipt__line-voided {
131
+ color: var(--pos-color-danger);
132
+ font-weight: var(--pos-font-weight-semibold);
133
+ }
134
+
135
+ .pos-receipt__qty {
136
+ grid-area: qty;
137
+ margin: 0;
138
+ font-family: var(--pos-font-family-display);
139
+ font-weight: var(--pos-font-weight-bold);
140
+ font-variant-numeric: tabular-nums;
141
+ text-align: right;
142
+ }
143
+
144
+ .pos-receipt__line-prices {
145
+ grid-area: prices;
146
+ display: flex;
147
+ justify-content: space-between;
148
+ gap: var(--pos-space-md);
149
+ }
150
+
151
+ .pos-receipt__unit-price {
152
+ margin: 0;
153
+ color: var(--pos-color-text-muted);
154
+ font-size: var(--pos-font-size-sm);
155
+ }
156
+
157
+ .pos-receipt__line-total {
158
+ margin: 0;
159
+ font-family: var(--pos-font-family-display);
160
+ font-weight: var(--pos-font-weight-bold);
161
+ font-variant-numeric: tabular-nums;
162
+ }
163
+
164
+ .pos-receipt__line--voided .pos-receipt__line-name,
165
+ .pos-receipt__line--voided .pos-receipt__line-sku,
166
+ .pos-receipt__line--voided .pos-receipt__line-prices,
167
+ .pos-receipt__line--voided .pos-receipt__qty {
168
+ color: var(--pos-color-text-muted);
169
+ text-decoration: line-through;
170
+ }
171
+
172
+ .pos-receipt__summary {
173
+ display: flex;
174
+ flex-direction: column;
175
+ gap: var(--pos-space-md);
176
+ margin-top: var(--pos-space-md);
177
+ padding-top: var(--pos-space-md);
178
+ border-top: 1px solid var(--pos-color-border);
179
+ }
180
+
181
+ .pos-receipt__totals,
182
+ .pos-receipt__payment-amounts {
183
+ display: flex;
184
+ flex-direction: column;
185
+ gap: var(--pos-space-sm);
186
+ margin: 0;
187
+ }
188
+
189
+ .pos-receipt__row {
190
+ display: flex;
191
+ justify-content: space-between;
192
+ gap: var(--pos-space-md);
193
+ margin: 0;
194
+ color: var(--pos-color-text-muted);
195
+ font-size: var(--pos-font-size-sm);
196
+ }
197
+
198
+ .pos-receipt__row dt,
199
+ .pos-receipt__row dd {
200
+ margin: 0;
201
+ }
202
+
203
+ .pos-receipt__row dd {
204
+ font-variant-numeric: tabular-nums;
205
+ }
206
+
207
+ .pos-receipt__row--due,
208
+ .pos-receipt__row--change {
209
+ padding-top: var(--pos-space-sm);
210
+ border-top: 1px solid var(--pos-color-border);
211
+ color: var(--pos-color-text);
212
+ font-family: var(--pos-font-family-display);
213
+ font-size: var(--pos-font-size-lg);
214
+ font-weight: var(--pos-font-weight-bold);
215
+ }
216
+
217
+ .pos-receipt__tender {
218
+ display: flex;
219
+ flex-direction: column;
220
+ gap: var(--pos-space-sm);
221
+ }
222
+
223
+ .pos-receipt__tender-title {
224
+ margin: 0;
225
+ color: var(--pos-color-text-muted);
226
+ font-size: var(--pos-font-size-sm);
227
+ font-weight: var(--pos-font-weight-semibold);
228
+ letter-spacing: 0.08em;
229
+ text-transform: uppercase;
230
+ }
231
+
232
+ .pos-receipt__payments {
233
+ display: flex;
234
+ flex-direction: column;
235
+ gap: var(--pos-space-sm);
236
+ margin: 0;
237
+ padding: 0;
238
+ list-style: none;
239
+ }
240
+
241
+ .pos-receipt__payment {
242
+ display: flex;
243
+ flex-direction: column;
244
+ gap: var(--pos-space-sm);
245
+ padding: var(--pos-space-md) var(--pos-space-lg);
246
+ border: 1px solid var(--pos-color-success);
247
+ border-radius: var(--pos-radius-md);
248
+ background: color-mix(
249
+ in srgb,
250
+ var(--pos-color-success) 14%,
251
+ var(--pos-color-panel-elevated)
252
+ );
253
+ }
254
+
255
+ .pos-receipt__payment-header {
256
+ display: flex;
257
+ align-items: baseline;
258
+ justify-content: space-between;
259
+ gap: var(--pos-space-md);
260
+ }
261
+
262
+ .pos-receipt__payment-tender {
263
+ margin: 0;
264
+ font-size: var(--pos-font-size-md);
265
+ font-weight: var(--pos-font-weight-bold);
266
+ }
267
+
268
+ .pos-receipt__payment-state {
269
+ margin: 0;
270
+ color: var(--pos-color-text-muted);
271
+ font-size: var(--pos-font-size-sm);
272
+ font-weight: var(--pos-font-weight-semibold);
273
+ }
274
+
275
+ .pos-receipt__actions {
276
+ margin-top: var(--pos-space-md);
277
+ }
278
+
279
+ .pos-receipt__new-sale {
280
+ width: 100%;
281
+ min-height: var(--pos-button-min-height);
282
+ border: 0;
283
+ border-radius: var(--pos-radius-md);
284
+ background: var(--pos-color-primary);
285
+ color: var(--pos-color-on-primary);
286
+ font: inherit;
287
+ font-weight: var(--pos-font-weight-bold);
288
+ letter-spacing: 0.04em;
289
+ text-transform: uppercase;
290
+ cursor: pointer;
291
+ -webkit-tap-highlight-color: transparent;
292
+ }
293
+
294
+ .pos-receipt__new-sale:not(:disabled):active {
295
+ transform: translateY(1px);
296
+ }
297
+
298
+ .pos-receipt__new-sale:disabled {
299
+ background: var(--pos-color-disabled);
300
+ color: var(--pos-color-on-disabled);
301
+ cursor: not-allowed;
302
+ transform: none;
303
+ }
304
+
305
+ .pos-receipt__new-sale:focus-visible {
306
+ outline: 2px solid var(--pos-color-primary);
307
+ outline-offset: 2px;
308
+ }
309
+
310
+ @media (min-width: 768px) {
311
+ .pos-receipt__panel {
312
+ display: grid;
313
+ grid-template-columns: minmax(0, 1.2fr) minmax(16rem, 1fr);
314
+ grid-template-areas:
315
+ 'header header'
316
+ 'status status'
317
+ 'lines summary'
318
+ 'actions actions';
319
+ gap: var(--pos-space-lg);
320
+ padding: var(--pos-space-xl);
321
+ align-items: start;
322
+ }
323
+
324
+ .pos-receipt__header {
325
+ grid-area: header;
326
+ }
327
+
328
+ .pos-receipt__status {
329
+ grid-area: status;
330
+ margin: 0;
331
+ }
332
+
333
+ .pos-receipt__lines {
334
+ grid-area: lines;
335
+ margin: 0;
336
+ }
337
+
338
+ .pos-receipt__line {
339
+ grid-template-columns: minmax(0, 1fr) auto auto;
340
+ grid-template-areas: 'info qty prices';
341
+ gap: var(--pos-space-md);
342
+ }
343
+
344
+ .pos-receipt__line-prices {
345
+ flex-direction: column;
346
+ align-items: flex-end;
347
+ justify-content: center;
348
+ }
349
+
350
+ .pos-receipt__summary {
351
+ grid-area: summary;
352
+ margin: 0;
353
+ padding: 0;
354
+ border-top: 0;
355
+ }
356
+
357
+ .pos-receipt__actions {
358
+ grid-area: actions;
359
+ margin: 0;
360
+ }
361
+ }
362
+
363
+ @media (prefers-reduced-motion: reduce) {
364
+ .pos-receipt__new-sale {
365
+ transform: none;
366
+ }
367
+ }
@@ -0,0 +1,188 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import {
4
+ PosReceipt,
5
+ PosReceiptIntent,
6
+ PosReceiptLine,
7
+ PosReceiptPayment,
8
+ PosReceiptTotals,
9
+ } from './pos-receipt';
10
+
11
+ const COFFEE_LINE: PosReceiptLine = {
12
+ lineId: 1,
13
+ sku: 'SKU-001',
14
+ name: 'Coffee 250g',
15
+ qty: 2,
16
+ unitPrice: 6,
17
+ lineTotal: 12,
18
+ vat: { rate: 0.25, amount: 2.4, included: true },
19
+ struckThrough: false,
20
+ };
21
+
22
+ const COMPLETED_TOTALS: PosReceiptTotals = {
23
+ gross: 12,
24
+ net: 9.6,
25
+ vat: 2.4,
26
+ discount: 0,
27
+ due: 0,
28
+ currency: 'EUR',
29
+ };
30
+
31
+ const CASH_PAYMENT: PosReceiptPayment = {
32
+ tender: 'cash',
33
+ amount: 12,
34
+ tendered: 20,
35
+ change: 8,
36
+ state: 'captured',
37
+ };
38
+
39
+ describe('PosReceipt', () => {
40
+ let fixture: ComponentFixture<PosReceipt>;
41
+ let component: PosReceipt;
42
+
43
+ beforeEach(async () => {
44
+ await TestBed.configureTestingModule({
45
+ imports: [PosReceipt],
46
+ }).compileComponents();
47
+
48
+ fixture = TestBed.createComponent(PosReceipt);
49
+ component = fixture.componentInstance;
50
+ await fixture.whenStable();
51
+ });
52
+
53
+ it('should render the preparing receipt state by default', () => {
54
+ const compiled = fixture.nativeElement as HTMLElement;
55
+ expect(compiled.querySelector('.pos-receipt__title')?.textContent).toContain(
56
+ 'Receipt',
57
+ );
58
+ expect(
59
+ compiled.querySelector('.pos-receipt__status')?.textContent,
60
+ ).toContain('Preparing receipt');
61
+ expect(compiled.querySelector('.pos-receipt__empty')?.textContent).toContain(
62
+ 'No receipt lines',
63
+ );
64
+ expect(compiled.querySelector('.pos-receipt__number')).toBeNull();
65
+ expect(compiled.querySelector('.pos-receipt__payments')).toBeNull();
66
+ });
67
+
68
+ it('should render lines, totals, tender and change exactly as received without calculating', async () => {
69
+ const mismatched: PosReceiptLine = {
70
+ ...COFFEE_LINE,
71
+ qty: 2,
72
+ unitPrice: 6,
73
+ lineTotal: 99,
74
+ };
75
+ const totals: PosReceiptTotals = {
76
+ ...COMPLETED_TOTALS,
77
+ gross: 50,
78
+ due: 1,
79
+ };
80
+ const payment: PosReceiptPayment = {
81
+ ...CASH_PAYMENT,
82
+ amount: 12,
83
+ tendered: 20,
84
+ change: 3,
85
+ };
86
+
87
+ fixture.componentRef.setInput('lines', [mismatched]);
88
+ fixture.componentRef.setInput('totals', totals);
89
+ fixture.componentRef.setInput('payments', [payment]);
90
+ fixture.componentRef.setInput('document', {
91
+ number: 'ENV-999',
92
+ receiptReady: true,
93
+ });
94
+ await fixture.whenStable();
95
+
96
+ const compiled = fixture.nativeElement as HTMLElement;
97
+ expect(
98
+ compiled.querySelector('.pos-receipt__line-name')?.textContent,
99
+ ).toContain('Coffee 250g');
100
+ expect(
101
+ compiled.querySelector('.pos-receipt__line-sku')?.textContent,
102
+ ).toContain('SKU-001');
103
+ expect(compiled.querySelector('.pos-receipt__qty')?.textContent?.trim()).toBe(
104
+ '2',
105
+ );
106
+ expect(
107
+ compiled.querySelector('.pos-receipt__line-total')?.textContent,
108
+ ).toContain('99');
109
+ expect(
110
+ compiled.querySelector('.pos-receipt__line-total')?.textContent,
111
+ ).not.toContain('12');
112
+
113
+ const due = compiled.querySelector('.pos-receipt__row--due dd');
114
+ expect(due?.textContent).toContain('1');
115
+ expect(due?.textContent).not.toContain('50');
116
+
117
+ const change = compiled.querySelector('.pos-receipt__row--change dd');
118
+ expect(change?.textContent).toContain('3');
119
+ expect(change?.textContent).not.toContain('8');
120
+
121
+ expect(
122
+ compiled.querySelector('.pos-receipt__payment-tender')?.textContent,
123
+ ).toContain('cash');
124
+ expect(
125
+ compiled.querySelector('.pos-receipt__number')?.textContent,
126
+ ).toContain('ENV-999');
127
+ expect(
128
+ compiled.querySelector('.pos-receipt__status')?.textContent,
129
+ ).toContain('Receipt ready');
130
+ });
131
+
132
+ it('should emit receipt.newSale when the cashier starts a new sale', async () => {
133
+ const intents: PosReceiptIntent[] = [];
134
+ component.action.subscribe((intent) => intents.push(intent));
135
+
136
+ fixture.componentRef.setInput('lines', [COFFEE_LINE]);
137
+ fixture.componentRef.setInput('totals', COMPLETED_TOTALS);
138
+ fixture.componentRef.setInput('payments', [CASH_PAYMENT]);
139
+ fixture.componentRef.setInput('document', {
140
+ number: 'POS-1001',
141
+ receiptReady: true,
142
+ });
143
+ await fixture.whenStable();
144
+
145
+ const newSale = (fixture.nativeElement as HTMLElement).querySelector(
146
+ '.pos-receipt__new-sale',
147
+ ) as HTMLButtonElement;
148
+ newSale.click();
149
+ await fixture.whenStable();
150
+
151
+ expect(intents).toEqual([{ type: 'receipt.newSale' }]);
152
+ });
153
+
154
+ it('should not emit while disabled', async () => {
155
+ const intents: PosReceiptIntent[] = [];
156
+ component.action.subscribe((intent) => intents.push(intent));
157
+
158
+ fixture.componentRef.setInput('lines', [COFFEE_LINE]);
159
+ fixture.componentRef.setInput('disabled', true);
160
+ await fixture.whenStable();
161
+
162
+ const newSale = (fixture.nativeElement as HTMLElement).querySelector(
163
+ '.pos-receipt__new-sale',
164
+ ) as HTMLButtonElement;
165
+ newSale.click();
166
+ await fixture.whenStable();
167
+
168
+ expect(newSale.disabled).toBe(true);
169
+ expect(intents).toEqual([]);
170
+ });
171
+
172
+ it('should mark voided lines without recalculating totals', async () => {
173
+ fixture.componentRef.setInput('lines', [
174
+ { ...COFFEE_LINE, struckThrough: true },
175
+ ]);
176
+ fixture.componentRef.setInput('totals', COMPLETED_TOTALS);
177
+ await fixture.whenStable();
178
+
179
+ const compiled = fixture.nativeElement as HTMLElement;
180
+ expect(compiled.querySelector('.pos-receipt__line--voided')).toBeTruthy();
181
+ expect(
182
+ compiled.querySelector('.pos-receipt__line-voided')?.textContent,
183
+ ).toContain('Voided');
184
+ expect(
185
+ compiled.querySelector('.pos-receipt__row--due dd')?.textContent,
186
+ ).toContain('0');
187
+ });
188
+ });
@@ -0,0 +1,163 @@
1
+ import type { Meta, StoryObj } from '@storybook/angular-vite';
2
+
3
+ import {
4
+ PosReceipt,
5
+ PosReceiptLine,
6
+ PosReceiptPayment,
7
+ PosReceiptTotals,
8
+ } from './pos-receipt';
9
+
10
+ const coffee: PosReceiptLine = {
11
+ lineId: 1,
12
+ sku: 'SKU-001',
13
+ name: 'Coffee 250g',
14
+ qty: 2,
15
+ unitPrice: 6,
16
+ lineTotal: 12,
17
+ vat: { rate: 0.25, amount: 2.4, included: true },
18
+ struckThrough: false,
19
+ };
20
+
21
+ const pastry: PosReceiptLine = {
22
+ lineId: 2,
23
+ sku: 'SKU-014',
24
+ name: 'Almond croissant',
25
+ qty: 1,
26
+ unitPrice: 3.5,
27
+ lineTotal: 3.5,
28
+ vat: { rate: 0.2, amount: 0.58, included: true },
29
+ struckThrough: false,
30
+ };
31
+
32
+ const completedTotals: PosReceiptTotals = {
33
+ gross: 12,
34
+ net: 9.6,
35
+ vat: 2.4,
36
+ discount: 0,
37
+ due: 0,
38
+ currency: 'EUR',
39
+ };
40
+
41
+ const cashPayment: PosReceiptPayment = {
42
+ tender: 'cash',
43
+ amount: 12,
44
+ tendered: 20,
45
+ change: 8,
46
+ state: 'captured',
47
+ };
48
+
49
+ const meta: Meta<PosReceipt> = {
50
+ title: 'Receipt',
51
+ component: PosReceipt,
52
+ args: {
53
+ lines: [],
54
+ totals: {
55
+ gross: 0,
56
+ net: 0,
57
+ vat: 0,
58
+ discount: 0,
59
+ due: 0,
60
+ currency: 'EUR',
61
+ },
62
+ payments: [],
63
+ document: { number: '', receiptReady: false },
64
+ disabled: false,
65
+ },
66
+ };
67
+
68
+ export default meta;
69
+
70
+ type Story = StoryObj<PosReceipt>;
71
+
72
+ export const Preparing: Story = {};
73
+
74
+ export const CompletedCashSale: Story = {
75
+ args: {
76
+ lines: [coffee],
77
+ totals: completedTotals,
78
+ payments: [cashPayment],
79
+ document: { number: 'POS-1001', receiptReady: true },
80
+ },
81
+ };
82
+
83
+ export const MultipleLines: Story = {
84
+ args: {
85
+ lines: [coffee, pastry],
86
+ totals: {
87
+ gross: 15.5,
88
+ net: 12.52,
89
+ vat: 2.98,
90
+ discount: 0,
91
+ due: 0,
92
+ currency: 'EUR',
93
+ },
94
+ payments: [
95
+ {
96
+ tender: 'cash',
97
+ amount: 15.5,
98
+ tendered: 20,
99
+ change: 4.5,
100
+ state: 'captured',
101
+ },
102
+ ],
103
+ document: { number: 'POS-1002', receiptReady: true },
104
+ },
105
+ };
106
+
107
+ export const VoidedLine: Story = {
108
+ args: {
109
+ lines: [{ ...coffee, struckThrough: true }, pastry],
110
+ totals: {
111
+ gross: 3.5,
112
+ net: 2.92,
113
+ vat: 0.58,
114
+ discount: 0,
115
+ due: 0,
116
+ currency: 'EUR',
117
+ },
118
+ payments: [
119
+ {
120
+ tender: 'cash',
121
+ amount: 3.5,
122
+ tendered: 5,
123
+ change: 1.5,
124
+ state: 'captured',
125
+ },
126
+ ],
127
+ document: { number: 'POS-1003', receiptReady: true },
128
+ },
129
+ };
130
+
131
+ export const AsReceivedNotCalculated: Story = {
132
+ args: {
133
+ lines: [{ ...coffee, qty: 2, unitPrice: 6, lineTotal: 99 }],
134
+ totals: {
135
+ gross: 50,
136
+ net: 40,
137
+ vat: 10,
138
+ discount: 5,
139
+ due: 1,
140
+ currency: 'EUR',
141
+ },
142
+ payments: [
143
+ {
144
+ tender: 'cash',
145
+ amount: 12,
146
+ tendered: 20,
147
+ change: 3,
148
+ state: 'captured',
149
+ },
150
+ ],
151
+ document: { number: 'ENV-999', receiptReady: true },
152
+ },
153
+ };
154
+
155
+ export const Disabled: Story = {
156
+ args: {
157
+ lines: [coffee],
158
+ totals: completedTotals,
159
+ payments: [cashPayment],
160
+ document: { number: 'POS-1001', receiptReady: true },
161
+ disabled: true,
162
+ },
163
+ };