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.
 
 

444 lines
15 KiB

  1. import { clientAuthFetch } from "@/app/utils/clientAuthFetch";
  2. import { NEXT_PUBLIC_API_URL } from "@/config/api";
  3. const BASE = `${NEXT_PUBLIC_API_URL}/chart`;
  4. function buildParams(params: Record<string, string | number | undefined>) {
  5. const p = new URLSearchParams();
  6. Object.entries(params).forEach(([k, v]) => {
  7. if (v !== undefined && v !== "") p.set(k, String(v));
  8. });
  9. return p.toString();
  10. }
  11. export interface StockTransactionsByDateRow {
  12. date: string;
  13. inQty: number;
  14. outQty: number;
  15. totalQty: number;
  16. }
  17. export interface DeliveryOrderByDateRow {
  18. date: string;
  19. orderCount: number;
  20. totalQty: number;
  21. }
  22. export interface PurchaseOrderByStatusRow {
  23. status: string;
  24. count: number;
  25. }
  26. export interface StockInOutByDateRow {
  27. date: string;
  28. inQty: number;
  29. outQty: number;
  30. }
  31. export interface TopDeliveryItemsRow {
  32. itemCode: string;
  33. itemName: string;
  34. totalQty: number;
  35. }
  36. export interface StockBalanceTrendRow {
  37. date: string;
  38. balance: number;
  39. }
  40. export interface ConsumptionTrendByMonthRow {
  41. month: string;
  42. outQty: number;
  43. }
  44. export interface StaffDeliveryPerformanceRow {
  45. date: string;
  46. staffName: string;
  47. orderCount: number;
  48. totalMinutes: number;
  49. }
  50. export interface StaffOption {
  51. staffNo: string;
  52. name: string;
  53. }
  54. export async function fetchStaffDeliveryPerformanceHandlers(): Promise<StaffOption[]> {
  55. const res = await clientAuthFetch(`${BASE}/staff-delivery-performance-handlers`);
  56. if (!res.ok) throw new Error("Failed to fetch staff list");
  57. const data = await res.json();
  58. if (!Array.isArray(data)) return [];
  59. return (data as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
  60. staffNo: String(r.staffNo ?? ""),
  61. name: String(r.name ?? ""),
  62. }));
  63. }
  64. // Job order
  65. export interface JobOrderByStatusRow {
  66. status: string;
  67. count: number;
  68. }
  69. export interface JobOrderCountByDateRow {
  70. date: string;
  71. orderCount: number;
  72. }
  73. export interface JobOrderCreatedCompletedRow {
  74. date: string;
  75. createdCount: number;
  76. completedCount: number;
  77. }
  78. export interface ProductionScheduleByDateRow {
  79. date: string;
  80. scheduledItemCount: number;
  81. totalEstProdCount: number;
  82. }
  83. export interface PlannedDailyOutputRow {
  84. itemCode: string;
  85. itemName: string;
  86. dailyQty: number;
  87. }
  88. export async function fetchJobOrderByStatus(
  89. targetDate?: string
  90. ): Promise<JobOrderByStatusRow[]> {
  91. const q = targetDate ? buildParams({ targetDate }) : "";
  92. const res = await clientAuthFetch(
  93. q ? `${BASE}/job-order-by-status?${q}` : `${BASE}/job-order-by-status`
  94. );
  95. if (!res.ok) throw new Error("Failed to fetch job order by status");
  96. const data = await res.json();
  97. return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
  98. status: String(r.status ?? ""),
  99. count: Number(r.count ?? 0),
  100. }));
  101. }
  102. export async function fetchJobOrderCountByDate(
  103. startDate?: string,
  104. endDate?: string
  105. ): Promise<JobOrderCountByDateRow[]> {
  106. const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
  107. const res = await clientAuthFetch(`${BASE}/job-order-count-by-date?${q}`);
  108. if (!res.ok) throw new Error("Failed to fetch job order count by date");
  109. const data = await res.json();
  110. return normalizeChartRows(data, "date", ["orderCount"]);
  111. }
  112. export async function fetchJobOrderCreatedCompletedByDate(
  113. startDate?: string,
  114. endDate?: string
  115. ): Promise<JobOrderCreatedCompletedRow[]> {
  116. const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
  117. const res = await clientAuthFetch(
  118. `${BASE}/job-order-created-completed-by-date?${q}`
  119. );
  120. if (!res.ok) throw new Error("Failed to fetch job order created/completed");
  121. const data = await res.json();
  122. return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
  123. date: String(r.date ?? ""),
  124. createdCount: Number(r.createdCount ?? 0),
  125. completedCount: Number(r.completedCount ?? 0),
  126. }));
  127. }
  128. export interface JobMaterialPendingPickedRow {
  129. date: string;
  130. pendingCount: number;
  131. pickedCount: number;
  132. }
  133. export async function fetchJobMaterialPendingPickedByDate(
  134. startDate?: string,
  135. endDate?: string
  136. ): Promise<JobMaterialPendingPickedRow[]> {
  137. const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
  138. const res = await clientAuthFetch(`${BASE}/job-material-pending-picked-by-date?${q}`);
  139. if (!res.ok) throw new Error("Failed to fetch job material pending/picked");
  140. const data = await res.json();
  141. return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
  142. date: String(r.date ?? ""),
  143. pendingCount: Number(r.pendingCount ?? 0),
  144. pickedCount: Number(r.pickedCount ?? 0),
  145. }));
  146. }
  147. export interface JobProcessPendingCompletedRow {
  148. date: string;
  149. pendingCount: number;
  150. completedCount: number;
  151. }
  152. export async function fetchJobProcessPendingCompletedByDate(
  153. startDate?: string,
  154. endDate?: string
  155. ): Promise<JobProcessPendingCompletedRow[]> {
  156. const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
  157. const res = await clientAuthFetch(`${BASE}/job-process-pending-completed-by-date?${q}`);
  158. if (!res.ok) throw new Error("Failed to fetch job process pending/completed");
  159. const data = await res.json();
  160. return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
  161. date: String(r.date ?? ""),
  162. pendingCount: Number(r.pendingCount ?? 0),
  163. completedCount: Number(r.completedCount ?? 0),
  164. }));
  165. }
  166. export interface JobEquipmentWorkingWorkedRow {
  167. date: string;
  168. workingCount: number;
  169. workedCount: number;
  170. }
  171. export async function fetchJobEquipmentWorkingWorkedByDate(
  172. startDate?: string,
  173. endDate?: string
  174. ): Promise<JobEquipmentWorkingWorkedRow[]> {
  175. const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
  176. const res = await clientAuthFetch(`${BASE}/job-equipment-working-worked-by-date?${q}`);
  177. if (!res.ok) throw new Error("Failed to fetch job equipment working/worked");
  178. const data = await res.json();
  179. return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
  180. date: String(r.date ?? ""),
  181. workingCount: Number(r.workingCount ?? 0),
  182. workedCount: Number(r.workedCount ?? 0),
  183. }));
  184. }
  185. export async function fetchProductionScheduleByDate(
  186. startDate?: string,
  187. endDate?: string
  188. ): Promise<ProductionScheduleByDateRow[]> {
  189. const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
  190. const res = await clientAuthFetch(
  191. `${BASE}/production-schedule-by-date?${q}`
  192. );
  193. if (!res.ok) throw new Error("Failed to fetch production schedule by date");
  194. const data = await res.json();
  195. return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
  196. date: String(r.date ?? ""),
  197. scheduledItemCount: Number(r.scheduledItemCount ?? r.scheduleCount ?? 0),
  198. totalEstProdCount: Number(r.totalEstProdCount ?? 0),
  199. }));
  200. }
  201. export async function fetchPlannedDailyOutputByItem(
  202. limit = 20
  203. ): Promise<PlannedDailyOutputRow[]> {
  204. const res = await clientAuthFetch(
  205. `${BASE}/planned-daily-output-by-item?limit=${limit}`
  206. );
  207. if (!res.ok) throw new Error("Failed to fetch planned daily output");
  208. const data = await res.json();
  209. return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
  210. itemCode: String(r.itemCode ?? ""),
  211. itemName: String(r.itemName ?? ""),
  212. dailyQty: Number(r.dailyQty ?? 0),
  213. }));
  214. }
  215. /** Planned production by date and by item (production_schedule). */
  216. export interface PlannedOutputByDateAndItemRow {
  217. date: string;
  218. itemCode: string;
  219. itemName: string;
  220. qty: number;
  221. }
  222. export async function fetchPlannedOutputByDateAndItem(
  223. startDate?: string,
  224. endDate?: string
  225. ): Promise<PlannedOutputByDateAndItemRow[]> {
  226. const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
  227. const res = await clientAuthFetch(
  228. q ? `${BASE}/planned-output-by-date-and-item?${q}` : `${BASE}/planned-output-by-date-and-item`
  229. );
  230. if (!res.ok) throw new Error("Failed to fetch planned output by date and item");
  231. const data = await res.json();
  232. return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
  233. date: String(r.date ?? ""),
  234. itemCode: String(r.itemCode ?? ""),
  235. itemName: String(r.itemName ?? ""),
  236. qty: Number(r.qty ?? 0),
  237. }));
  238. }
  239. export async function fetchStaffDeliveryPerformance(
  240. startDate?: string,
  241. endDate?: string,
  242. staffNos?: string[]
  243. ): Promise<StaffDeliveryPerformanceRow[]> {
  244. const p = new URLSearchParams();
  245. if (startDate) p.set("startDate", startDate);
  246. if (endDate) p.set("endDate", endDate);
  247. (staffNos ?? []).forEach((no) => p.append("staffNo", no));
  248. const q = p.toString();
  249. const res = await clientAuthFetch(
  250. q ? `${BASE}/staff-delivery-performance?${q}` : `${BASE}/staff-delivery-performance`
  251. );
  252. if (!res.ok) throw new Error("Failed to fetch staff delivery performance");
  253. const data = await res.json();
  254. return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => {
  255. // Accept camelCase or lowercase keys (JDBC/DB may return different casing)
  256. const row = r as Record<string, unknown>;
  257. return {
  258. date: String(row.date ?? row.Date ?? ""),
  259. staffName: String(row.staffName ?? row.staffname ?? ""),
  260. orderCount: Number(row.orderCount ?? row.ordercount ?? 0),
  261. totalMinutes: Number(row.totalMinutes ?? row.totalminutes ?? 0),
  262. };
  263. });
  264. }
  265. export async function fetchStockTransactionsByDate(
  266. startDate?: string,
  267. endDate?: string
  268. ): Promise<StockTransactionsByDateRow[]> {
  269. const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
  270. const res = await clientAuthFetch(`${BASE}/stock-transactions-by-date?${q}`);
  271. if (!res.ok) throw new Error("Failed to fetch stock transactions by date");
  272. const data = await res.json();
  273. return normalizeChartRows(data, "date", ["inQty", "outQty", "totalQty"]);
  274. }
  275. export async function fetchDeliveryOrderByDate(
  276. startDate?: string,
  277. endDate?: string
  278. ): Promise<DeliveryOrderByDateRow[]> {
  279. const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
  280. const res = await clientAuthFetch(`${BASE}/delivery-order-by-date?${q}`);
  281. if (!res.ok) throw new Error("Failed to fetch delivery order by date");
  282. const data = await res.json();
  283. return normalizeChartRows(data, "date", ["orderCount", "totalQty"]);
  284. }
  285. export async function fetchPurchaseOrderByStatus(
  286. targetDate?: string
  287. ): Promise<PurchaseOrderByStatusRow[]> {
  288. const q = targetDate
  289. ? buildParams({ targetDate })
  290. : "";
  291. const res = await clientAuthFetch(
  292. q ? `${BASE}/purchase-order-by-status?${q}` : `${BASE}/purchase-order-by-status`
  293. );
  294. if (!res.ok) throw new Error("Failed to fetch purchase order by status");
  295. const data = await res.json();
  296. return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
  297. status: String(r.status ?? ""),
  298. count: Number(r.count ?? 0),
  299. }));
  300. }
  301. export async function fetchStockInOutByDate(
  302. startDate?: string,
  303. endDate?: string
  304. ): Promise<StockInOutByDateRow[]> {
  305. const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
  306. const res = await clientAuthFetch(`${BASE}/stock-in-out-by-date?${q}`);
  307. if (!res.ok) throw new Error("Failed to fetch stock in/out by date");
  308. const data = await res.json();
  309. return normalizeChartRows(data, "date", ["inQty", "outQty"]);
  310. }
  311. export interface TopDeliveryItemOption {
  312. itemCode: string;
  313. itemName: string;
  314. }
  315. export async function fetchTopDeliveryItemsItemOptions(
  316. startDate?: string,
  317. endDate?: string
  318. ): Promise<TopDeliveryItemOption[]> {
  319. const q = buildParams({ startDate: startDate ?? "", endDate: endDate ?? "" });
  320. const res = await clientAuthFetch(
  321. q ? `${BASE}/top-delivery-items-item-options?${q}` : `${BASE}/top-delivery-items-item-options`
  322. );
  323. if (!res.ok) throw new Error("Failed to fetch item options");
  324. const data = await res.json();
  325. return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
  326. itemCode: String(r.itemCode ?? ""),
  327. itemName: String(r.itemName ?? ""),
  328. }));
  329. }
  330. export async function fetchTopDeliveryItems(
  331. startDate?: string,
  332. endDate?: string,
  333. limit = 10,
  334. itemCodes?: string[]
  335. ): Promise<TopDeliveryItemsRow[]> {
  336. const p = new URLSearchParams();
  337. if (startDate) p.set("startDate", startDate);
  338. if (endDate) p.set("endDate", endDate);
  339. p.set("limit", String(limit));
  340. (itemCodes ?? []).forEach((code) => p.append("itemCode", code));
  341. const q = p.toString();
  342. const res = await clientAuthFetch(`${BASE}/top-delivery-items?${q}`);
  343. if (!res.ok) throw new Error("Failed to fetch top delivery items");
  344. const data = await res.json();
  345. return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
  346. itemCode: String(r.itemCode ?? ""),
  347. itemName: String(r.itemName ?? ""),
  348. totalQty: Number(r.totalQty ?? 0),
  349. }));
  350. }
  351. export async function fetchStockBalanceTrend(
  352. startDate?: string,
  353. endDate?: string,
  354. itemCode?: string
  355. ): Promise<StockBalanceTrendRow[]> {
  356. const q = buildParams({
  357. startDate: startDate ?? "",
  358. endDate: endDate ?? "",
  359. itemCode: itemCode ?? "",
  360. });
  361. const res = await clientAuthFetch(`${BASE}/stock-balance-trend?${q}`);
  362. if (!res.ok) throw new Error("Failed to fetch stock balance trend");
  363. const data = await res.json();
  364. return normalizeChartRows(data, "date", ["balance"]);
  365. }
  366. export async function fetchConsumptionTrendByMonth(
  367. year?: number,
  368. startDate?: string,
  369. endDate?: string,
  370. itemCode?: string
  371. ): Promise<ConsumptionTrendByMonthRow[]> {
  372. const q = buildParams({
  373. year: year ?? "",
  374. startDate: startDate ?? "",
  375. endDate: endDate ?? "",
  376. itemCode: itemCode ?? "",
  377. });
  378. const res = await clientAuthFetch(`${BASE}/consumption-trend-by-month?${q}`);
  379. if (!res.ok) throw new Error("Failed to fetch consumption trend");
  380. const data = await res.json();
  381. return ((Array.isArray(data) ? data : []) as Record<string, unknown>[]).map((r: Record<string, unknown>) => ({
  382. month: String(r.month ?? ""),
  383. outQty: Number(r.outQty ?? 0),
  384. }));
  385. }
  386. /** Normalize rows: ensure date key is string and numeric keys are numbers (backend may return BigDecimal/Long). */
  387. function normalizeChartRows<T>(
  388. rows: unknown[],
  389. dateKey: string,
  390. numberKeys: string[]
  391. ): T[] {
  392. if (!Array.isArray(rows)) return [];
  393. return rows.map((r: unknown) => {
  394. const row = r as Record<string, unknown>;
  395. const out: Record<string, unknown> = {};
  396. out[dateKey] = row[dateKey] != null ? String(row[dateKey]) : "";
  397. numberKeys.forEach((k) => {
  398. out[k] = Number(row[k]) || 0;
  399. });
  400. return out as T;
  401. });
  402. }