@teincfood/core 0.9.19 → 0.9.20

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.
@@ -81,6 +81,7 @@ export declare function listOrders(db: SQLiteDatabase, businessId: string, filte
81
81
  order_type?: string;
82
82
  date_from?: string;
83
83
  date_to?: string;
84
+ is_paid?: string;
84
85
  }, page?: number, pageSize?: number): Promise<{
85
86
  data: import("../types/api/seller.api").SellerOrder[];
86
87
  total: number;
@@ -263,6 +263,12 @@ export async function listOrders(db, businessId, filters, page = 1, pageSize = 2
263
263
  where.push("order_type = ?");
264
264
  args.push(filters.order_type);
265
265
  }
266
+ if (filters?.is_paid === "paid") {
267
+ where.push("json_extract(payment, '$.is_paid') = 1");
268
+ }
269
+ else if (filters?.is_paid === "unpaid") {
270
+ where.push("(json_extract(payment, '$.is_paid') IS NULL OR json_extract(payment, '$.is_paid') = 0)");
271
+ }
266
272
  if (filters?.q) {
267
273
  where.push("(order_number LIKE ? OR customer_name LIKE ? OR customer_phone LIKE ?)");
268
274
  const q = `%${filters.q}%`;
@@ -17,6 +17,7 @@ export declare function listLocalOrders(businessId: string, filters?: {
17
17
  order_type?: string;
18
18
  date_from?: string;
19
19
  date_to?: string;
20
+ is_paid?: string;
20
21
  }, page?: number, pageSize?: number): Promise<{
21
22
  data: import("../../types/api/seller.api").SellerOrder[];
22
23
  total: number;
@@ -17,6 +17,10 @@ export declare function useDeliveryDetailQuery(deliveryId: string, enabled?: boo
17
17
  export interface DeliveryFilters {
18
18
  status?: string;
19
19
  search?: string;
20
+ order_type?: string;
21
+ is_paid?: string;
22
+ date_from?: string;
23
+ date_to?: string;
20
24
  }
21
25
  export declare function useBusinessDeliveriesQuery(enabled?: boolean, filters?: DeliveryFilters): {
22
26
  dataUpdatedAt: number;
@@ -34,7 +34,14 @@ function useBusinessId() {
34
34
  return id ?? undefined;
35
35
  }
36
36
  async function fetchOfflineDeliveries(businessId, filters, page, pageSize) {
37
- const { data } = await listLocalOrders(businessId, { status: undefined, q: filters?.search }, page, pageSize);
37
+ const { data } = await listLocalOrders(businessId, {
38
+ status: undefined,
39
+ q: filters?.search,
40
+ order_type: filters?.order_type,
41
+ is_paid: filters?.is_paid,
42
+ date_from: filters?.date_from,
43
+ date_to: filters?.date_to,
44
+ }, page, pageSize);
38
45
  const deliveryOrders = data.filter((o) => o.delivery_id);
39
46
  const statusSet = new Set((filters?.status ?? "").split(",").map((s) => s.trim()).filter(Boolean));
40
47
  const filtered = statusSet.size > 0
@@ -73,6 +80,14 @@ export function useBusinessDeliveriesQuery(enabled = true, filters) {
73
80
  params.status = filters.status;
74
81
  if (filters?.search)
75
82
  params.search = filters.search;
83
+ if (filters?.order_type)
84
+ params.order_type = filters.order_type;
85
+ if (filters?.is_paid)
86
+ params.payment_status = filters.is_paid;
87
+ if (filters?.date_from)
88
+ params.date_from = filters.date_from;
89
+ if (filters?.date_to)
90
+ params.date_to = filters.date_to;
76
91
  const res = (await deliveryService.fetchBusinessDeliveries(businessId, params));
77
92
  return res;
78
93
  },
@@ -12,11 +12,16 @@ export declare const SELLER_KEYS: {
12
12
  order: (id: string, bid?: string) => readonly ["seller", "order", string, string | undefined];
13
13
  };
14
14
  export declare function usePOSOrdersQuery(filters?: Record<string, string>, enabled?: boolean): import("@tanstack/react-query").UseInfiniteQueryResult<SellerOrder[], Error>;
15
- export declare function useSellerOrdersQuery(enabled?: boolean, filters?: {
15
+ export interface SellerOrderFilters {
16
16
  status?: string;
17
17
  q?: string;
18
18
  search?: string;
19
- }): import("@tanstack/react-query").UseInfiniteQueryResult<SellerOrder[], Error>;
19
+ order_type?: string;
20
+ is_paid?: string;
21
+ date_from?: string;
22
+ date_to?: string;
23
+ }
24
+ export declare function useSellerOrdersQuery(enabled?: boolean, filters?: SellerOrderFilters): import("@tanstack/react-query").UseInfiniteQueryResult<SellerOrder[], Error>;
20
25
  export declare function useSellerOrderQuery(orderId: string, enabled?: boolean): import("@tanstack/react-query").UseQueryResult<{
21
26
  data: SellerOrder;
22
27
  }, Error>;
@@ -58,7 +58,14 @@ export function useSellerOrdersQuery(enabled = true, filters) {
58
58
  if (!bid)
59
59
  return { data: [], pagination: { page: 1, page_size: 20, total_pages: 1, total_entries: 0 } };
60
60
  const q = filters?.q ?? filters?.search;
61
- const { data, total } = await listLocalOrders(bid, { status: filters?.status, q }, pageParam, 20);
61
+ const { data, total } = await listLocalOrders(bid, {
62
+ status: filters?.status,
63
+ q,
64
+ order_type: filters?.order_type,
65
+ is_paid: filters?.is_paid,
66
+ date_from: filters?.date_from,
67
+ date_to: filters?.date_to,
68
+ }, pageParam, 20);
62
69
  const merged = await mergePendingIntoList(data, bid, pageParam);
63
70
  const totalPages = Math.max(1, Math.ceil(total / 20));
64
71
  return { data: merged, pagination: { page: pageParam, page_size: 20, total_pages: totalPages, total_entries: total + (merged.length - data.length) } };
package/dist/index.d.ts CHANGED
@@ -21,7 +21,9 @@ export * from "./utils/attempt-stats";
21
21
  export { PHONE_COUNTRIES, getDialCodeFromCountry, getCountryOption, normalizePhoneForCountry, isLikelyE164 } from "./utils/phone";
22
22
  export type { PhoneCountryOption } from "./utils/phone";
23
23
  export * from "./utils/formatCurrency";
24
- export { formatDate, formatRelativeTime } from "./utils/formatDate";
24
+ export { formatDate, formatDateTime, formatTime, formatDateCompact, formatRelativeTime } from "./utils/formatDate";
25
+ export { resolveDateRange, DATE_PRESET_LABELS } from "./utils/date-range";
26
+ export type { DatePreset, DateRange } from "./utils/date-range";
25
27
  export * from "./utils/validation";
26
28
  export * from "./validation/offline";
27
29
  export * from "./utils/copyToClipBoard";
package/dist/index.js CHANGED
@@ -22,7 +22,8 @@ export * from "./utils/debug-log";
22
22
  export * from "./utils/attempt-stats";
23
23
  export { PHONE_COUNTRIES, getDialCodeFromCountry, getCountryOption, normalizePhoneForCountry, isLikelyE164 } from "./utils/phone";
24
24
  export * from "./utils/formatCurrency";
25
- export { formatDate, formatRelativeTime } from "./utils/formatDate";
25
+ export { formatDate, formatDateTime, formatTime, formatDateCompact, formatRelativeTime } from "./utils/formatDate";
26
+ export { resolveDateRange, DATE_PRESET_LABELS } from "./utils/date-range";
26
27
  export * from "./utils/validation";
27
28
  export * from "./validation/offline";
28
29
  export * from "./utils/copyToClipBoard";
@@ -410,24 +410,61 @@ export function createMemoryDriver() {
410
410
  rows = rows.slice(0, limit);
411
411
  return rows;
412
412
  }
413
- // orders list with filters (simple business_id filter + pagination)
413
+ // orders list with filters (business_id + status/order_type/is_paid/q/date)
414
414
  if (s.includes("SELECT * FROM orders WHERE")) {
415
415
  const business_id = args[0];
416
- // args includes where-clause params plus LIMIT and OFFSET at end
417
- // For test, just filter by business_id and apply basic status/q if present in sql string via args slicing?
418
- // Simpler: return all orders for that business, sorted by inserted_at desc, handle LIMIT/OFFSET manually
419
416
  let rows = tables.orders.filter((r) => r.business_id === business_id);
420
- // handle status filter if sql contains status IN
417
+ // Consume filter args in the exact clause order produced by listOrders.
418
+ let i = 1;
421
419
  if (s.includes("status IN")) {
422
- // find args that are status values (excluding business_id, limit, offset)
423
- // Our sql: SELECT * FROM orders WHERE business_id = ? AND status IN (?,?) ... LIMIT ? OFFSET ?
424
- // So statuses are args[1]..args[?]
425
- // Count statuses by parsing SQL placeholder count?
426
- // For test, just extract known statuses from args that look like status strings
427
- const possibleStatuses = ["pending", "awaiting_payment", "accepted", "preparing", "ready", "waiting_pickup", "on_the_way", "arrived", "delivered", "cancelled", "assigned", "picked_up", "en_route", "failed"];
428
- const statusArgs = args.filter((v) => possibleStatuses.includes(String(v)));
429
- if (statusArgs.length)
430
- rows = rows.filter((r) => statusArgs.includes(r.status));
420
+ const n = (s.match(/status IN \([^)]*\)/)[0].match(/\?/g) || []).length;
421
+ const statuses = args.slice(i, i + n);
422
+ i += n;
423
+ if (statuses.length)
424
+ rows = rows.filter((r) => statuses.includes(r.status));
425
+ }
426
+ if (s.includes("is_pos = ?")) {
427
+ const v = Number(args[i++]) === 1;
428
+ rows = rows.filter((r) => !!r.is_pos === v);
429
+ }
430
+ if (s.includes("order_type = ?")) {
431
+ const t = args[i++];
432
+ rows = rows.filter((r) => r.order_type === t);
433
+ }
434
+ if (s.includes("json_extract(payment, '$.is_paid') = 1")) {
435
+ rows = rows.filter((r) => {
436
+ try {
437
+ return JSON.parse(r.payment ?? "{}").is_paid === true;
438
+ }
439
+ catch {
440
+ return false;
441
+ }
442
+ });
443
+ }
444
+ else if (s.includes("json_extract(payment, '$.is_paid') IS NULL")) {
445
+ rows = rows.filter((r) => {
446
+ try {
447
+ return JSON.parse(r.payment ?? "{}").is_paid !== true;
448
+ }
449
+ catch {
450
+ return true;
451
+ }
452
+ });
453
+ }
454
+ if (s.includes("order_number LIKE ?")) {
455
+ const term = String(args[i]).replace(/%/g, "").toLowerCase();
456
+ i += 3;
457
+ rows = rows.filter((r) => String(r.order_number ?? "").toLowerCase().includes(term) ||
458
+ String(r.customer_name ?? "").toLowerCase().includes(term) ||
459
+ String(r.customer_phone ?? "").toLowerCase().includes(term));
460
+ }
461
+ if (s.includes("inserted_at >= ?")) {
462
+ const from = String(args[i++]);
463
+ rows = rows.filter((r) => String(r.inserted_at) >= from);
464
+ }
465
+ if (s.includes("inserted_at < ?")) {
466
+ const to = String(args[i++]);
467
+ rows = rows.filter((r) => String(r.inserted_at) < to);
431
468
  }
432
469
  rows = rows.sort((a, b) => (a.inserted_at < b.inserted_at ? 1 : -1));
433
470
  const limit = args[args.length - 2];
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Date-range presets for order/delivery filters.
3
+ *
4
+ * Returns YYYY-MM-DD strings (local day boundaries) that both the backend
5
+ * (normalizes to UTC day bounds) and the local SQLite read-model
6
+ * (`inserted_at >= from` / `inserted_at < `${to}T23:59:59``) accept.
7
+ */
8
+ export type DatePreset = "all" | "today" | "yesterday" | "last7" | "custom";
9
+ export interface DateRange {
10
+ date_from?: string;
11
+ date_to?: string;
12
+ }
13
+ export declare function resolveDateRange(preset: DatePreset | string | undefined, customFrom?: string, customTo?: string): DateRange;
14
+ export declare const DATE_PRESET_LABELS: Array<{
15
+ key: DatePreset;
16
+ label: string;
17
+ }>;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Date-range presets for order/delivery filters.
3
+ *
4
+ * Returns YYYY-MM-DD strings (local day boundaries) that both the backend
5
+ * (normalizes to UTC day bounds) and the local SQLite read-model
6
+ * (`inserted_at >= from` / `inserted_at < `${to}T23:59:59``) accept.
7
+ */
8
+ function toISODate(d) {
9
+ const y = d.getFullYear();
10
+ const m = String(d.getMonth() + 1).padStart(2, "0");
11
+ const day = String(d.getDate()).padStart(2, "0");
12
+ return `${y}-${m}-${day}`;
13
+ }
14
+ function addDays(base, days) {
15
+ const d = new Date(base);
16
+ d.setDate(d.getDate() + days);
17
+ return d;
18
+ }
19
+ export function resolveDateRange(preset, customFrom, customTo) {
20
+ const today = new Date();
21
+ switch (preset) {
22
+ case "today":
23
+ return { date_from: toISODate(today), date_to: toISODate(today) };
24
+ case "yesterday": {
25
+ const y = toISODate(addDays(today, -1));
26
+ return { date_from: y, date_to: y };
27
+ }
28
+ case "last7":
29
+ return { date_from: toISODate(addDays(today, -6)), date_to: toISODate(today) };
30
+ case "custom":
31
+ return {
32
+ ...(customFrom ? { date_from: customFrom } : {}),
33
+ ...(customTo ? { date_to: customTo } : {}),
34
+ };
35
+ default:
36
+ return {};
37
+ }
38
+ }
39
+ export const DATE_PRESET_LABELS = [
40
+ { key: "all", label: "All time" },
41
+ { key: "today", label: "Today" },
42
+ { key: "yesterday", label: "Yesterday" },
43
+ { key: "last7", label: "Last 7 days" },
44
+ { key: "custom", label: "Custom" },
45
+ ];
@@ -1,3 +1,16 @@
1
+ /**
2
+ * Format the time-of-day of an ISO date string (e.g. "2:30 PM").
3
+ */
4
+ export declare function formatTime(isoString: string): string;
5
+ /**
6
+ * Compact date (e.g. "26 Sep 2026") for list cards where the year may be
7
+ * implied but is still useful for older orders.
8
+ */
9
+ export declare function formatDateCompact(isoString: string): string;
10
+ /**
11
+ * Date + time for order/delivery cards: "26 Sep 2026, 2:30 PM".
12
+ */
13
+ export declare function formatDateTime(isoString: string): string;
1
14
  /**
2
15
  * Format an ISO date string into a human-readable format.
3
16
  */
@@ -1,3 +1,32 @@
1
+ /**
2
+ * Format the time-of-day of an ISO date string (e.g. "2:30 PM").
3
+ */
4
+ export function formatTime(isoString) {
5
+ const date = new Date(isoString);
6
+ return date.toLocaleTimeString('en-US', {
7
+ hour: 'numeric',
8
+ minute: '2-digit',
9
+ hour12: true,
10
+ });
11
+ }
12
+ /**
13
+ * Compact date (e.g. "26 Sep 2026") for list cards where the year may be
14
+ * implied but is still useful for older orders.
15
+ */
16
+ export function formatDateCompact(isoString) {
17
+ const date = new Date(isoString);
18
+ return date.toLocaleDateString('en-US', {
19
+ day: '2-digit',
20
+ month: 'short',
21
+ year: 'numeric',
22
+ });
23
+ }
24
+ /**
25
+ * Date + time for order/delivery cards: "26 Sep 2026, 2:30 PM".
26
+ */
27
+ export function formatDateTime(isoString) {
28
+ return `${formatDateCompact(isoString)}, ${formatTime(isoString)}`;
29
+ }
1
30
  /**
2
31
  * Format an ISO date string into a human-readable format.
3
32
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teincfood/core",
3
- "version": "0.9.19",
3
+ "version": "0.9.20",
4
4
  "description": "TeincFood shared offline-first core — types, sync engine, local DB, reference data, and repositories for mobile + desktop",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",