@teincfood/core 0.1.5 → 0.1.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.
package/dist/index.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ export { formatDate, formatRelativeTime } from "./utils/formatDate";
|
|
|
20
20
|
export * from "./utils/validation";
|
|
21
21
|
export * from "./utils/responsive";
|
|
22
22
|
export * from "./utils/copyToClipBoard";
|
|
23
|
+
export * from "./utils/customer-display";
|
|
24
|
+
export * from "./utils/order-filter";
|
|
23
25
|
export * from "./db/migrations";
|
|
24
26
|
export * from "./db/operations";
|
|
25
27
|
export * as dbOps from "./db/operations";
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,8 @@ export { formatDate, formatRelativeTime } from "./utils/formatDate";
|
|
|
21
21
|
export * from "./utils/validation";
|
|
22
22
|
export * from "./utils/responsive";
|
|
23
23
|
export * from "./utils/copyToClipBoard";
|
|
24
|
+
export * from "./utils/customer-display";
|
|
25
|
+
export * from "./utils/order-filter";
|
|
24
26
|
// ── DB ──
|
|
25
27
|
export * from "./db/migrations";
|
|
26
28
|
export * from "./db/operations";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Customer display helpers — offline-first, shared by mobile + desktop.
|
|
3
|
+
* SellerOrder shape comes from backend order_serializer (customer_name, is_pos, user{full_name, avatar_url, phone}).
|
|
4
|
+
*/
|
|
5
|
+
import type { SellerOrder } from "../types/api/seller.api";
|
|
6
|
+
export type CustomerDisplay = {
|
|
7
|
+
name: string;
|
|
8
|
+
avatarUrl: string | null;
|
|
9
|
+
phone: string | null;
|
|
10
|
+
isWalkIn: boolean;
|
|
11
|
+
tellerName?: string | null;
|
|
12
|
+
};
|
|
13
|
+
type OrderLike = Pick<SellerOrder, "customer_name"> & {
|
|
14
|
+
user?: {
|
|
15
|
+
full_name?: string;
|
|
16
|
+
avatar_url?: string;
|
|
17
|
+
phone?: string;
|
|
18
|
+
} | null;
|
|
19
|
+
customer_phone?: string | null;
|
|
20
|
+
is_pos?: boolean;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Single source of truth for customer identity.
|
|
24
|
+
* - is_pos:true (POS walk-in): customer is walk-in/typed name, never the staff user.
|
|
25
|
+
* Staff is the teller (order.user) — do not show staff avatar as customer.
|
|
26
|
+
* Returns tellerName so UI can optionally show "Teller: <name>" underneath.
|
|
27
|
+
* - is_pos:false (WhatsApp / online / AI agent): customer_name || user.full_name || "Customer" + avatar from user.avatar_url
|
|
28
|
+
*/
|
|
29
|
+
export declare function getCustomerDisplay(order: OrderLike): CustomerDisplay;
|
|
30
|
+
export declare function getCustomerSearchKeys(order: OrderLike): string[];
|
|
31
|
+
export declare function matchesCustomerSearch(order: OrderLike, qLower: string): boolean;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Customer display helpers — offline-first, shared by mobile + desktop.
|
|
3
|
+
* SellerOrder shape comes from backend order_serializer (customer_name, is_pos, user{full_name, avatar_url, phone}).
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Single source of truth for customer identity.
|
|
7
|
+
* - is_pos:true (POS walk-in): customer is walk-in/typed name, never the staff user.
|
|
8
|
+
* Staff is the teller (order.user) — do not show staff avatar as customer.
|
|
9
|
+
* Returns tellerName so UI can optionally show "Teller: <name>" underneath.
|
|
10
|
+
* - is_pos:false (WhatsApp / online / AI agent): customer_name || user.full_name || "Customer" + avatar from user.avatar_url
|
|
11
|
+
*/
|
|
12
|
+
export function getCustomerDisplay(order) {
|
|
13
|
+
if (order.is_pos) {
|
|
14
|
+
const walkName = order.customer_name?.trim() ? order.customer_name.trim() : "Walk-in";
|
|
15
|
+
return {
|
|
16
|
+
name: walkName,
|
|
17
|
+
avatarUrl: null,
|
|
18
|
+
phone: order.customer_phone ?? null,
|
|
19
|
+
isWalkIn: true,
|
|
20
|
+
tellerName: order.user?.full_name?.trim() ?? null,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const rawName = order.customer_name?.trim()
|
|
24
|
+
? order.customer_name.trim()
|
|
25
|
+
: order.user?.full_name?.trim() ?? "";
|
|
26
|
+
if (rawName) {
|
|
27
|
+
return {
|
|
28
|
+
name: rawName,
|
|
29
|
+
avatarUrl: order.user?.avatar_url ?? null,
|
|
30
|
+
phone: order.customer_phone ?? order.user?.phone ?? null,
|
|
31
|
+
isWalkIn: false,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
name: "Customer",
|
|
36
|
+
avatarUrl: order.user?.avatar_url ?? null,
|
|
37
|
+
phone: order.customer_phone ?? order.user?.phone ?? null,
|
|
38
|
+
isWalkIn: false,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function getCustomerSearchKeys(order) {
|
|
42
|
+
return [
|
|
43
|
+
order.customer_name ?? "",
|
|
44
|
+
order.user?.full_name ?? "",
|
|
45
|
+
order.customer_phone ?? "",
|
|
46
|
+
order.user?.phone ?? "",
|
|
47
|
+
order.user?.avatar_url ?? "",
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
export function matchesCustomerSearch(order, qLower) {
|
|
51
|
+
if (!qLower)
|
|
52
|
+
return true;
|
|
53
|
+
const hay = `${order.customer_name ?? ""} ${order.user?.full_name ?? ""} ${order.customer_phone ?? ""} ${order.user?.phone ?? ""}`.toLowerCase();
|
|
54
|
+
return hay.includes(qLower);
|
|
55
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Offline order list helpers — shared filtering + pagination for
|
|
3
|
+
* usePOSOrdersQuery / useSellerOrdersQuery snapshot fallbacks.
|
|
4
|
+
*/
|
|
5
|
+
import type { SellerOrder } from "../types/api/seller.api";
|
|
6
|
+
export declare function filterOrdersOffline(orders: SellerOrder[], filters?: {
|
|
7
|
+
status?: string;
|
|
8
|
+
order_type?: string;
|
|
9
|
+
is_pos?: string;
|
|
10
|
+
q?: string;
|
|
11
|
+
search?: string;
|
|
12
|
+
}): SellerOrder[];
|
|
13
|
+
export declare function filterSellerOrdersOffline(orders: SellerOrder[], filters?: {
|
|
14
|
+
status?: string;
|
|
15
|
+
q?: string;
|
|
16
|
+
}): SellerOrder[];
|
|
17
|
+
export declare function paginateOffline<T>(orders: T[], page: number, pageSize?: number): {
|
|
18
|
+
data: T[];
|
|
19
|
+
pagination: {
|
|
20
|
+
page: number;
|
|
21
|
+
page_size: number;
|
|
22
|
+
total_pages: number;
|
|
23
|
+
total_count: number;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Offline order list helpers — shared filtering + pagination for
|
|
3
|
+
* usePOSOrdersQuery / useSellerOrdersQuery snapshot fallbacks.
|
|
4
|
+
*/
|
|
5
|
+
import { matchesCustomerSearch } from "./customer-display";
|
|
6
|
+
export function filterOrdersOffline(orders, filters) {
|
|
7
|
+
let filtered = orders;
|
|
8
|
+
const statusSet = filters?.status ? new Set(filters.status.split(",").map((s) => s.trim())) : null;
|
|
9
|
+
if (statusSet)
|
|
10
|
+
filtered = filtered.filter((o) => statusSet.has(o.status));
|
|
11
|
+
if (filters?.order_type)
|
|
12
|
+
filtered = filtered.filter((o) => (o.order_type ?? "pos") === filters.order_type);
|
|
13
|
+
if (filters?.is_pos !== undefined) {
|
|
14
|
+
const wantPos = filters.is_pos === "true" || filters.is_pos === "1";
|
|
15
|
+
filtered = filtered.filter((o) => !!o.is_pos === wantPos);
|
|
16
|
+
}
|
|
17
|
+
const q = (filters?.q ?? filters?.search ?? "").toLowerCase();
|
|
18
|
+
if (q) {
|
|
19
|
+
filtered = filtered.filter((o) => o.order_number.toLowerCase().includes(q) ||
|
|
20
|
+
matchesCustomerSearch(o, q));
|
|
21
|
+
}
|
|
22
|
+
return filtered;
|
|
23
|
+
}
|
|
24
|
+
export function filterSellerOrdersOffline(orders, filters) {
|
|
25
|
+
let filtered = orders;
|
|
26
|
+
if (filters?.status) {
|
|
27
|
+
const set = new Set(filters.status.split(",").map((s) => s.trim()));
|
|
28
|
+
filtered = filtered.filter((o) => set.has(o.status));
|
|
29
|
+
}
|
|
30
|
+
if (filters?.q) {
|
|
31
|
+
const q = filters.q.toLowerCase();
|
|
32
|
+
filtered = filtered.filter((o) => o.order_number.toLowerCase().includes(q) || matchesCustomerSearch(o, q));
|
|
33
|
+
}
|
|
34
|
+
return filtered;
|
|
35
|
+
}
|
|
36
|
+
export function paginateOffline(orders, page, pageSize = 20) {
|
|
37
|
+
const start = (page - 1) * pageSize;
|
|
38
|
+
const slice = orders.slice(start, start + pageSize);
|
|
39
|
+
const totalPages = Math.max(1, Math.ceil(orders.length / pageSize));
|
|
40
|
+
return { data: slice, pagination: { page, page_size: pageSize, total_pages: totalPages, total_count: orders.length } };
|
|
41
|
+
}
|
package/package.json
CHANGED