@teincfood/core 0.9.20 → 0.9.21
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.
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { CustomerDisplayModel } from "./model";
|
|
2
|
+
export interface CustomerDisplayCapabilities {
|
|
3
|
+
/** True when the platform exposes a usable secondary display mechanism. */
|
|
4
|
+
supported: boolean;
|
|
5
|
+
/** Number of monitors/displays the platform reports (1 = single screen). */
|
|
6
|
+
displays: number;
|
|
7
|
+
/** Human-readable reason when unsupported (for the settings UI). */
|
|
8
|
+
reason?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Platform-specific presentation of the customer display. The shared POS/model
|
|
12
|
+
* layer never knows whether a driver renders to a Tauri window, an Android
|
|
13
|
+
* `Presentation`, or a future serial customer pole. Drivers are responsible for
|
|
14
|
+
* their own lifecycle and hardware quirks.
|
|
15
|
+
*/
|
|
16
|
+
export interface CustomerDisplayDriver {
|
|
17
|
+
capabilities(): Promise<CustomerDisplayCapabilities>;
|
|
18
|
+
/** Open/show the secondary presentation. Idempotent. */
|
|
19
|
+
open(): Promise<void>;
|
|
20
|
+
/** Close/hide the secondary presentation. Idempotent. */
|
|
21
|
+
close(): Promise<void>;
|
|
22
|
+
/** Push the latest model to the presentation (fire-and-forget). */
|
|
23
|
+
push(model: CustomerDisplayModel): void;
|
|
24
|
+
/** Subscribe to display topology changes (monitor add/remove). */
|
|
25
|
+
onCapabilitiesChanged(cb: (caps: CustomerDisplayCapabilities) => void): () => void;
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Customer display model — a platform-agnostic snapshot of what the
|
|
3
|
+
* customer-facing screen should show. The merchant POS owns order/payment
|
|
4
|
+
* state; this module only *describes* that state so any presentation layer
|
|
5
|
+
* (Tauri second window, Android `Presentation`, future hardware) renders it
|
|
6
|
+
* identically. No hardware or framework details live here.
|
|
7
|
+
*/
|
|
8
|
+
export type CustomerDisplayPhase = "idle" | "ordering" | "payment" | "confirmation";
|
|
9
|
+
export type CustomerDisplayPaymentMethod = "cash" | "mobile_money" | "card" | "split" | "other";
|
|
10
|
+
export type CustomerDisplayPaymentStatus = "pending" | "processing" | "paid" | "failed";
|
|
11
|
+
export interface CustomerDisplayLineItem {
|
|
12
|
+
/** Stable key for list rendering. */
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
variant?: string;
|
|
16
|
+
addons: string[];
|
|
17
|
+
quantity: number;
|
|
18
|
+
unitPriceMinor: number;
|
|
19
|
+
lineTotalMinor: number;
|
|
20
|
+
}
|
|
21
|
+
export interface CustomerDisplayTaxLine {
|
|
22
|
+
id?: string;
|
|
23
|
+
name: string;
|
|
24
|
+
amountMinor: number;
|
|
25
|
+
inclusive?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface CustomerDisplayPayment {
|
|
28
|
+
method: CustomerDisplayPaymentMethod;
|
|
29
|
+
status: CustomerDisplayPaymentStatus;
|
|
30
|
+
/** Optional hosted-checkout / QR URL the customer can scan. */
|
|
31
|
+
qrUrl?: string | null;
|
|
32
|
+
}
|
|
33
|
+
export interface CustomerDisplayConfirmation {
|
|
34
|
+
orderNumber: string;
|
|
35
|
+
totalMinor: number;
|
|
36
|
+
currency: string;
|
|
37
|
+
/** Epoch ms; used to auto-dismiss back to idle. */
|
|
38
|
+
at: number;
|
|
39
|
+
}
|
|
40
|
+
export interface CustomerDisplayBranding {
|
|
41
|
+
name: string;
|
|
42
|
+
logoUrl?: string | null;
|
|
43
|
+
/** Optional promo / tagline shown on the idle screen. */
|
|
44
|
+
tagline?: string | null;
|
|
45
|
+
}
|
|
46
|
+
export interface CustomerDisplayModel {
|
|
47
|
+
phase: CustomerDisplayPhase;
|
|
48
|
+
branding: CustomerDisplayBranding | null;
|
|
49
|
+
currency: string;
|
|
50
|
+
items: CustomerDisplayLineItem[];
|
|
51
|
+
itemCount: number;
|
|
52
|
+
subtotalMinor: number;
|
|
53
|
+
taxLines: CustomerDisplayTaxLine[];
|
|
54
|
+
discountMinor: number;
|
|
55
|
+
totalMinor: number;
|
|
56
|
+
customerName?: string;
|
|
57
|
+
orderType?: string;
|
|
58
|
+
payment?: CustomerDisplayPayment;
|
|
59
|
+
confirmation?: CustomerDisplayConfirmation;
|
|
60
|
+
updatedAt: number;
|
|
61
|
+
}
|
|
62
|
+
export interface CustomerDisplayInput {
|
|
63
|
+
branding: CustomerDisplayBranding | null;
|
|
64
|
+
currency: string;
|
|
65
|
+
items: CustomerDisplayLineItem[];
|
|
66
|
+
subtotalMinor: number;
|
|
67
|
+
taxLines: CustomerDisplayTaxLine[];
|
|
68
|
+
discountMinor: number;
|
|
69
|
+
totalMinor: number;
|
|
70
|
+
customerName?: string;
|
|
71
|
+
orderType?: string;
|
|
72
|
+
/** Runtime overrides from the POS payment flow. */
|
|
73
|
+
phase?: CustomerDisplayPhase;
|
|
74
|
+
payment?: CustomerDisplayPayment;
|
|
75
|
+
confirmation?: CustomerDisplayConfirmation;
|
|
76
|
+
now?: number;
|
|
77
|
+
}
|
|
78
|
+
export declare const EMPTY_CUSTOMER_DISPLAY_MODEL: CustomerDisplayModel;
|
|
79
|
+
/**
|
|
80
|
+
* Resolve the display phase from order + runtime state. Explicit runtime phase
|
|
81
|
+
* (set by the POS flow) wins; otherwise the presence of items implies ordering.
|
|
82
|
+
*/
|
|
83
|
+
export declare function resolvePhase(input: CustomerDisplayInput): CustomerDisplayPhase;
|
|
84
|
+
/** Pure builder — deterministic and safe to unit-test without any hardware. */
|
|
85
|
+
export declare function buildCustomerDisplayModel(input: CustomerDisplayInput): CustomerDisplayModel;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Customer display model — a platform-agnostic snapshot of what the
|
|
3
|
+
* customer-facing screen should show. The merchant POS owns order/payment
|
|
4
|
+
* state; this module only *describes* that state so any presentation layer
|
|
5
|
+
* (Tauri second window, Android `Presentation`, future hardware) renders it
|
|
6
|
+
* identically. No hardware or framework details live here.
|
|
7
|
+
*/
|
|
8
|
+
export const EMPTY_CUSTOMER_DISPLAY_MODEL = {
|
|
9
|
+
phase: "idle",
|
|
10
|
+
branding: null,
|
|
11
|
+
currency: "GHS",
|
|
12
|
+
items: [],
|
|
13
|
+
itemCount: 0,
|
|
14
|
+
subtotalMinor: 0,
|
|
15
|
+
taxLines: [],
|
|
16
|
+
discountMinor: 0,
|
|
17
|
+
totalMinor: 0,
|
|
18
|
+
updatedAt: 0,
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Resolve the display phase from order + runtime state. Explicit runtime phase
|
|
22
|
+
* (set by the POS flow) wins; otherwise the presence of items implies ordering.
|
|
23
|
+
*/
|
|
24
|
+
export function resolvePhase(input) {
|
|
25
|
+
if (input.phase)
|
|
26
|
+
return input.phase;
|
|
27
|
+
return input.items.length > 0 ? "ordering" : "idle";
|
|
28
|
+
}
|
|
29
|
+
/** Pure builder — deterministic and safe to unit-test without any hardware. */
|
|
30
|
+
export function buildCustomerDisplayModel(input) {
|
|
31
|
+
const itemCount = input.items.reduce((sum, item) => sum + item.quantity, 0);
|
|
32
|
+
return {
|
|
33
|
+
phase: resolvePhase(input),
|
|
34
|
+
branding: input.branding,
|
|
35
|
+
currency: input.currency,
|
|
36
|
+
items: input.items,
|
|
37
|
+
itemCount,
|
|
38
|
+
subtotalMinor: input.subtotalMinor,
|
|
39
|
+
taxLines: input.taxLines,
|
|
40
|
+
discountMinor: input.discountMinor,
|
|
41
|
+
totalMinor: input.totalMinor,
|
|
42
|
+
customerName: input.customerName,
|
|
43
|
+
orderType: input.orderType,
|
|
44
|
+
payment: input.payment,
|
|
45
|
+
confirmation: input.confirmation,
|
|
46
|
+
updatedAt: input.now ?? Date.now(),
|
|
47
|
+
};
|
|
48
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export * from "./types/pos.types";
|
|
|
9
9
|
export * from "./types/rider";
|
|
10
10
|
export * from "./types/places";
|
|
11
11
|
export * from "./adapters/notify";
|
|
12
|
+
export * from "./customer-display/model";
|
|
13
|
+
export type { CustomerDisplayDriver, CustomerDisplayCapabilities } from "./customer-display/driver";
|
|
12
14
|
export { calculatePricing, calculateTaxLines, calculateExclusiveTax, calculateInclusiveTaxShare, } from "./pricing/engine";
|
|
13
15
|
export type { PricingInput, PricingResult } from "./pricing/engine";
|
|
14
16
|
export * from "./utils/constants";
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,8 @@ export * from "./types/pos.types";
|
|
|
10
10
|
export * from "./types/rider";
|
|
11
11
|
export * from "./types/places";
|
|
12
12
|
export * from "./adapters/notify";
|
|
13
|
+
// ── Customer display (shared model + platform driver contract) ──
|
|
14
|
+
export * from "./customer-display/model";
|
|
13
15
|
// ── Pricing (server-parity) ──
|
|
14
16
|
export { calculatePricing, calculateTaxLines, calculateExclusiveTax, calculateInclusiveTaxShare, } from "./pricing/engine";
|
|
15
17
|
// ── Utils ──
|
package/package.json
CHANGED