FPSMS-frontend
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

usePurchaseStockInAlerts.ts 3.4 KiB

пре 7 месеци
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. "use client";
  2. import { clientAuthFetch } from "@/app/utils/clientAuthFetch";
  3. import { NEXT_PUBLIC_API_URL } from "@/config/api";
  4. import { useCallback, useEffect, useState } from "react";
  5. const POLL_MS = 60_000;
  6. /** Match backend [PurchaseStockInAlertRow] JSON. */
  7. export type PurchaseStockInAlertItem = {
  8. stockInLineId: number;
  9. purchaseOrderId: number;
  10. purchaseOrderLineId: number;
  11. poCode: string | null;
  12. itemNo: string | null;
  13. itemName: string | null;
  14. status: string | null;
  15. lineCreated: string | null;
  16. receiptDate: string | null;
  17. lotNo: string | null;
  18. };
  19. function parseAlertsPayload(raw: unknown): PurchaseStockInAlertItem[] {
  20. if (!Array.isArray(raw)) return [];
  21. return raw.map((r) => {
  22. const o = r as Record<string, unknown>;
  23. return {
  24. stockInLineId: Number(o.stockInLineId ?? o.stockinLineId ?? 0),
  25. purchaseOrderId: Number(o.purchaseOrderId ?? o.purchaseorderid ?? 0),
  26. purchaseOrderLineId: Number(o.purchaseOrderLineId ?? o.purchaseorderlineid ?? 0),
  27. poCode: o.poCode != null ? String(o.poCode) : null,
  28. itemNo: o.itemNo != null ? String(o.itemNo) : null,
  29. itemName: o.itemName != null ? String(o.itemName) : null,
  30. status: o.status != null ? String(o.status) : null,
  31. lineCreated: o.lineCreated != null ? String(o.lineCreated) : null,
  32. receiptDate: o.receiptDate != null ? String(o.receiptDate) : null,
  33. lotNo: o.lotNo != null ? String(o.lotNo) : null,
  34. };
  35. });
  36. }
  37. /**
  38. * Recent PO stock-in lines in pending / receiving (backend lookback window).
  39. * Fetches full list for alert dialog + count for badge.
  40. */
  41. export function usePurchaseStockInAlerts(enabled: boolean, days?: number) {
  42. const [items, setItems] = useState<PurchaseStockInAlertItem[]>([]);
  43. const [count, setCount] = useState(0);
  44. const [loading, setLoading] = useState(false);
  45. const load = useCallback(async () => {
  46. if (!enabled) {
  47. setItems([]);
  48. setCount(0);
  49. return;
  50. }
  51. setLoading(true);
  52. try {
  53. const listParams = new URLSearchParams();
  54. if (days != null && Number.isFinite(days)) listParams.set("days", String(days));
  55. listParams.set("limit", "80");
  56. const listUrl = `${NEXT_PUBLIC_API_URL}/stockInLine/alerts/purchase-incomplete?${listParams}`;
  57. const countParams = new URLSearchParams();
  58. if (days != null && Number.isFinite(days)) countParams.set("days", String(days));
  59. const countSuffix = countParams.toString() ? `?${countParams}` : "";
  60. const countUrl = `${NEXT_PUBLIC_API_URL}/stockInLine/alerts/purchase-incomplete-count${countSuffix}`;
  61. const [resList, resCount] = await Promise.all([
  62. clientAuthFetch(listUrl),
  63. clientAuthFetch(countUrl),
  64. ]);
  65. if (!resList.ok) {
  66. setItems([]);
  67. } else {
  68. const data = await resList.json();
  69. setItems(parseAlertsPayload(data));
  70. }
  71. if (resCount.ok) {
  72. const c = (await resCount.json()) as { count?: number };
  73. const n = Number(c.count ?? 0);
  74. setCount(Number.isFinite(n) && n > 0 ? n : 0);
  75. } else {
  76. setCount(0);
  77. }
  78. } catch {
  79. setItems([]);
  80. setCount(0);
  81. } finally {
  82. setLoading(false);
  83. }
  84. }, [enabled, days]);
  85. useEffect(() => {
  86. if (!enabled) {
  87. setItems([]);
  88. setCount(0);
  89. return;
  90. }
  91. void load();
  92. const id = window.setInterval(() => void load(), POLL_MS);
  93. return () => window.clearInterval(id);
  94. }, [enabled, load]);
  95. return { items, count, loading, reload: load };
  96. }