FPSMS-frontend
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

JoPickOrderList.tsx 11 KiB

5ヶ月前
5ヶ月前
1ヶ月前
1ヶ月前
5ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
5ヶ月前
1ヶ月前
5ヶ月前
1ヶ月前
5ヶ月前
5ヶ月前
5ヶ月前
5ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
5ヶ月前
1ヶ月前
5ヶ月前
5ヶ月前
5ヶ月前
5ヶ月前
1ヶ月前
5ヶ月前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /*
  2. "use client";
  3. import React, { useCallback, useEffect, useState } from "react";
  4. import {
  5. Box,
  6. Button,
  7. Card,
  8. CardContent,
  9. CardActions,
  10. Stack,
  11. Typography,
  12. Chip,
  13. CircularProgress,
  14. Grid,
  15. } from "@mui/material";
  16. import ArrowBackIcon from "@mui/icons-material/ArrowBack";
  17. import { useTranslation } from "react-i18next";
  18. import { fetchAllJoPickOrders, AllJoPickOrderResponse } from "@/app/api/jo/actions";
  19. import JobPickExecution from "./newJobPickExecution";
  20. interface Props {
  21. onSwitchToRecordTab?: () => void;
  22. }
  23. const JoPickOrderList: React.FC<Props> = ({ onSwitchToRecordTab }) =>{
  24. const { t } = useTranslation(["common", "jo"]);
  25. const [loading, setLoading] = useState(false);
  26. const [pickOrders, setPickOrders] = useState<AllJoPickOrderResponse[]>([]);
  27. const [selectedPickOrderId, setSelectedPickOrderId] = useState<number | undefined>(undefined);
  28. const [selectedJobOrderId, setSelectedJobOrderId] = useState<number | undefined>(undefined);
  29. type PickOrderFilter = "all" | "drink" | "Powder_Mixture" | "other";
  30. const [filter, setFilter] = useState<PickOrderFilter>("all");
  31. type FloorFilter = "ALL" | "2F" | "3F" | "4F" | "NO_LOT";
  32. const [floorFilter, setFloorFilter] = useState<FloorFilter>("ALL");
  33. const fetchPickOrders = useCallback(async () => {
  34. setLoading(true);
  35. try {
  36. const typeParam = filter === "all" ? undefined : filter;
  37. const floorParam = floorFilter === "ALL" ? undefined : floorFilter;
  38. const data = await fetchAllJoPickOrders(typeParam, floorParam);
  39. setPickOrders(Array.isArray(data) ? data : []);
  40. } catch (e) {
  41. console.error(e);
  42. setPickOrders([]);
  43. } finally {
  44. setLoading(false);
  45. }
  46. }, [filter, floorFilter]);
  47. useEffect(() => {
  48. fetchPickOrders();
  49. }, [fetchPickOrders, filter, floorFilter]);
  50. const handleBackToList = useCallback(() => {
  51. setSelectedPickOrderId(undefined);
  52. setSelectedJobOrderId(undefined);
  53. fetchPickOrders();
  54. }, [fetchPickOrders]);
  55. // If a pick order is selected, show JobPickExecution detail view
  56. if (selectedPickOrderId !== undefined) {
  57. return (
  58. <Box>
  59. <Box sx={{ mb: 2 }}>
  60. <Button
  61. variant="outlined"
  62. onClick={handleBackToList}
  63. startIcon={<ArrowBackIcon />}
  64. >
  65. {t("Back to List")}
  66. </Button>
  67. </Box>
  68. <JobPickExecution
  69. filterArgs={{ pickOrderId: selectedPickOrderId, jobOrderId: selectedJobOrderId }}
  70. //onSwitchToRecordTab={onSwitchToRecordTab}
  71. onBackToList={handleBackToList} // 传递新的回调
  72. />
  73. </Box>
  74. );
  75. }
  76. return (
  77. <Box>
  78. {loading ? (
  79. <Box sx={{ display: "flex", justifyContent: "center", p: 3 }}>
  80. <CircularProgress />
  81. </Box>
  82. ) : (
  83. <Box>
  84. <Box sx={{ display: 'flex', gap: 1, alignItems: 'center', flexWrap: 'wrap', mb: 2 }}>
  85. <Button
  86. variant={filter === 'all' ? 'contained' : 'outlined'}
  87. size="small"
  88. onClick={() => setFilter('all')}
  89. >
  90. {t("All")}
  91. </Button>
  92. <Button
  93. variant={filter === 'drink' ? 'contained' : 'outlined'}
  94. size="small"
  95. onClick={() => setFilter('drink')}
  96. >
  97. {t("Drink")}
  98. </Button>
  99. <Button
  100. variant={filter === 'Powder_Mixture' ? 'contained' : 'outlined'}
  101. size="small"
  102. onClick={() => setFilter('Powder_Mixture')}
  103. >
  104. {t("Powder Mixture")}
  105. </Button>
  106. <Button
  107. variant={filter === 'other' ? 'contained' : 'outlined'}
  108. size="small"
  109. onClick={() => setFilter('other')}
  110. >
  111. {t("Other")}
  112. </Button>
  113. </Box>
  114. <Box sx={{ display: 'flex', gap: 1, alignItems: 'center', flexWrap: 'wrap', mb: 2 }}>
  115. <Button
  116. variant={floorFilter === "ALL" ? "contained" : "outlined"}
  117. size="small"
  118. onClick={() => setFloorFilter("ALL")}
  119. >
  120. {t("Select All")}
  121. </Button>
  122. <Button
  123. variant={floorFilter === "2F" ? "contained" : "outlined"}
  124. size="small"
  125. onClick={() => setFloorFilter("2F")}
  126. >
  127. 2F
  128. </Button>
  129. <Button
  130. variant={floorFilter === "3F" ? "contained" : "outlined"}
  131. size="small"
  132. onClick={() => setFloorFilter("3F")}
  133. >
  134. 3F
  135. </Button>
  136. <Button
  137. variant={floorFilter === "4F" ? "contained" : "outlined"}
  138. size="small"
  139. onClick={() => setFloorFilter("4F")}
  140. >
  141. 4F
  142. </Button>
  143. <Button
  144. variant={floorFilter === "NO_LOT" ? "contained" : "outlined"}
  145. size="small"
  146. onClick={() => setFloorFilter("NO_LOT")}
  147. >
  148. {t("No Lot")}
  149. </Button>
  150. </Box>
  151. <Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
  152. {t("Total pick orders")}: {pickOrders.length}
  153. </Typography>
  154. <Grid container spacing={2}>
  155. {pickOrders.map((pickOrder) => {
  156. const status = String(pickOrder.jobOrderStatus || "");
  157. const statusLower = status.toLowerCase();
  158. const statusColor =
  159. statusLower === "completed"
  160. ? "success"
  161. : statusLower === "pending" || statusLower === "processing"
  162. ? "primary"
  163. : "default";
  164. const finishedCount = pickOrder.finishedPickOLineCount ?? 0;
  165. return (
  166. <Grid key={pickOrder.id} item xs={12} sm={6} md={4}>
  167. <Card
  168. sx={{
  169. minHeight: 180,
  170. maxHeight: 280,
  171. display: "flex",
  172. flexDirection: "column",
  173. }}
  174. >
  175. <CardContent
  176. sx={{
  177. pb: 1,
  178. flexGrow: 1,
  179. overflow: "auto",
  180. }}
  181. >
  182. <Stack direction="row" justifyContent="space-between" alignItems="center">
  183. <Box sx={{ minWidth: 0 }}>
  184. <Typography variant="subtitle1">
  185. {t("Job Order")}: {pickOrder.jobOrderCode || "-"}
  186. </Typography>
  187. </Box>
  188. <Chip size="small" label={t(status)} color={statusColor as any} />
  189. </Stack>
  190. <Typography variant="body2" color="text.secondary">
  191. {t("Lot No")}: {pickOrder.lotNo || "-"}
  192. </Typography>
  193. <Typography variant="body2" color="text.secondary">
  194. {t("Pick Order")}: {pickOrder.pickOrderCode || "-"}
  195. </Typography>
  196. <Typography variant="body2" color="text.secondary">
  197. {t("Item Name")}: {pickOrder.itemName}
  198. </Typography>
  199. <Typography variant="body2" color="text.secondary">
  200. {t("Required Qty")}: {pickOrder.reqQty} ({pickOrder.uomName})
  201. </Typography>
  202. {floorFilter === "ALL" ? (
  203. <>
  204. {pickOrder.floorPickCounts?.map(({ floor, finishedCount, totalCount }) => (
  205. <Typography
  206. key={floor}
  207. variant="body2"
  208. color="text.secondary"
  209. component="span"
  210. sx={{ mr: 1 }}
  211. >
  212. {floor}: {finishedCount}/{totalCount}
  213. </Typography>
  214. ))}
  215. {!!pickOrder.noLotPickCount && (
  216. <Typography
  217. key="NO_LOT"
  218. variant="body2"
  219. color="text.secondary"
  220. component="span"
  221. sx={{ mr: 1 }}
  222. >
  223. {t("No Lot")}: {pickOrder.noLotPickCount.finishedCount}/{pickOrder.noLotPickCount.totalCount}
  224. </Typography>
  225. )}
  226. </>
  227. ) : floorFilter === "NO_LOT" ? (
  228. !!pickOrder.noLotPickCount && (
  229. <Typography
  230. key="NO_LOT"
  231. variant="body2"
  232. color="text.secondary"
  233. component="span"
  234. sx={{ mr: 1 }}
  235. >
  236. {t("No Lot")}: {pickOrder.noLotPickCount.finishedCount}/{pickOrder.noLotPickCount.totalCount}
  237. </Typography>
  238. )
  239. ) : (
  240. pickOrder.floorPickCounts
  241. ?.filter((c) => c.floor === floorFilter)
  242. .map(({ floor, finishedCount, totalCount }) => (
  243. <Typography
  244. key={floor}
  245. variant="body2"
  246. color="text.secondary"
  247. component="span"
  248. sx={{ mr: 1 }}
  249. >
  250. {floor}: {finishedCount}/{totalCount}
  251. </Typography>
  252. ))
  253. )}
  254. {typeof pickOrder.suggestedFailCount === "number" && pickOrder.suggestedFailCount > 0 && (
  255. <Typography variant="body2" color="error" sx={{ mt: 0.5 }}>
  256. {t("Suggested Fail")}: {pickOrder.suggestedFailCount}
  257. </Typography>
  258. )}
  259. {statusLower !== "pending" && finishedCount > 0 && (
  260. <Box sx={{ mt: 1 }}>
  261. <Typography variant="body2" fontWeight={600}>
  262. {t("Finished lines")}: {finishedCount}
  263. </Typography>
  264. </Box>
  265. )}
  266. </CardContent>
  267. <CardActions sx={{ pt: 0.5 }}>
  268. <Button
  269. variant="contained"
  270. size="small"
  271. onClick={() => {
  272. setSelectedPickOrderId(pickOrder.pickOrderId ?? undefined);
  273. setSelectedJobOrderId(pickOrder.jobOrderId ?? undefined);
  274. }}
  275. >
  276. {t("View Details")}
  277. </Button>
  278. <Box sx={{ flex: 1 }} />
  279. </CardActions>
  280. </Card>
  281. </Grid>
  282. );
  283. })}
  284. </Grid>
  285. </Box>
  286. )}
  287. </Box>
  288. );
  289. };
  290. export default JoPickOrderList;
  291. */