Ver a proveniência

Merge branch 'MergeProblem1' of https://git.2fi-solutions.com/jason/FPSMS-frontend into MergeProblem1

reset-do-picking-order
Tommy\2Fi-Staff há 6 dias
ascendente
cometimento
84088c143d
5 ficheiros alterados com 80 adições e 25 eliminações
  1. +1
    -0
      src/app/api/jo/index.ts
  2. +14
    -5
      src/components/JoSearch/JoSearch.tsx
  3. +10
    -1
      src/components/NavigationContent/NavigationContent.tsx
  4. +49
    -16
      src/components/StockIssue/SearchPage.tsx
  5. +6
    -3
      src/components/StockTakeManagement/StockTakeTab.tsx

+ 1
- 0
src/app/api/jo/index.ts Ver ficheiro

@@ -21,6 +21,7 @@ export interface JobOrder {
reqQty: number;
item: Item;
itemName: string;
bomId: number;
// uom: Uom;
pickLines?: JoDetailPickLine[];
status: JoStatus;


+ 14
- 5
src/components/JoSearch/JoSearch.tsx Ver ficheiro

@@ -161,11 +161,20 @@ const JoSearch: React.FC<Props> = ({ defaultInputs, bomCombo, printerCombo, jobT
const fetchBomForJo = useCallback(async (jo: JobOrder): Promise<BomCombo | null> => {
try {
const detailedJo = detailedJos.get(jo.id) || await fetchJoDetailClient(jo.id);
const matchingBom = bomCombo.find(bom => {
return true; // 临时占位
});
return matchingBom || null;
// 若列表的 jo 已有 bomId(之後若 API 有回傳),可直接用
const bomId = (jo as { bomId?: number }).bomId;
if (bomId != null) {
const matchingBom = bomCombo.find(bom => bom.id === bomId);
if (matchingBom) return matchingBom;
}
// 否則打明細 API,明細會帶 bomId
const detailedJo = detailedJos.get(jo.id) ?? await fetchJoDetailClient(jo.id);
const detailBomId = (detailedJo as { bomId?: number }).bomId;
if (detailBomId != null) {
const matchingBom = bomCombo.find(bom => bom.id === detailBomId);
if (matchingBom) return matchingBom;
}
return null;
} catch (error) {
console.error("Error fetching BOM for JO:", error);
return null;


+ 10
- 1
src/components/NavigationContent/NavigationContent.tsx Ver ficheiro

@@ -1,5 +1,7 @@
import { useSession } from "next-auth/react";
import Box from "@mui/material/Box";
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import React from "react";
import List from "@mui/material/List";
import ListItemButton from "@mui/material/ListItemButton";
@@ -440,7 +442,14 @@ const NavigationContent: React.FC = () => {
minHeight: 56,
}}
>
<Logo height={42} />
<Stack direction="column" alignItems="flex-start" spacing={0.5}>
<Logo height={42} />
{process.env.NODE_ENV === "production" && (
<Typography variant="caption" sx={{ fontWeight: 600, color: "text.secondary" }}>
正式服務器 Production Server
</Typography>
)}
</Stack>
</Box>
<Box sx={{ borderTop: 1, borderColor: "divider" }} />
<List component="nav" sx={{ flex: 1, overflow: "auto", py: 1, px: 0 }}>


+ 49
- 16
src/components/StockIssue/SearchPage.tsx Ver ficheiro

@@ -1,5 +1,6 @@
"use client";

import dayjs from "dayjs";
import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
import SearchBox, { Criterion } from "../SearchBox";
import { useCallback, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -52,7 +53,7 @@ const SearchPage: React.FC<Props> = ({ dataList }) => {
const [selectedIds, setSelectedIds] = useState<(string | number)[]>([]);
const [submittingIds, setSubmittingIds] = useState<Set<number>>(new Set());
const [batchSubmitting, setBatchSubmitting] = useState(false);
const [paging, setPaging] = useState({ pageNum: 1, pageSize: 10 });
const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
() => [
{
@@ -251,7 +252,33 @@ const SearchPage: React.FC<Props> = ({ dataList }) => {
{ name: "itemDescription", label: t("Item") },
{ name: "lotNo", label: t("Lot No.") },
{ name: "storeLocation", label: t("Location") },
{ name: "expiryDate", label: t("Expiry Date") },
{
name: "expiryDate",
label: t("Expiry Date"),
renderCell: (item) => {
const raw = String(item.expiryDate ?? "").trim();
if (!raw) return "—";
let d;
if (raw.includes(",")) {
const parts = raw.split(",").map((s) => parseInt(s.trim(), 10));
const [y, m, d_] = parts;
if (parts.length >= 3 && y != null && m != null && d_ != null && !Number.isNaN(y) && !Number.isNaN(m) && !Number.isNaN(d_)) {
d = dayjs(new Date(y, m - 1, d_));
} else {
d = dayjs("");
}
} else {
let normalized = raw;
if (raw.length === 7) {
normalized = raw.slice(0, 4) + "0" + raw.slice(4, 5) + raw.slice(5, 7);
} else if (raw.length === 6) {
normalized = raw.slice(0, 4) + "0" + raw.slice(4, 5) + "0" + raw.slice(5, 6);
}
d = dayjs(normalized, "YYYYMMDD", true);
}
return d.isValid() ? d.format(OUTPUT_DATE_FORMAT) : raw;
},
},
{ name: "remainingQty", label: t("Remaining Qty") },
{
name: "id",
@@ -280,6 +307,7 @@ const SearchPage: React.FC<Props> = ({ dataList }) => {
(_: React.SyntheticEvent, value: string) => {
setTab(value as "miss" | "bad" | "expiry");
setSelectedIds([]);
setPaging((prev) => ({ ...prev, pageNum: 1 })); // 新增:切 Tab 时回到第 1 页
},
[],
);
@@ -291,8 +319,9 @@ const SearchPage: React.FC<Props> = ({ dataList }) => {
<SearchResults<StockIssueResult>
items={items}
columns={missColumns}
pagingController={{ pageNum: 1, pageSize: 10 }}
pagingController={paging}
checkboxIds={selectedIds}
setPagingController={setPaging}
setCheckboxIds={setSelectedIds}
/>
);
@@ -304,7 +333,8 @@ const SearchPage: React.FC<Props> = ({ dataList }) => {
<SearchResults<StockIssueResult>
items={items}
columns={badColumns}
pagingController={{ pageNum: 1, pageSize: 10 }}
pagingController={paging}
setPagingController={setPaging}
checkboxIds={selectedIds}
setCheckboxIds={setSelectedIds}
/>
@@ -316,7 +346,8 @@ const SearchPage: React.FC<Props> = ({ dataList }) => {
<SearchResults<ExpiryItemResult>
items={items}
columns={expiryColumns}
pagingController={{ pageNum: 1, pageSize: 10 }}
pagingController={paging}
setPagingController={setPaging}
checkboxIds={selectedIds}
setCheckboxIds={setSelectedIds}
/>
@@ -336,16 +367,18 @@ const SearchPage: React.FC<Props> = ({ dataList }) => {
onSearch={handleSearch}
/>

<Box sx={{ display: "flex", justifyContent: "flex-end", mb: 1 }}>
<Button
variant="contained"
color="primary"
onClick={handleSubmitSelected}
disabled={batchSubmitting || !currentUserId}
>
{batchSubmitting ? tab === "miss" ? t("Processing...") : tab === "bad" ? t("Disposing...") : t("Disposing...") : t("Batch Disposed All")}
</Button>
</Box>
{tab === "expiry" && (
<Box sx={{ display: "flex", justifyContent: "flex-end", mb: 1 }}>
<Button
variant="contained"
color="primary"
onClick={handleSubmitSelected}
disabled={batchSubmitting || !currentUserId}
>
{batchSubmitting ? t("Disposing...") : t("Batch Disposed All")}
</Button>
</Box>
)}

{renderCurrentTab()}
<SubmitIssueForm


+ 6
- 3
src/components/StockTakeManagement/StockTakeTab.tsx Ver ficheiro

@@ -80,13 +80,14 @@ const StockTakeTab: React.FC = () => {
)
) : null
)}
{/*
{viewScope === "approver-by-section" && tabValue === 1 && (
<ApproverStockTake
selectedSession={selectedSession}
onBack={handleBackToList}
onSnackbar={handleSnackbar}
/>
)}
)} */}
{viewScope === "approver-all" && tabValue === 2 && (
<ApproverStockTakeAll
selectedSession={selectedSession}
@@ -124,7 +125,7 @@ const StockTakeTab: React.FC = () => {
sx={{ mb: 2 }}
>
<Tab label={t("Picker")} />
<Tab label={t("Approver")} />
{/* <Tab label={t("Approver")} /> */}
<Tab label={t("Approver All")} />
</Tabs>

@@ -137,6 +138,7 @@ const StockTakeTab: React.FC = () => {
onReStockTakeClick={handleReStockTakeClick}
/>
)}
{/*
{tabValue === 1 && (
<ApproverCardList
onCardClick={(session) => {
@@ -145,7 +147,8 @@ const StockTakeTab: React.FC = () => {
}}
/>
)}
{tabValue === 2 && (
*/}
{tabValue === 1 && (
<ApproverAllCardList
onCardClick={handleApproverAllCardClick}
/>


Carregando…
Cancelar
Guardar