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.
 
 

221 line
7.8 KiB

  1. "use client";
  2. import React, { useCallback, useEffect, useState } from "react";
  3. import {
  4. Box,
  5. Button,
  6. Card,
  7. CardContent,
  8. CardActions,
  9. Stack,
  10. Typography,
  11. Chip,
  12. CircularProgress,
  13. TablePagination,
  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 PER_PAGE = 6;
  24. const JoPickOrderList: React.FC<Props> = ({ onSwitchToRecordTab }) =>{
  25. const { t } = useTranslation(["common", "jo"]);
  26. const [loading, setLoading] = useState(false);
  27. const [pickOrders, setPickOrders] = useState<AllJoPickOrderResponse[]>([]);
  28. const [page, setPage] = useState(0);
  29. const [selectedPickOrderId, setSelectedPickOrderId] = useState<number | undefined>(undefined);
  30. const [selectedJobOrderId, setSelectedJobOrderId] = useState<number | undefined>(undefined);
  31. type PickOrderFilter = "all" | "drink" | "other";
  32. const [filter, setFilter] = useState<PickOrderFilter>("all");
  33. const fetchPickOrders = useCallback(async () => {
  34. setLoading(true);
  35. try {
  36. const isDrinkParam =
  37. filter === "all" ? undefined : filter === "drink" ? true : false;
  38. const data = await fetchAllJoPickOrders(isDrinkParam);
  39. setPickOrders(Array.isArray(data) ? data : []);
  40. setPage(0);
  41. } catch (e) {
  42. console.error(e);
  43. setPickOrders([]);
  44. } finally {
  45. setLoading(false);
  46. }
  47. }, [filter]);
  48. useEffect(() => {
  49. fetchPickOrders( );
  50. }, [fetchPickOrders, filter]);
  51. const handleBackToList = useCallback(() => {
  52. setSelectedPickOrderId(undefined);
  53. setSelectedJobOrderId(undefined);
  54. fetchPickOrders();
  55. }, [fetchPickOrders]);
  56. // If a pick order is selected, show JobPickExecution detail view
  57. if (selectedPickOrderId !== undefined) {
  58. return (
  59. <Box>
  60. <Box sx={{ mb: 2 }}>
  61. <Button
  62. variant="outlined"
  63. onClick={() => {
  64. setSelectedPickOrderId(undefined);
  65. setSelectedJobOrderId(undefined);
  66. }}
  67. startIcon={<ArrowBackIcon />}
  68. >
  69. {t("Back to List")}
  70. </Button>
  71. </Box>
  72. <JobPickExecution
  73. filterArgs={{ pickOrderId: selectedPickOrderId, jobOrderId: selectedJobOrderId }}
  74. //onSwitchToRecordTab={onSwitchToRecordTab}
  75. onBackToList={handleBackToList} // 传递新的回调
  76. />
  77. </Box>
  78. );
  79. }
  80. const startIdx = page * PER_PAGE;
  81. const paged = pickOrders.slice(startIdx, startIdx + PER_PAGE);
  82. return (
  83. <Box>
  84. {loading ? (
  85. <Box sx={{ display: "flex", justifyContent: "center", p: 3 }}>
  86. <CircularProgress />
  87. </Box>
  88. ) : (
  89. <Box>
  90. <Box sx={{ display: 'flex', gap: 1, alignItems: 'center', flexWrap: 'wrap', mb: 2 }}>
  91. <Button
  92. variant={filter === 'all' ? 'contained' : 'outlined'}
  93. size="small"
  94. onClick={() => setFilter('all')}
  95. >
  96. {t("All")}
  97. </Button>
  98. <Button
  99. variant={filter === 'drink' ? 'contained' : 'outlined'}
  100. size="small"
  101. onClick={() => setFilter('drink')}
  102. >
  103. {t("Drink")}
  104. </Button>
  105. <Button
  106. variant={filter === 'other' ? 'contained' : 'outlined'}
  107. size="small"
  108. onClick={() => setFilter('other')}
  109. >
  110. {t("Other")}
  111. </Button>
  112. </Box>
  113. <Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
  114. {t("Total pick orders")}: {pickOrders.length}
  115. </Typography>
  116. <Grid container spacing={2}>
  117. {paged.map((pickOrder) => {
  118. const status = String(pickOrder.jobOrderStatus || "");
  119. const statusLower = status.toLowerCase();
  120. const statusColor =
  121. statusLower === "completed"
  122. ? "success"
  123. : statusLower === "pending" || statusLower === "processing"
  124. ? "primary"
  125. : "default";
  126. const finishedCount = pickOrder.finishedPickOLineCount ?? 0;
  127. return (
  128. <Grid key={pickOrder.id} item xs={12} sm={6} md={4}>
  129. <Card
  130. sx={{
  131. minHeight: 160,
  132. maxHeight: 240,
  133. display: "flex",
  134. flexDirection: "column",
  135. }}
  136. >
  137. <CardContent
  138. sx={{
  139. pb: 1,
  140. flexGrow: 1,
  141. overflow: "auto",
  142. }}
  143. >
  144. <Stack direction="row" justifyContent="space-between" alignItems="center">
  145. <Box sx={{ minWidth: 0 }}>
  146. <Typography variant="subtitle1">
  147. {t("Job Order")}: {pickOrder.jobOrderCode || "-"}
  148. </Typography>
  149. </Box>
  150. <Chip size="small" label={t(status)} color={statusColor as any} />
  151. </Stack>
  152. <Typography variant="body2" color="text.secondary">
  153. {t("Lot No")}: {pickOrder.lotNo || "-"}
  154. </Typography>
  155. <Typography variant="body2" color="text.secondary">
  156. {t("Pick Order")}: {pickOrder.pickOrderCode || "-"}
  157. </Typography>
  158. <Typography variant="body2" color="text.secondary">
  159. {t("Item Name")}: {pickOrder.itemName}
  160. </Typography>
  161. <Typography variant="body2" color="text.secondary">
  162. {t("Required Qty")}: {pickOrder.reqQty} ({pickOrder.uomName})
  163. </Typography>
  164. {pickOrder.floorPickCounts?.map(({ floor, finishedCount, totalCount }) => (
  165. <Typography key={floor} variant="body2" color="text.secondary" component="span" sx={{ mr: 1 }}>
  166. {floor}: {finishedCount}/{totalCount}
  167. </Typography>
  168. ))}
  169. {statusLower !== "pending" && finishedCount > 0 && (
  170. <Box sx={{ mt: 1 }}>
  171. <Typography variant="body2" fontWeight={600}>
  172. {t("Finished lines")}: {finishedCount}
  173. </Typography>
  174. </Box>
  175. )}
  176. </CardContent>
  177. <CardActions sx={{ pt: 0.5 }}>
  178. <Button
  179. variant="contained"
  180. size="small"
  181. onClick={() => {
  182. setSelectedPickOrderId(pickOrder.pickOrderId ?? undefined);
  183. setSelectedJobOrderId(pickOrder.jobOrderId ?? undefined);
  184. }}
  185. >
  186. {t("View Details")}
  187. </Button>
  188. <Box sx={{ flex: 1 }} />
  189. </CardActions>
  190. </Card>
  191. </Grid>
  192. );
  193. })}
  194. </Grid>
  195. {pickOrders.length > 0 && (
  196. <TablePagination
  197. component="div"
  198. count={pickOrders.length}
  199. page={page}
  200. rowsPerPage={PER_PAGE}
  201. onPageChange={(e, p) => setPage(p)}
  202. rowsPerPageOptions={[PER_PAGE]}
  203. />
  204. )}
  205. </Box>
  206. )}
  207. </Box>
  208. );
  209. };
  210. export default JoPickOrderList;