CANCERYS\kw093 1 тиждень тому
джерело
коміт
86dc6f7f4c
8 змінених файлів з 1569 додано та 137 видалено
  1. +19
    -0
      src/app/api/jo/actions.ts
  2. +37
    -3
      src/app/api/pickOrder/actions.ts
  3. +1
    -0
      src/app/api/settings/item/actions.ts
  4. +490
    -0
      src/components/PickOrderSearch/AssignAndRelease.tsx
  5. +54
    -37
      src/components/PickOrderSearch/PickOrderSearch.tsx
  6. +233
    -0
      src/components/PickOrderSearch/assignTo.tsx
  7. +709
    -94
      src/components/PickOrderSearch/newcreatitem.tsx
  8. +26
    -3
      src/i18n/zh/pickOrder.json

+ 19
- 0
src/app/api/jo/actions.ts Переглянути файл

@@ -67,6 +67,25 @@ export interface isCorrectMachineUsedResponse<T> {
entity: T;
}

export interface JobOrderDetail {
id: number;
code: string;
name: string;
reqQty: number;
uom: string;
pickLines: any[];
status: string;
}

export const fetchJobOrderDetailByCode = cache(async (code: string) => {
return serverFetchJson<JobOrderDetail>(
`${BASE_API_URL}/jo/detailByCode/${code}`,
{
method: "GET",
next: { tags: ["jo"] },
},
);
});
export const isOperatorExist = async (username: string) => {
const isExist = await serverFetchJson<IsOperatorExistResponse<Operator>>(
`${BASE_API_URL}/jop/isOperatorExist`,


+ 37
- 3
src/app/api/pickOrder/actions.ts Переглянути файл

@@ -102,7 +102,7 @@ export interface GetPickOrderLineInfo {
itemId: number;
itemCode: string;
itemName: string;
availableQty: number;
availableQty: number| null;
requiredQty: number;
uomCode: string;
uomDesc: string;
@@ -142,7 +142,17 @@ export interface PickOrderLotDetailResponse {
lotAvailability: 'available' | 'insufficient_stock' | 'expired' | 'status_unavailable';
}


export interface GetPickOrderLineInfo {
id: number;
itemId: number;
itemCode: string;
itemName: string;
availableQty: number;
requiredQty: number;
uomCode: string;
uomDesc: string;
suggestedList: any[];
}
export const fetchAllPickOrderDetails = cache(async () => {
return serverFetchJson<GetPickOrderInfoResponse>(
`${BASE_API_URL}/pickOrder/detail`,
@@ -182,7 +192,7 @@ export const createPickOrder = async (data: SavePickOrderRequest) => {
return po;
}

export const consolidatePickOrder = async (ids: number[]) => {
export const assignPickOrder = async (ids: number[]) => {
const pickOrder = await serverFetchJson<any>(
`${BASE_API_URL}/pickOrder/conso`,
{
@@ -231,6 +241,30 @@ export const fetchPickOrderClient = cache(
},
);


export const fetchPickOrderWithStockClient = cache(
async (queryParams?: Record<string, any>) => {
if (queryParams) {
const queryString = new URLSearchParams(queryParams).toString();
return serverFetchJson<RecordsRes<GetPickOrderInfo[]>>(
`${BASE_API_URL}/pickOrder/getRecordByPageWithStock?${queryString}`,
{
method: "GET",
next: { tags: ["pickorder"] },
},
);
} else {
return serverFetchJson<RecordsRes<GetPickOrderInfo[]>>(
`${BASE_API_URL}/pickOrder/getRecordByPageWithStock`,
{
method: "GET",
next: { tags: ["pickorder"] },
},
);
}
},
);

export const fetchConsoPickOrderClient = cache(
async (queryParams?: Record<string, any>) => {
if (queryParams) {


+ 1
- 0
src/app/api/settings/item/actions.ts Переглянути файл

@@ -57,6 +57,7 @@ export interface ItemCombo {
uomId: number,
uom: string,
group?: string,
currentStockBalance?: number,
}

export const fetchAllItemsInClient = cache(async () => {


+ 490
- 0
src/components/PickOrderSearch/AssignAndRelease.tsx Переглянути файл

@@ -0,0 +1,490 @@
"use client";
import {
Autocomplete,
Box,
Button,
CircularProgress,
FormControl,
Grid,
Modal,
TextField,
Typography,
Accordion,
AccordionSummary,
AccordionDetails,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
} from "@mui/material";
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import { useCallback, useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import SearchResults, { Column } from "../SearchResults/SearchResults";
import {
PickOrderResult,
} from "@/app/api/pickOrder";
import {
assignPickOrder,
fetchPickOrderClient,
fetchPickOrderWithStockClient,
releasePickOrder,
ReleasePickOrderInputs,
GetPickOrderInfo,
GetPickOrderLineInfo,
} from "@/app/api/pickOrder/actions";
import { fetchNameList, NameList } from "@/app/api/user/actions";
import {
FormProvider,
useForm,
} from "react-hook-form";
import { isEmpty, upperCase, upperFirst } from "lodash";
import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
import useUploadContext from "../UploadProvider/useUploadContext";
import dayjs from "dayjs";
import arraySupport from "dayjs/plugin/arraySupport";
import SearchBox, { Criterion } from "../SearchBox";
import { flatten, intersectionWith, sortBy, uniqBy } from "lodash";
import { arrayToDayjs } from "@/app/utils/formatUtil";

dayjs.extend(arraySupport);

interface Props {
filterArgs: Record<string, any>;
}

const style = {
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
bgcolor: "background.paper",
pt: 5,
px: 5,
pb: 10,
width: { xs: "100%", sm: "100%", md: "100%" },
};

const AssignAndRelease: React.FC<Props> = ({ filterArgs }) => {
const { t } = useTranslation("pickOrder");
const { setIsUploading } = useUploadContext();
// State for Pick Orders
const [selectedRows, setSelectedRows] = useState<(string | number)[]>([]);
const [filteredPickOrder, setFilteredPickOrder] = useState([] as GetPickOrderInfo[]);
const [isLoadingPickOrders, setIsLoadingPickOrders] = useState(false);
const [pagingController, setPagingController] = useState({
pageNum: 0,
pageSize: 10,
});
const [totalCountPickOrders, setTotalCountPickOrders] = useState<number>();

// State for Assign & Release Modal
const [modalOpen, setModalOpen] = useState(false);
const [usernameList, setUsernameList] = useState<NameList[]>([]);

// Add search state
const [searchQuery, setSearchQuery] = useState<Record<string, any>>({});
const [originalPickOrderData, setOriginalPickOrderData] = useState([] as GetPickOrderInfo[]);

const formProps = useForm<ReleasePickOrderInputs>();
const errors = formProps.formState.errors;

// Fetch Pick Orders with Stock Information
const fetchNewPagePickOrder = useCallback(
async (
pagingController: Record<string, number>,
filterArgs: Record<string, number>,
) => {
setIsLoadingPickOrders(true);
const params = {
...pagingController,
...filterArgs,
};
const res = await fetchPickOrderWithStockClient(params);
if (res) {
console.log(res);
setFilteredPickOrder(res.records);
setOriginalPickOrderData(res.records); // Store original data
setTotalCountPickOrders(res.total);
}
setIsLoadingPickOrders(false);
},
[],
);

// Add search criteria
const searchCriteria: Criterion<any>[] = useMemo(
() => [
{
label: t("Pick Order Code"),
paramName: "code",
type: "text"
},
{
label: t("Type"),
paramName: "type",
type: "autocomplete",
options: sortBy(
uniqBy(
originalPickOrderData.map((po) => ({
value: po.type,
label: t(upperCase(po.type)),
})),
"value",
),
"label",
),
},
{
label: t("Target Date From"),
label2: t("Target Date To"),
paramName: "targetDate",
type: "dateRange",
},
{
label: t("Status"),
paramName: "status",
type: "autocomplete",
options: sortBy(
uniqBy(
originalPickOrderData.map((po) => ({
value: po.status,
label: t(upperFirst(po.status)),
})),
"value",
),
"label",
),
},
],
[originalPickOrderData, t],
);

// Add search handler
const handleSearch = useCallback((query: Record<string, any>) => {
console.log("AssignAndRelease search triggered with query:", query);
setSearchQuery({ ...query });
// Apply search filters to the data
const filtered = originalPickOrderData.filter((po) => {
const poTargetDateStr = arrayToDayjs(po.targetDate);

const codeMatch = !query.code ||
po.code?.toLowerCase().includes((query.code || "").toLowerCase());
const dateMatch = !query.targetDate ||
poTargetDateStr.isSame(query.targetDate) ||
poTargetDateStr.isAfter(query.targetDate);
const dateToMatch = !query.targetDateTo ||
poTargetDateStr.isSame(query.targetDateTo) ||
poTargetDateStr.isBefore(query.targetDateTo);
const statusMatch = !query.status ||
query.status.toLowerCase() === "all" ||
po.status?.toLowerCase().includes((query.status || "").toLowerCase());
const typeMatch = !query.type ||
query.type.toLowerCase() === "all" ||
po.type?.toLowerCase().includes((query.type || "").toLowerCase());

return codeMatch && dateMatch && dateToMatch && statusMatch && typeMatch;
});
setFilteredPickOrder(filtered);
}, [originalPickOrderData]);

// Add reset handler
const handleReset = useCallback(() => {
setSearchQuery({});
// Reset to original data
setFilteredPickOrder(originalPickOrderData);
}, [originalPickOrderData]);

// Handle Assign & Release
const handleAssignAndRelease = useCallback(async (data: ReleasePickOrderInputs) => {
if (selectedRows.length === 0) return;
setIsUploading(true);
try {
// First, assign the pick orders
const assignRes = await assignPickOrder(selectedRows as number[]);
if (assignRes) {
console.log("Assign successful:", assignRes);
// Get the assign code from the response
const consoCode = assignRes.consoCode || assignRes.code;
if (consoCode) {
// Then, release the assign pick order
const releaseData = {
consoCode: consoCode,
assignTo: data.assignTo
};
const releaseRes = await releasePickOrder(releaseData);
if (releaseRes) {
console.log("Release successful:", releaseRes);
setModalOpen(false);
// Clear selected rows
setSelectedRows([]);
// Refresh the pick orders list
fetchNewPagePickOrder(pagingController, filterArgs);
}
}
}
} catch (error) {
console.error("Error in assign and release:", error);
} finally {
setIsUploading(false);
}
}, [selectedRows, setIsUploading, fetchNewPagePickOrder, pagingController, filterArgs]);

// Open assign & release modal
const openAssignModal = useCallback(() => {
setModalOpen(true);
// Reset form
formProps.reset();
}, [formProps]);

// Load data
useEffect(() => {
fetchNewPagePickOrder(pagingController, filterArgs);
}, [fetchNewPagePickOrder, pagingController, filterArgs]);

// Load username list
useEffect(() => {
const loadUsernameList = async () => {
try {
const res = await fetchNameList();
if (res) {
setUsernameList(res);
}
} catch (error) {
console.error("Error loading username list:", error);
}
};
loadUsernameList();
}, []);

// Pick Orders columns with detailed item information
const pickOrderColumns = useMemo<Column<GetPickOrderInfo>[]>(
() => [
{
name: "id",
label: "",
type: "checkbox",
disabled: (params) => {
return !isEmpty(params.consoCode);
},
},
{
name: "code",
label: t("Pick Order Code"),
},
{
name: "pickOrderLines",
label: t("Items"),
renderCell: (params) => {
if (!params.pickOrderLines || params.pickOrderLines.length === 0) return "";
return (
<Accordion sx={{ boxShadow: 'none', '&:before': { display: 'none' } }}>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
sx={{ minHeight: 'auto', padding: 0 }}
>
<Typography variant="body2">
{params.pickOrderLines.length} items
</Typography>
</AccordionSummary>
<AccordionDetails sx={{ padding: 1 }}>
<TableContainer component={Paper} sx={{ maxHeight: 200 }}>
<Table size="small">
<TableHead>
<TableRow>
<TableCell sx={{ fontSize: '0.75rem', padding: '4px' }}>{t("Item Name")}</TableCell>
<TableCell sx={{ fontSize: '0.75rem', padding: '4px' }}>{t("Required Qty")}</TableCell>
<TableCell sx={{ fontSize: '0.75rem', padding: '4px' }}>{t("Available Qty")}</TableCell>
<TableCell sx={{ fontSize: '0.75rem', padding: '4px' }}>{t("Unit")}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{params.pickOrderLines.map((line: GetPickOrderLineInfo, index: number) => (
<TableRow key={index}>
<TableCell sx={{ fontSize: '0.75rem', padding: '4px' }}>
{line.itemName}
</TableCell>
<TableCell sx={{ fontSize: '0.75rem', padding: '4px' }}>
{line.requiredQty}
</TableCell>
<TableCell sx={{ fontSize: '0.75rem', padding: '4px' }}>
<Typography
variant="caption"
//color={line.availableQty && line.availableQty >= line.requiredQty ? 'success.main' : 'error.main'}
>
{line.availableQty ?? 0}
</Typography>
</TableCell>
<TableCell sx={{ fontSize: '0.75rem', padding: '4px' }}>
{line.uomDesc}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</AccordionDetails>
</Accordion>
);
},
},
{
name: "targetDate",
label: t("Target Date"),
renderCell: (params) => {
return (
dayjs(params.targetDate)
.add(-1, "month")
.format(OUTPUT_DATE_FORMAT)
);
},
},
{
name: "status",
label: t("Status"),
renderCell: (params) => {
return upperFirst(params.status);
},
},
],
[t],
);

return (
<>
{/* Search Box */}
<SearchBox
criteria={searchCriteria}
onSearch={handleSearch}
onReset={handleReset}
/>
{/* Pick Orders View */}
<Grid container rowGap={1}>
{/* Remove the button from here */}
<Grid item xs={12}>
{isLoadingPickOrders ? (
<CircularProgress size={40} />
) : (
<SearchResults<GetPickOrderInfo>
items={filteredPickOrder}
columns={pickOrderColumns}
pagingController={pagingController}
setPagingController={setPagingController}
totalCount={totalCountPickOrders}
checkboxIds={selectedRows!}
setCheckboxIds={setSelectedRows}
/>
)}
</Grid>
{/* Add the button below the table */}
<Grid item xs={12}>
<Box sx={{ display: 'flex', justifyContent: 'flex-start', mt: 2 }}>
<Button
disabled={selectedRows.length < 1}
variant="outlined"
onClick={openAssignModal}
>
{t("Assign & Release")}
</Button>
</Box>
</Grid>
</Grid>

{/* Assign & Release Modal */}
{modalOpen ? (
<Modal
open={modalOpen}
onClose={() => setModalOpen(false)}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box sx={style}>
<Grid container rowGap={2}>
<Grid item xs={12}>
<Typography variant="h6" component="h2">
{t("assign & Release Pick Orders")}
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="body1" color="text.secondary">
{t("Selected Pick Orders")}: {selectedRows.length}
</Typography>
</Grid>

<Grid item xs={12}>
<FormProvider {...formProps}>
<form onSubmit={formProps.handleSubmit(handleAssignAndRelease)}>
<Grid container spacing={2}>
<Grid item xs={12}>
<FormControl fullWidth>
<Autocomplete
options={usernameList}
getOptionLabel={(option) => option.name}
onChange={(_, value) => {
formProps.setValue("assignTo", value?.id || 0);
}}
renderInput={(params) => (
<TextField
{...params}
label={t("Assign To")}
error={!!errors.assignTo}
helperText={errors.assignTo?.message}
required
/>
)}
/>
</FormControl>
</Grid>
<Grid item xs={12}>
<Typography variant="body2" color="warning.main">
{t("This action will assign the selected pick orders and release them immediately.")}
</Typography>
</Grid>
<Grid item xs={12}>
<Box sx={{ display: 'flex', gap: 2, justifyContent: 'flex-end' }}>
<Button
variant="outlined"
onClick={() => setModalOpen(false)}
>
{t("Cancel")}
</Button>
<Button
type="submit"
variant="contained"
color="primary"
>
{t("Assign & Release")}
</Button>
</Box>
</Grid>
</Grid>
</form>
</FormProvider>
</Grid>
</Grid>
</Box>
</Modal>
) : undefined}
</>
);
};

export default AssignAndRelease;

+ 54
- 37
src/components/PickOrderSearch/PickOrderSearch.tsx Переглянути файл

@@ -21,6 +21,8 @@ import ConsolidatedPickOrders from "./ConsolidatedPickOrders";
import PickExecution from "./PickExecution";
import CreatePickOrderModal from "./CreatePickOrderModal";
import NewCreateItem from "./newcreatitem";
import AssignAndRelease from "./AssignAndRelease";
import AssignTo from "./assignTo";
import { fetchAllItemsInClient, ItemCombo } from "@/app/api/settings/item/actions";
import { fetchPickOrderClient } from "@/app/api/pickOrder/actions";

@@ -116,42 +118,22 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
"label",
),
},
{
label: tabIndex === 3 ? t("Item Name") : t("Items"),
paramName: "items",
type: tabIndex === 3 ? "text" : "autocomplete",
options: tabIndex === 3
? []
:
uniqBy(
flatten(
sortBy(
pickOrders.map((po) =>
po.items
? po.items.map((item) => ({
value: item.name,
label: item.name,
}))
: [],
),
"label",
),
),
"value",
),
},
];

// Add Job Order search for Create Item tab (tabIndex === 3)
if (tabIndex === 3) {
baseCriteria.splice(1, 0, {
label: t("Job Order"),
paramName: "jobOrderCode" as any, // Type assertion for now
type: "text",
});
baseCriteria.splice(2, 0, {
label: t("Target Date"),
paramName: "targetDate",
type: "date",
});
} else {
baseCriteria.splice(1, 0, {
label: t("Target Date From"),
label2: t("Target Date To"),
@@ -159,9 +141,36 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
type: "dateRange",
});
}

// Add Items/Item Name criteria
baseCriteria.push({
label: tabIndex === 3 ? t("Item Name") : t("Items"),
paramName: "items",
type: tabIndex === 3 ? "text" : "autocomplete",
options: tabIndex === 3
? []
:
uniqBy(
flatten(
sortBy(
pickOrders.map((po) =>
po.items
? po.items.map((item) => ({
value: item.name,
label: item.name,
}))
: [],
),
"label",
),
),
"value",
),
});
// Add Status criteria for non-Create Item tabs
if (tabIndex !== 3) {
baseCriteria.splice(4, 0, {
baseCriteria.push({
label: t("Status"),
paramName: "status",
type: "autocomplete",
@@ -177,7 +186,7 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
),
});
}
return baseCriteria;
},
[pickOrders, t, tabIndex, items],
@@ -241,6 +250,7 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
</Grid>
</Grid>
</Stack>
{/*
<SearchBox
criteria={searchCriteria}
onSearch={(query) => {
@@ -298,22 +308,29 @@ const PickOrderSearch: React.FC<Props> = ({ pickOrders }) => {
}
}}
/>
*/}
<Tabs value={tabIndex} onChange={handleTabChange} variant="scrollable">

<Tab label={t("Select Items")} iconPosition="end" />
<Tab label={t("Assign")} iconPosition="end" />
<Tab label={t("Release")} iconPosition="end" />
<Tab label={t("Pick Execution")} iconPosition="end" />
<Tab label={t("Pick Orders")} iconPosition="end" />
<Tab label={t("Consolidated Pick Orders")} iconPosition="end" />
<Tab label={t("Pick Execution")} iconPosition="end" />
<Tab label={t("Create Item")} iconPosition="end" />
</Tabs>
{tabIndex === 0 && (
{tabIndex === 4 && (
<PickOrders
filteredPickOrders={filteredPickOrders}
filterArgs={filterArgs}
/>
)}
{tabIndex === 1 && <ConsolidatedPickOrders filterArgs={filterArgs} />}
{tabIndex === 2 && <PickExecution filterArgs={filterArgs} />}
{tabIndex === 3 && <NewCreateItem filterArgs={filterArgs} searchQuery={searchQuery} />}
{tabIndex === 5 && <ConsolidatedPickOrders filterArgs={filterArgs} />}
{tabIndex === 3 && <PickExecution filterArgs={filterArgs} />}
{tabIndex === 0 && <NewCreateItem filterArgs={filterArgs} searchQuery={searchQuery} />}
{tabIndex === 1 && <AssignAndRelease filterArgs={filterArgs} />}
{tabIndex === 2 && <AssignTo filterArgs={filterArgs} />}
</>
);
};


+ 233
- 0
src/components/PickOrderSearch/assignTo.tsx Переглянути файл

@@ -0,0 +1,233 @@
"use client";
import {
Autocomplete,
Box,
Button,
CircularProgress,
Grid,
Stack,
TextField,
Typography,
} from "@mui/material";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import SearchResults, { Column } from "../SearchResults/SearchResults";
import { fetchConsoPickOrderClient } from "@/app/api/pickOrder/actions";
import { fetchNameList, NameList } from "@/app/api/user/actions";
import { isEmpty, upperFirst } from "lodash";
import { arrayToDateString } from "@/app/utils/formatUtil";

interface Props {
filterArgs: Record<string, any>;
}

interface AssignmentData {
id: string;
consoCode: string;
releasedDate: string | null;
status: string;
assignTo: number | null;
assignedUserName?: string;
}

const AssignTo: React.FC<Props> = ({ filterArgs }) => {
const { t } = useTranslation("pickOrder");
// State
const [assignmentData, setAssignmentData] = useState<AssignmentData[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [pagingController, setPagingController] = useState({
pageNum: 1,
pageSize: 50,
});
const [totalCount, setTotalCount] = useState<number>();
const [usernameList, setUsernameList] = useState<NameList[]>([]);
const [selectedUser, setSelectedUser] = useState<NameList | null>(null);

// Fetch assignment data
const fetchAssignmentData = useCallback(async () => {
setIsLoading(true);
try {
const params = {
...pagingController,
...filterArgs,
// Add user filter if selected
...(selectedUser && { assignTo: selectedUser.id }),
};
console.log("Fetching with params:", params);
const res = await fetchConsoPickOrderClient(params);
if (res) {
console.log("API response:", res);
// Enhance data with user names and add id
const enhancedData = res.records.map((record: any, index: number) => {
const userName = record.assignTo
? usernameList.find(user => user.id === record.assignTo)?.name
: null;
return {
...record,
id: record.consoCode || `temp-${index}`,
assignedUserName: userName || 'Unassigned',
};
});
setAssignmentData(enhancedData);
setTotalCount(res.total);
}
} catch (error) {
console.error("Error fetching assignment data:", error);
} finally {
setIsLoading(false);
}
}, [pagingController, filterArgs, selectedUser, usernameList]);

// Load username list
useEffect(() => {
const loadUsernameList = async () => {
try {
const res = await fetchNameList();
if (res) {
console.log("Loaded username list:", res);
setUsernameList(res);
}
} catch (error) {
console.error("Error loading username list:", error);
}
};
loadUsernameList();
}, []);

// Fetch data when dependencies change
useEffect(() => {
fetchAssignmentData();
}, [fetchAssignmentData]);

// Handle user selection
const handleUserChange = useCallback((event: any, newValue: NameList | null) => {
setSelectedUser(newValue);
// Reset to first page when filtering
setPagingController(prev => ({ ...prev, pageNum: 1 }));
}, []);

// Clear filter
const handleClearFilter = useCallback(() => {
setSelectedUser(null);
setPagingController(prev => ({ ...prev, pageNum: 1 }));
}, []);

// Columns definition
const columns = useMemo<Column<AssignmentData>[]>(
() => [
{
name: "consoCode",
label: t("Consolidated Code"),
},
{
name: "assignedUserName",
label: t("Assigned To"),
renderCell: (params) => {
if (!params.assignTo) {
return (
<Typography variant="body2" color="text.secondary">
{t("Unassigned")}
</Typography>
);
}
return (
<Typography variant="body2" color="primary">
{params.assignedUserName}
</Typography>
);
},
},
{
name: "status",
label: t("Status"),
renderCell: (params) => {
return upperFirst(params.status);
},
},
{
name: "releasedDate",
label: t("Released Date"),
renderCell: (params) => {
if (!params.releasedDate) {
return (
<Typography variant="body2" color="text.secondary">
{t("Not Released")}
</Typography>
);
}
return arrayToDateString(params.releasedDate);
},
},
],
[t],
);

return (
<Stack spacing={2}>
{/* Filter Section */}
<Grid container spacing={2}>
<Grid item xs={4}>
<Autocomplete
options={usernameList}
getOptionLabel={(option) => option.name}
value={selectedUser}
onChange={handleUserChange}
renderInput={(params) => (
<TextField
{...params}
label={t("Select User to Filter")}
variant="outlined"
fullWidth
/>
)}
renderOption={(props, option) => (
<Box component="li" {...props}>
<Typography variant="body2">
{option.name} (ID: {option.id})
</Typography>
</Box>
)}
/>
</Grid>
<Grid item xs={2}>
<Button
variant="outlined"
onClick={handleClearFilter}
disabled={!selectedUser}
>
{t("Clear Filter")}
</Button>
</Grid>
</Grid>

{/* Data Table - Match PickExecution exactly */}
<Grid container spacing={2} sx={{ height: '100%', flex: 1 }}>
<Grid item xs={12} sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
{isLoading ? (
<Box display="flex" justifyContent="center" alignItems="center" flex={1}>
<CircularProgress size={40} />
</Box>
) : (
<SearchResults<AssignmentData>
items={assignmentData}
columns={columns}
pagingController={pagingController}
setPagingController={setPagingController}
totalCount={totalCount}
/>
)}
</Grid>
</Grid>
</Stack>
);
};

export default AssignTo;



+ 709
- 94
src/components/PickOrderSearch/newcreatitem.tsx
Різницю між файлами не показано, бо вона завелика
Переглянути файл


+ 26
- 3
src/i18n/zh/pickOrder.json Переглянути файл

@@ -1,6 +1,8 @@
{
"Purchase Order": "採購訂單",
"Code": "編號",
"Pick Order Code": "提料單編號",
"Item Code": "貨品編號",
"OrderDate": "下單日期",
"Details": "詳情",
"Supplier": "供應商",
@@ -23,6 +25,7 @@
"itemNo": "貨品編號",
"itemName": "貨品名稱",
"qty": "訂單數量",
"Require Qty": "需求數量",
"uom": "計量單位",
"total weight": "總重量",
"weight unit": "重量單位",
@@ -91,6 +94,7 @@

"Pick Order": "提料單",
"Type": "類型",
"Product Type": "貨品類型",
"Reset": "重置",
"Search": "搜尋",
"Pick Orders": "提料單",
@@ -102,7 +106,7 @@
"Consolidated Code": "合併編號",
"type": "類型",
"Items": "項目",
"Target Date": "目標日期",
"Target Date": "需求日期",
"Released By": "發佈者",
"Target Date From": "目標日期從",
"Target Date To": "目標日期到",
@@ -111,7 +115,8 @@
"create": "新增",
"detail": "詳情",
"Pick Order Detail": "提料單詳情",
"item": "項目",
"item": "貨品",
"Unit": "單位",
"reset": "重置",
"targetDate": "目標日期",
"remove": "移除",
@@ -120,7 +125,25 @@
"suggestedLotNo": "建議批次",
"lotNo": "批次",
"item name": "貨品名稱",
"Item Name": "貨品名稱",
"approval": "審核",
"lot change": "批次變更",
"checkout": "出庫"
"checkout": "出庫",
"Search Items": "搜尋貨品",
"Search Results": "搜尋結果",
"Second Search Results": "第二搜尋結果",
"Second Search Items": "第二搜尋項目",
"Second Search": "第二搜尋",
"Item": "貨品",
"Order Quantity": "要求數量",
"Current Stock": "現時庫存",
"Selected": "已選擇",
"Select Items": "選擇貨品",
"Assign": "分派提料單",
"Release": "放單",
"Pick Execution": "進行提料",
"Create Pick Order": "建立貨品提料單",
"consumable": "食材",
"Material": "材料",
"Job Order": "工單"
}

Завантаження…
Відмінити
Зберегти