Quellcode durchsuchen

Merge branch 'master' of https://git.2fi-solutions.com/derek/FPSMS-frontend

# Conflicts:
#	src/app/api/pickOrder/index.ts
#	src/components/PickOrderSearch/PickOrderSearch.tsx
master
CANCERYS\kw093 vor 2 Monaten
Ursprung
Commit
db543b5465
25 geänderte Dateien mit 1555 neuen und 183 gelöschten Zeilen
  1. +1
    -1
      src/app/(main)/layout.tsx
  2. +30
    -0
      src/app/(main)/pickOrder/detail/page.tsx
  3. +1
    -1
      src/app/(main)/pickOrder/page.tsx
  4. +23
    -0
      src/app/api/inventory/actions.ts
  5. +101
    -0
      src/app/api/pickOrder/actions.ts
  6. +68
    -7
      src/app/api/pickOrder/index.ts
  7. +7
    -5
      src/app/api/qrcode/index.ts
  8. +11
    -0
      src/app/api/user/actions.ts
  9. +5
    -0
      src/app/utils/fetchUtil.ts
  10. +7
    -0
      src/app/utils/formatUtil.ts
  11. +1
    -0
      src/components/Breadcrumb/Breadcrumb.tsx
  12. +1
    -1
      src/components/NavigationContent/NavigationContent.tsx
  13. +314
    -0
      src/components/PickOrderDetail/PickOrderDetail.tsx
  14. +40
    -0
      src/components/PickOrderDetail/PickOrderDetailLoading.tsx
  15. +35
    -0
      src/components/PickOrderDetail/PickOrderDetailWrapper.tsx
  16. +1
    -0
      src/components/PickOrderDetail/index.ts
  17. +91
    -0
      src/components/PickOrderSearch/ConsolidatePickOrderItemSum.tsx
  18. +115
    -0
      src/components/PickOrderSearch/ConsolidatePickOrderSum.tsx
  19. +365
    -5
      src/components/PickOrderSearch/ConsolidatedPickOrders.tsx
  20. +151
    -77
      src/components/PickOrderSearch/PickOrderSearch.tsx
  21. +9
    -2
      src/components/PickOrderSearch/PickOrderSearchWrapper.tsx
  22. +138
    -81
      src/components/PickOrderSearch/PickOrders.tsx
  23. +1
    -1
      src/components/PoSearch/PoSearch.tsx
  24. +19
    -2
      src/components/SearchResults/SearchResults.tsx
  25. +20
    -0
      src/i18n/zh/common.json

+ 1
- 1
src/app/(main)/layout.tsx Datei anzeigen

@@ -55,8 +55,8 @@ export default async function MainLayout({
<Stack spacing={2}>
<I18nProvider namespaces={["common"]}>
<Breadcrumb />
{children}
</I18nProvider>
{children}
</Stack>
</Box>
</>


+ 30
- 0
src/app/(main)/pickOrder/detail/page.tsx Datei anzeigen

@@ -0,0 +1,30 @@
import { PreloadPickOrder } from "@/app/api/pickorder";
import { SearchParams } from "@/app/utils/fetchUtil";
import PickOrderDetail from "@/components/PickOrderDetail";
import { getServerI18n, I18nProvider } from "@/i18n";
import { Stack, Typography } from "@mui/material";
import { Metadata } from "next";
import { Suspense } from "react";

export const metadata: Metadata = {
title: "Consolidated Pick Order Flow",
};
type Props = {} & SearchParams;

const PickOrder: React.FC<Props> = async ({ searchParams }) => {
const { t } = await getServerI18n("pickOrder");

PreloadPickOrder();

return (
<>
<I18nProvider namespaces={["pickOrder"]}>
<Suspense fallback={<PickOrderDetail.Loading />}>
<PickOrderDetail consoCode={`${searchParams["consoCode"]}`}/>
</Suspense>
</I18nProvider>
</>
);
};

export default PickOrder;

+ 1
- 1
src/app/(main)/pickOrder/page.tsx Datei anzeigen

@@ -1,4 +1,4 @@
import { PreloadPickOrder } from "@/app/api/pickOrder";
import { PreloadPickOrder } from "@/app/api/pickorder";
import PickOrderSearch from "@/components/PickOrderSearch";
import { getServerI18n } from "@/i18n";
import { I18nProvider } from "@/i18n";


+ 23
- 0
src/app/api/inventory/actions.ts Datei anzeigen

@@ -0,0 +1,23 @@
"use server";
import { BASE_API_URL } from "@/config/api";
// import { ServerFetchError, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil";
import { revalidateTag } from "next/cache";
import { cache } from "react";
import { serverFetchJson } from "@/app/utils/fetchUtil";
import { QcItemResult } from "../settings/qcItem";
import { RecordsRes } from "../utils";
// import { BASE_API_URL } from "@/config/api";

export interface LotLineInfo {
inventoryLotLineId: number,
lotNo: string,
remainingQty: number,
uom: string
}

export const fetchLotDetail = cache(async (stockInLineId: number) => {
return serverFetchJson<LotLineInfo>(`${BASE_API_URL}/inventoryLotLine/lot-detail/${stockInLineId}`, {
method: 'GET',
next: { tags: ["inventory"] },
});
});

+ 101
- 0
src/app/api/pickOrder/actions.ts Datei anzeigen

@@ -0,0 +1,101 @@
"use server";
import { BASE_API_URL } from "@/config/api";
// import { ServerFetchError, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil";
import { revalidateTag } from "next/cache";
import { cache } from "react";
import { serverFetchJson } from "@/app/utils/fetchUtil";
import { QcItemResult } from "../settings/qcItem";
import { RecordsRes } from "../utils";
import { ConsoPickOrderResult, PickOrderLineWithSuggestedLot, PickOrderResult, PreReleasePickOrderSummary } from ".";
// import { BASE_API_URL } from "@/config/api";

export interface ReleasePickOrderInputs {
consoCode: string
assignTo: number,
}

export const consolidatePickOrder = async (ids: number[]) => {
const pickOrder = await serverFetchJson<any>(`${BASE_API_URL}/pickOrder/conso`, {
method: "POST",
body: JSON.stringify({ ids: ids }),
headers: { "Content-Type": "application/json" },
});
// revalidateTag("po");
return pickOrder
}

export const consolidatePickOrder_revert = async (ids: number[]) => {
const pickOrder = await serverFetchJson<any>(`${BASE_API_URL}/pickOrder/deconso`, {
method: "POST",
body: JSON.stringify({ ids: ids }),
headers: { "Content-Type": "application/json" },
});
// revalidateTag("po");
return pickOrder
}


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

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

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

export const fetchConsoDetail = cache(async (consoCode: string) => {
return serverFetchJson<PreReleasePickOrderSummary>(`${BASE_API_URL}/pickOrder/pre-release-info/${consoCode}`, {
method: 'GET',
next: { tags: ["pickorder"] },
});
});

export const releasePickOrder = async (data: ReleasePickOrderInputs) => {
console.log(data)
console.log(JSON.stringify(data))
const po = await serverFetchJson<any>(`${BASE_API_URL}/pickOrder/releaseConso`, {
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
});
revalidateTag("pickorder");
return po
}

+ 68
- 7
src/app/api/pickOrder/index.ts Datei anzeigen

@@ -1,8 +1,6 @@
import "server-only";
// import { serverFetchJson } from "@/app/utils/fetchUtil";
// import { BASE_API_URL } from "@/config/api";
import { serverFetchJson } from "../../utils/fetchUtil";
import { BASE_API_URL } from "../../../config/api";
import { Pageable, serverFetchJson } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";
import { cache } from "react";

interface PickOrderItemInfo {
@@ -20,14 +18,77 @@ export interface PickOrderResult{
status: string,
releasedBy: string,
items?: PickOrderItemInfo[] | null,
pickOrderLine?: PickOrderLine[]
}

export interface PickOrderLine {
id: number,
itemId: number,
itemCode: string,
itemName: string,
availableQty: number,
requiredQty: number,
uomCode: string,
uomDesc: string
}
export interface ConsoPickOrderResult{
id: number,
code: string,
consoCode?: string,
targetDate: number[],
completeDate?: number[],
type: string,
status: string,
releasedBy: string,
items?: PickOrderItemInfo[] | null,
}

export interface FetchPickOrders extends Pageable {
code: string | undefined
targetDateFrom: string | undefined
targetDateTo: string | undefined
type: string | undefined
status: string | undefined
itemName: string | undefined
}
export type ByItemsSummary = {
id: number,
code: string,
name: string,
uomDesc: string,
availableQty: number,
requiredQty: number,
}
export interface PreReleasePickOrderSummary {
consoCode: string
pickOrders: Omit<PickOrderResult, "items">[]
items: ByItemsSummary[]
}

export interface PickOrderLineWithSuggestedLot {
id: number,
itemName: string,
qty: number,
uom: string
status: string
warehouse: string
suggestedLotNo: string
}

export const PreloadPickOrder = () => {
fetchPickOrders()
fetchPickOrders({
code: undefined,
targetDateFrom: undefined,
targetDateTo: undefined,
type: undefined,
status: undefined,
itemName: undefined,
})
}

export const fetchPickOrders = cache(async () => {
return serverFetchJson<PickOrderResult[]>(`${BASE_API_URL}/pickOrder/list`, {
export const fetchPickOrders = cache(async (queryParams: FetchPickOrders) => {
const queryString = new URLSearchParams(queryParams as Record<string, any>).toString();
return serverFetchJson<PickOrderResult[]>(`${BASE_API_URL}/pickOrder/list?${queryString}`, {
next: {
tags: ["pickOrders"]
}


+ 7
- 5
src/app/api/qrcode/index.ts Datei anzeigen

@@ -6,8 +6,10 @@ import { serverFetchJson } from "../../utils/fetchUtil";
import { BASE_API_URL } from "../../../config/api";

export interface QrCodeInfo {
stockInLineId?: number;
itemId: number
warehouseId?: number
lotNo?: string
}
// warehouse qrcode
warehouseId?: number
// item qrcode
stockInLineId?: number;
itemId: number
lotNo?: string
}

+ 11
- 0
src/app/api/user/actions.ts Datei anzeigen

@@ -22,12 +22,23 @@ export interface PasswordInputs {
newPasswordCheck: string;
}

export interface NameList {
id: number
name: string
}

export const fetchUserDetails = cache(async (id: number) => {
return serverFetchJson<UserDetail>(`${BASE_API_URL}/user/${id}`, {
next: { tags: ["user"] },
});
});

export const fetchNameList = cache(async () => {
return serverFetchJson<NameList[]>(`${BASE_API_URL}/user/name-list`, {
next: { tags: ["user"] },
});
});

export const editUser = async (id: number, data: UserInputs) => {
const newUser = serverFetchWithNoContent(`${BASE_API_URL}/user/${id}`, {
method: "PUT",


+ 5
- 0
src/app/utils/fetchUtil.ts Datei anzeigen

@@ -3,6 +3,11 @@ import { getServerSession } from "next-auth";
import { headers } from "next/headers";
import { redirect } from "next/navigation";

export interface Pageable {
pageSize?: number
pageNum?: number
}

export type SearchParams = {
searchParams: { [key: string]: string | string[] | undefined };
}


+ 7
- 0
src/app/utils/formatUtil.ts Datei anzeigen

@@ -67,6 +67,13 @@ export const stockInLineStatusMap: { [status: string]: number } = {
"rejected": 9,
};

export const pickOrderStatusMap: { [status: string]: number } = {
"pending": 1,
"consolidated": 2,
"released": 3,
"completed": 4,
};

export const calculateWeight = (qty: number, uom: Uom) => {
return qty * (uom.unit2Qty || 1) * (uom.unit3Qty || 1) * (uom.unit4Qty || 1);
}


+ 1
- 0
src/components/Breadcrumb/Breadcrumb.tsx Datei anzeigen

@@ -24,6 +24,7 @@ const pathToLabelMap: { [path: string]: string } = {
"/do": "Delivery Order",
"/pickOrder": "Pick Order",
"/po": "Purchase Order",
"/dashboard": "dashboard",
};

const Breadcrumb = () => {


+ 1
- 1
src/components/NavigationContent/NavigationContent.tsx Datei anzeigen

@@ -52,7 +52,7 @@ const NavigationContent: React.FC = () => {
{
icon: <RequestQuote />,
label: "Pick Order",
path: "/pickOrder",
path: "/pickorder",
},
// {
// icon: <RequestQuote />,


+ 314
- 0
src/components/PickOrderDetail/PickOrderDetail.tsx Datei anzeigen

@@ -0,0 +1,314 @@
"use client";

import {
Button,
ButtonProps,
Card,
CardContent,
CardHeader,
CircularProgress,
Grid,
Stack,
Typography,
} from "@mui/material";
import { useTranslation } from "react-i18next";
import StyledDataGrid from "../StyledDataGrid";
import { useCallback, useEffect, useMemo, useState } from "react";
import { GridColDef } from "@mui/x-data-grid";
import { PlayArrow } from "@mui/icons-material";
import DoneIcon from "@mui/icons-material/Done";
import { GridRowSelectionModel } from "@mui/x-data-grid";
import { useQcCodeScanner } from "../QrCodeScannerProvider/QrCodeScannerProvider";
import { fetchPickOrderLineClient } from "@/app/api/pickorder/actions";
import { PickOrderLineWithSuggestedLot } from "@/app/api/pickorder";
import { Pageable } from "@/app/utils/fetchUtil";
import { QrCodeInfo } from "@/app/api/qrcode";
import { QrCode } from "../QrCode";
import { fetchLotDetail, LotLineInfo } from "@/app/api/inventory/actions";
import { GridRowModesModel } from "@mui/x-data-grid";

interface Props {
consoCode: string;
}
interface IsLoadingModel {
pickOrderLineTable: boolean;
stockOutLineTable: boolean;
}

const PickOrderDetail: React.FC<Props> = ({ consoCode }) => {
const { t } = useTranslation("pickOrder");
const [selectedRow, setSelectRow] = useState<GridRowSelectionModel>();
const [isLoadingModel, setIsLoadingModel] = useState<IsLoadingModel>({
pickOrderLineTable: false,
stockOutLineTable: false,
});
const [polCriteriaArgs, setPolCriteriaArgs] = useState<Pageable>({
pageNum: 1,
pageSize: 10,
});
const [solCriteriaArgs, setSolCriteriaArgs] = useState<Pageable>({
pageNum: 1,
pageSize: 10,
});
const [polTotalCount, setPolTotalCount] = useState(0);
const [solTotalCount, setSolTotalCount] = useState(0);
const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({});

const [pickOrderLine, setPickOrderLine] = useState<
PickOrderLineWithSuggestedLot[]
>([]);

const pickOrderLineColumns = useMemo<GridColDef[]>(
() => [
{
field: "id",
headerName: "pickOrderLineId",
flex: 1,
},
{
field: "itemName",
headerName: "itemId",
flex: 1,
},
{
field: "qty",
headerName: "qty",
flex: 1,
},
{
field: "uom",
headerName: "uom",
flex: 1,
},
{
field: "warehouse",
headerName: "location",
flex: 1,
},
{
field: "suggestedLotNo",
headerName: "suggestedLotNo",
flex: 1.2,
},
],
[]
);
const [stockOutLine, setStockOutLine] = useState([]);
const stockOutLineColumns = useMemo<GridColDef[]>(
() => [
{
field: "code",
headerName: "actual lot (out line",
flex: 1,
},
],
[]
);

const handleStartPickOrder = useCallback(async () => {}, []);

const handleCompletePickOrder = useCallback(async () => {}, []);

useEffect(() => {
console.log(selectedRow);
}, [selectedRow]);

const buttonData = useMemo(
() => ({
buttonName: "complete",
title: t("Do you want to complete?"),
confirmButtonText: t("Complete"),
successTitle: t("Complete Success"),
errorTitle: t("Complete Fail"),
buttonText: t("Complete PO"),
buttonIcon: <DoneIcon />,
buttonColor: "info",
disabled: true,
}),
[]
);

const [isOpenScanner, setOpenScanner] = useState(false);
const onOpenScanner = useCallback(() => {
setOpenScanner((prev) => !prev);
}, []);

const fetchPickOrderLine = useCallback(
async (params: Record<string, any>) => {
setIsLoadingModel((prev) => ({
...prev,
pickOrderLineTable: true,
}));
const res = await fetchPickOrderLineClient({
...params,
consoCode: consoCode,
});
if (res) {
console.log(res);
setPickOrderLine(res.records);
setPolTotalCount(res.total);
} else {
console.log("error");
console.log(res);
}
setIsLoadingModel((prev) => ({
...prev,
pickOrderLineTable: false,
}));
},
[fetchPickOrderLineClient, consoCode]
);
const fetchStockOutLine = useCallback(
async (params: Record<string, any>) => {},
[]
);

useEffect(() => {
fetchPickOrderLine(polCriteriaArgs);
}, [polCriteriaArgs]);

useEffect(() => {
fetchStockOutLine(solCriteriaArgs);
}, [solCriteriaArgs]);

const getLotDetail = useCallback(
async (stockInLineId: number): Promise<LotLineInfo> => {
const res = await fetchLotDetail(stockInLineId);
return res;
},
[fetchLotDetail]
);

const scanner = useQcCodeScanner();
useEffect(() => {
if (isOpenScanner && !scanner.isScanning) {
scanner.startScan();
} else if (!isOpenScanner && scanner.isScanning) {
scanner.stopScan();
}
}, [isOpenScanner]);

useEffect(() => {
if (scanner.values.length > 0) {
console.log(scanner.values[0]);
const data: QrCodeInfo = JSON.parse(scanner.values[0]);
console.log(data);
if (data.stockInLineId) {
console.log("still got in");
console.log(data.stockInLineId);
// fetch
getLotDetail(data.stockInLineId).then((value) => {});
}
scanner.resetScan();
}
}, [scanner.values]);

const homemade_Qrcode = {
stockInLineId: 156,
};

return (
<>
<Stack spacing={2}>
<Grid container xs={12} justifyContent="start">
<Grid item xs={12}>
<Typography variant="h4" marginInlineEnd={2}>
{consoCode}
</Typography>
</Grid>
<Grid item xs={8}>
<Button
// onClick={buttonData.onClick}
disabled={buttonData.disabled}
color={buttonData.buttonColor as ButtonProps["color"]}
startIcon={buttonData.buttonIcon}
>
{buttonData.buttonText}
</Button>
</Grid>
<Grid
item
xs={4}
display="flex"
justifyContent="end"
alignItems="end"
>
<Button onClick={onOpenScanner}>
{isOpenScanner ? t("binding") : t("bind")}
</Button>
</Grid>
{/* homemade qrcode for testing purpose */}
{/* <Grid
item
xs={12}
style={{ display: "flex", justifyContent: "center" }}
>
<QrCode
content={homemade_Qrcode}
sx={{ width: 200, height: 200 }}
/>
</Grid> */}
</Grid>
<Grid container xs={12} justifyContent="space-between">
{/* <Grid item xs={12} sx={{ height: 400 }}>
<StyledDataGrid rows={pickOrderLine} columns={columns} />
</Grid> */}
<Grid item xs={12} sx={{ height: 400 }}>
{isLoadingModel.pickOrderLineTable ? (
<CircularProgress size={40} />
) : (
<StyledDataGrid
rows={pickOrderLine}
columns={pickOrderLineColumns}
rowSelectionModel={selectedRow}
onRowSelectionModelChange={(newRowSelectionModel) => {
setSelectRow(newRowSelectionModel);
}}
initialState={{
pagination: {
paginationModel: { pageSize: 10, page: 0 },
},
}}
pageSizeOptions={[10, 25, 50, 100]}
onPaginationModelChange={async (model, details) => {
setPolCriteriaArgs({
pageNum: model.page + 1,
pageSize: model.pageSize,
});
}}
rowCount={polTotalCount}
/>
)}
</Grid>
<Grid item xs={12} sx={{ height: 400 }}>
<StyledDataGrid
rows={stockOutLine}
columns={stockOutLineColumns}
rowModesModel={rowModesModel}
onRowModesModelChange={setRowModesModel}
disableColumnMenu
editMode="row"
// processRowUpdate={processRowUpdate}
// onProcessRowUpdateError={onProcessRowUpdateError}

initialState={{
pagination: {
paginationModel: { pageSize: 10, page: 0 },
},
}}
pageSizeOptions={[10, 25, 50, 100]}
onPaginationModelChange={async (model, details) => {
setSolCriteriaArgs({
pageNum: model.page + 1,
pageSize: model.pageSize,
});
}}
rowCount={solTotalCount}
/>
</Grid>
</Grid>
</Stack>
</>
);
};
export default PickOrderDetail;

+ 40
- 0
src/components/PickOrderDetail/PickOrderDetailLoading.tsx Datei anzeigen

@@ -0,0 +1,40 @@
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import Skeleton from "@mui/material/Skeleton";
import Stack from "@mui/material/Stack";
import React from "react";

// Can make this nicer
export const PickOrderDetailLoading: React.FC = () => {
return (
<>
<Card>
<CardContent>
<Stack spacing={2}>
<Skeleton variant="rounded" height={60} />
<Skeleton variant="rounded" height={60} />
<Skeleton variant="rounded" height={60} />
<Skeleton
variant="rounded"
height={50}
width={100}
sx={{ alignSelf: "flex-end" }}
/>
</Stack>
</CardContent>
</Card>
<Card>
<CardContent>
<Stack spacing={2}>
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
<Skeleton variant="rounded" height={40} />
</Stack>
</CardContent>
</Card>
</>
);
};

export default PickOrderDetailLoading;

+ 35
- 0
src/components/PickOrderDetail/PickOrderDetailWrapper.tsx Datei anzeigen

@@ -0,0 +1,35 @@
import { fetchAllItems } from "@/app/api/settings/item";
// import ItemsSearch from "./ItemsSearch";
// import ItemsSearchLoading from "./ItemsSearchLoading";
import { SearchParams } from "@/app/utils/fetchUtil";
import { TypeEnum } from "@/app/utils/typeEnum";
import { notFound } from "next/navigation";
import { fetchPoWithStockInLines, PoResult } from "@/app/api/po";
import { QcItemWithChecks } from "@/app/api/qc";
import { fetchWarehouseList } from "@/app/api/warehouse";
import { fetchQcItemCheck } from "@/app/api/qc/actions";
import PickOrderDetail from "./PickOrderDetail";
import PickOrderDetailLoading from "./PickOrderDetailLoading";

interface SubComponents {
Loading: typeof PickOrderDetailLoading;
}

type Props = {
consoCode: string;
};

const PoDetailWrapper: React.FC<Props> & SubComponents = async ({ consoCode }) => {
// const [poWithStockInLine, warehouse, qc] = await Promise.all([
// fetchPoWithStockInLines(id),
// fetchWarehouseList(),
// fetchQcItemCheck(),
// ]);
// const poWithStockInLine = await fetchPoWithStockInLines(id)

return <PickOrderDetail consoCode={consoCode}/>;
};

PoDetailWrapper.Loading = PickOrderDetailLoading;

export default PoDetailWrapper;

+ 1
- 0
src/components/PickOrderDetail/index.ts Datei anzeigen

@@ -0,0 +1 @@
export { default } from "./PickOrderDetailWrapper"

+ 91
- 0
src/components/PickOrderSearch/ConsolidatePickOrderItemSum.tsx Datei anzeigen

@@ -0,0 +1,91 @@
"use client";
import dayjs from "dayjs";
import arraySupport from "dayjs/plugin/arraySupport";
import StyledDataGrid from "../StyledDataGrid";
import {
Dispatch,
SetStateAction,
useCallback,
useEffect,
useMemo,
useState,
} from "react";
import { GridColDef } from "@mui/x-data-grid";
import { CircularProgress, Grid, Typography } from "@mui/material";
import { ByItemsSummary } from "@/app/api/pickorder";
import { useTranslation } from "react-i18next";

dayjs.extend(arraySupport);

interface Props {
rows: ByItemsSummary[] | undefined;
setRows: Dispatch<SetStateAction<ByItemsSummary[] | undefined>>;
}

const ConsolidatePickOrderItemSum: React.FC<Props> = ({ rows, setRows }) => {
console.log(rows);
const { t } = useTranslation("pickOrder");

const columns = useMemo<GridColDef[]>(
() => [
{
field: "name",
headerName: "name",
flex: 1,
renderCell: (params) => {
console.log(params.row.name);
return params.row.name;
},
},
{
field: "requiredQty",
headerName: "requiredQty",
flex: 1,
renderCell: (params) => {
console.log(params.row.requiredQty);
const requiredQty = params.row.requiredQty ?? 0;
return `${requiredQty} ${params.row.uomDesc}`;
},
},
{
field: "availableQty",
headerName: "availableQty",
flex: 1,
renderCell: (params) => {
console.log(params.row.availableQty);
const availableQty = params.row.availableQty ?? 0;
return `${availableQty} ${params.row.uomDesc}`;
},
},
],
[]
);
return (
<Grid
container
rowGap={1}
// direction="column"
alignItems="center"
justifyContent="center"
>
<Grid item xs={12}>
<Typography variant="h5" marginInlineEnd={2}>
{t("Items Included")}
</Typography>
</Grid>
<Grid item xs={12}>
{!rows ? (
<CircularProgress size={40} />
) : (
<StyledDataGrid
sx={{ maxHeight: 450 }}
rows={rows}
columns={columns}
/>
)}
</Grid>
</Grid>
);
};

export default ConsolidatePickOrderItemSum;

+ 115
- 0
src/components/PickOrderSearch/ConsolidatePickOrderSum.tsx Datei anzeigen

@@ -0,0 +1,115 @@
"use client";
import dayjs from "dayjs";
import arraySupport from "dayjs/plugin/arraySupport";
import StyledDataGrid from "../StyledDataGrid";
import {
Dispatch,
SetStateAction,
useCallback,
useEffect,
useMemo,
useState,
} from "react";
import { GridColDef, GridInputRowSelectionModel } from "@mui/x-data-grid";
import { Box, CircularProgress, Grid, Typography } from "@mui/material";
import { PickOrderResult } from "@/app/api/pickorder";
import { useTranslation } from "react-i18next";

dayjs.extend(arraySupport);

interface Props {
consoCode: string;
rows: Omit<PickOrderResult, "items">[] | undefined;
setRows: Dispatch<
SetStateAction<Omit<PickOrderResult, "items">[] | undefined>
>;
revertIds: GridInputRowSelectionModel;
setRevertIds: Dispatch<SetStateAction<GridInputRowSelectionModel>>;
}

const ConsolidatePickOrderSum: React.FC<Props> = ({
consoCode,
rows,
setRows,
revertIds,
setRevertIds,
}) => {
const { t } = useTranslation("pickOrder");
const columns = useMemo<GridColDef[]>(
() => [
{
field: "code",
headerName: "code",
flex: 0.6,
},

{
field: "pickOrderLines",
headerName: "items",
flex: 1,
renderCell: (params) => {
console.log(params);
const pickOrderLine = params.row.pickOrderLines as any[];
return (
<Box
sx={{
display: "flex",
flexDirection: "column",
maxHeight: 100,
overflowY: "scroll",
scrollbarWidth: "none", // For Firefox
"&::-webkit-scrollbar": {
display: "none", // For Chrome, Safari, and Opera
},
}}
>
{pickOrderLine.map((item, index) => (
<Grid sx={{mt:1}}
key={index}
>{`${item.itemName} x ${item.requiredQty} ${item.uomDesc}`}</Grid> // Render each name in a span
))}
</Box>
);
},
},
],
[]
);

return (
<Grid
container
rowGap={1}
// direction="column"
alignItems="center"
justifyContent="center"
>
<Grid item xs={12}>
<Typography variant="h5" marginInlineEnd={2}>
{t("Pick Order Included")}
</Typography>
</Grid>
<Grid item xs={12}>
{!rows ? (
<CircularProgress size={40} />
) : (
<StyledDataGrid
sx={{ maxHeight: 450 }}
checkboxSelection
rowSelectionModel={revertIds}
onRowSelectionModelChange={(newRowSelectionModel) => {
setRevertIds(newRowSelectionModel);
}}
getRowHeight={(params) => {
return 100
}}
rows={rows}
columns={columns}
/>
)}
</Grid>
</Grid>
);
};

export default ConsolidatePickOrderSum;

+ 365
- 5
src/components/PickOrderSearch/ConsolidatedPickOrders.tsx Datei anzeigen

@@ -1,12 +1,372 @@
import {
Autocomplete,
Box,
Button,
CircularProgress,
FormControl,
Grid,
Modal,
ModalProps,
TextField,
Typography,
} from "@mui/material";
import { GridToolbarContainer } from "@mui/x-data-grid";
import {
FooterPropsOverrides,
GridColDef,
GridRowSelectionModel,
useGridApiRef,
} from "@mui/x-data-grid";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import StyledDataGrid from "../StyledDataGrid";
import SearchResults, {
Column,
defaultPagingController,
} from "../SearchResults/SearchResults";
import {
ByItemsSummary,
ConsoPickOrderResult,
PickOrderLine,
PickOrderResult,
} from "@/app/api/pickorder";
import { useRouter, useSearchParams } from "next/navigation";
import ConsolidatePickOrderItemSum from "./ConsolidatePickOrderItemSum";
import ConsolidatePickOrderSum from "./ConsolidatePickOrderSum";
import { GridInputRowSelectionModel } from "@mui/x-data-grid";
import {
fetchConsoDetail,
fetchConsoPickOrderClient,
releasePickOrder,
ReleasePickOrderInputs,
} from "@/app/api/pickorder/actions";
import { EditNote } from "@mui/icons-material";
import { fetchNameList, NameList } from "@/app/api/user/actions";
import { useField } from "@mui/x-date-pickers/internals";
import {
FormProvider,
SubmitErrorHandler,
SubmitHandler,
useForm,
} from "react-hook-form";
import { pickOrderStatusMap } from "@/app/utils/formatUtil";

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: 1500,
};
interface DisableButton {
releaseBtn: boolean;
removeBtn: boolean;
}

const ConsolidatedPickOrders: React.FC<Props> = ({
const ConsolidatedPickOrders: React.FC<Props> = ({ filterArgs }) => {
const { t } = useTranslation("pickOrder");
const router = useRouter();
const apiRef = useGridApiRef();
const [filteredPickOrders, setFilteredPickOrders] = useState(
[] as ConsoPickOrderResult[]
);
const [isLoading, setIsLoading] = useState(false);
const [modalOpen, setModalOpen] = useState(false); //change back to false
const [consoCode, setConsoCode] = useState<string | undefined>(); ///change back to undefined
const [revertIds, setRevertIds] = useState<GridInputRowSelectionModel>([]);
const [totalCount, setTotalCount] = useState<number>();
const [usernameList, setUsernameList] = useState<NameList[]>([]);

}) => {
return <></>
}
const [byPickOrderRows, setByPickOrderRows] = useState<
Omit<PickOrderResult, "items">[] | undefined
>(undefined);
const [byItemsRows, setByItemsRows] = useState<ByItemsSummary[] | undefined>(
undefined
);
const [disableRelease, setDisableRelease] = useState<boolean>(true);

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

const openDetailModal = useCallback((consoCode: string) => {
setConsoCode(consoCode);
setModalOpen(true);
}, []);

const closeDetailModal = useCallback(() => {
setModalOpen(false);
setConsoCode(undefined);
}, []);

const onDetailClick = useCallback(
(pickOrder: any) => {
console.log(pickOrder);
const status = pickOrder.status
if (pickOrderStatusMap[status] >= 2) {
router.push(`/pickorder/detail?consoCode=${pickOrder.consoCode}`);
} else {
openDetailModal(pickOrder.consoCode);
}
},
[router, openDetailModal]
);
const columns = useMemo<Column<ConsoPickOrderResult>[]>(
() => [
{
name: "id",
label: t("Detail"),
onClick: onDetailClick,
buttonIcon: <EditNote />,
},
{
name: "consoCode",
label: t("consoCode"),
},
{
name: "status",
label: t("status"),
},
],
[]
);
const [pagingController, setPagingController] = useState(
defaultPagingController
);

// pass conso code back to assign
// pass user back to assign
const fetchNewPageConsoPickOrder = useCallback(
async (
pagingController: Record<string, number>,
filterArgs: Record<string, number>
) => {
setIsLoading(true);
const params = {
...pagingController,
...filterArgs,
};
const res = await fetchConsoPickOrderClient(params);
if (res) {
console.log(res);
setFilteredPickOrders(res.records);
setTotalCount(res.total);
}
setIsLoading(false);
},
[]
);

useEffect(() => {
fetchNewPageConsoPickOrder(pagingController, filterArgs);
}, [fetchNewPageConsoPickOrder, pagingController, filterArgs]);

const isReleasable = useCallback((itemList: ByItemsSummary[]): boolean => {
var isReleasable = true;
for (const item of itemList) {
isReleasable = item.requiredQty >= item.availableQty;
if (!isReleasable) return isReleasable;
}
return isReleasable;
}, []);

const fetchConso = useCallback(
async (consoCode: string) => {
const res = await fetchConsoDetail(consoCode);
const nameListRes = await fetchNameList();
if (res) {
console.log(res);
setByPickOrderRows(res.pickOrders);
// for testing
// for (const item of res.items) {
// item.availableQty = 1000;
// }
setByItemsRows(res.items);
setDisableRelease(isReleasable(res.items));
} else {
console.log("error");
console.log(res);
}
if (nameListRes) {
console.log(nameListRes);
setUsernameList(nameListRes);
}
},
[isReleasable]
);

const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>(
(...args) => {
closeDetailModal();
// reset();
},
[closeDetailModal]
);


const onChange = useCallback(
(
event: React.SyntheticEvent,
newValue: NameList
) => {
console.log(newValue);
formProps.setValue("assignTo", newValue.id);
},
[]
);

const onSubmit = useCallback<SubmitHandler<ReleasePickOrderInputs & {}>>(
async (data, event) => {
console.log(data);
try {
const res = await releasePickOrder(data)
console.log(res)
if (res.status = 200) {
router.push(`/pickorder/detail?consoCode=${data.consoCode}`);
} else {
throw Error("hv error")
}
} catch (error) {
console.log(error)
}
},
[releasePickOrder]
);
const onSubmitError = useCallback<SubmitErrorHandler<ReleasePickOrderInputs>>(
(errors) => {},
[]
);

const handleConsolidate_revert = useCallback(() => {
console.log(revertIds);
}, [revertIds]);

useEffect(() => {
if (consoCode) {
fetchConso(consoCode);
formProps.setValue("consoCode", consoCode)
}
}, [consoCode]);

return (
<>
<Grid
container
rowGap={1}
// direction="column"
alignItems="center"
justifyContent="center"
>
<Grid item xs={12}>
{isLoading ? (
<CircularProgress size={40} />
) : (
<SearchResults<ConsoPickOrderResult>
items={filteredPickOrders}
columns={columns}
pagingController={pagingController}
setPagingController={setPagingController}
totalCount={totalCount}
/>
)}
</Grid>
</Grid>
{consoCode != undefined ? (
<Modal open={modalOpen} onClose={closeHandler}>
<FormProvider {...formProps}>
<Box
sx={{ ...style, maxHeight: 800 }}
component="form"
onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)}
>
<Grid container>
<Grid item xs={8}>
<Typography mb={2} variant="h4">
{consoCode}
</Typography>
</Grid>
<Grid
item
xs={4}
display="flex"
justifyContent="end"
alignItems="end"
>
<FormControl fullWidth>
<Autocomplete
disableClearable
fullWidth
getOptionLabel={(option) => option.name}
options={usernameList}
onChange={onChange}
renderInput={(params) => <TextField {...params} />}
/>
</FormControl>
</Grid>
</Grid>
<Box
sx={{
height: 400,
overflowY: "auto",
}}
>
<Grid container>
<Grid item xs={12} sx={{ mt: 2 }}>
<ConsolidatePickOrderSum
rows={byPickOrderRows}
setRows={setByPickOrderRows}
consoCode={consoCode}
revertIds={revertIds}
setRevertIds={setRevertIds}
/>
</Grid>
<Grid item xs={12}>
<ConsolidatePickOrderItemSum
rows={byItemsRows}
setRows={setByItemsRows}
/>
</Grid>
</Grid>
</Box>
<Grid container>
<Grid
item
xs={12}
display="flex"
justifyContent="end"
alignItems="end"
>
<Button
disabled={(revertIds as number[]).length < 1}
variant="outlined"
onClick={handleConsolidate_revert}
sx={{ mr: 1 }}
>
{t("remove")}
</Button>
<Button
disabled={disableRelease}
variant="outlined"
// onClick={handleRelease}
type="submit"
>
{t("release")}
</Button>
</Grid>
</Grid>
</Box>
</FormProvider>
</Modal>
) : undefined}
</>
);
};

export default ConsolidatedPickOrders;
export default ConsolidatedPickOrders;

+ 151
- 77
src/components/PickOrderSearch/PickOrderSearch.tsx Datei anzeigen

@@ -1,24 +1,40 @@
"use client"
import { PickOrderResult } from "@/app/api/pickOrder";
"use client";
import { PickOrderResult } from "@/app/api/pickorder";
import { SearchParams } from "@/app/utils/fetchUtil";
import { useCallback, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import SearchBox, { Criterion } from "../SearchBox";
import SearchResults, { Column } from "../SearchResults";
import { flatten, groupBy, intersectionWith, isEmpty, map, sortBy, sortedUniq, uniqBy, upperCase, upperFirst } from "lodash";
import { arrayToDateString, arrayToDayjs, dateStringToDayjs } from "@/app/utils/formatUtil";
import {
flatten,
groupBy,
intersectionWith,
isEmpty,
map,
sortBy,
sortedUniq,
uniqBy,
upperCase,
upperFirst,
} from "lodash";
import {
arrayToDateString,
arrayToDayjs,
dateStringToDayjs,
} from "@/app/utils/formatUtil";
import dayjs from "dayjs";
import { Button, Grid, Stack, Tab, Tabs, TabsProps } from "@mui/material";
import PickOrders from "./PickOrders";
import ConsolidatedPickOrders from "./ConsolidatedPickOrders";

import { getServerI18n } from "@/i18n";
interface Props {
pickOrders: PickOrderResult[];
pickOrders: PickOrderResult[];
}

type SearchQuery = Partial<Omit<PickOrderResult,
| "id"
| "consoCode"
| "completeDate">>
type SearchQuery = Partial<
Omit<PickOrderResult, "id" | "consoCode" | "completeDate">
>;

type SearchParamNames = keyof SearchQuery;

@@ -27,76 +43,134 @@ const PickOrderSearch: React.FC<Props> = ({
}) => {
const { t } = useTranslation("pickOrder");

const [filteredPickOrders, setFilteredPickOrders] = useState(pickOrders)
const [tabIndex, setTabIndex] = useState(0);
const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>(
(_e, newValue) => {
setTabIndex(newValue);
},
[],
);
const [filteredPickOrders, setFilteredPickOrders] = useState(pickOrders);
const [filterArgs, setFilterArgs] = useState<Record<string, any>>({});
const [tabIndex, setTabIndex] = useState(0);
const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>(
(_e, newValue) => {
setTabIndex(newValue);
},
[]
);

const searchCriteria: Criterion<SearchParamNames>[] = useMemo(() => [
{ label: t("Code"), paramName: "code", type: "text" },
{ label: t("Target Date From"), label2: t("Target Date To"), paramName: "targetDate", type: "dateRange" },
{
label: t("Type"), paramName: "type", type: "autocomplete",
options: sortBy(
uniqBy(pickOrders.map((po) => ({ value: po.type, label: t(upperCase(po.type)) })), "value"),
"label")
},
{
label: t("Status"), paramName: "status", type: "autocomplete",
options: sortBy(
uniqBy(pickOrders.map((po) => ({ value: po.status, label: t(upperFirst(po.status)) })), "value"),
"label")
},
{
label: t("Items"), paramName: "items", type: "autocomplete", // multiple: true,
options: uniqBy(flatten(sortBy(
pickOrders.map((po) => po.items ? po.items.map((item) => ({
value: item.name, label: item.name,
// group: item.type
})) : []),
"label")), "value")
},
], [t])
const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
() => [
{ label: t("Code"), paramName: "code", type: "text" },
{
label: t("Target Date From"),
label2: t("Target Date To"),
paramName: "targetDate",
type: "dateRange",
},
{
label: t("Type"),
paramName: "type",
type: "autocomplete",
options: sortBy(
uniqBy(
pickOrders.map((po) => ({
value: po.type,
label: t(upperCase(po.type)),
})),
"value"
),
"label"
),
},
{
label: t("Status"),
paramName: "status",
type: "autocomplete",
options: sortBy(
uniqBy(
pickOrders.map((po) => ({
value: po.status,
label: t(upperFirst(po.status)),
})),
"value"
),
"label"
),
},
{
label: t("Items"),
paramName: "items",
type: "autocomplete", // multiple: true,
options: uniqBy(
flatten(
sortBy(
pickOrders.map((po) =>
po.items
? po.items.map((item) => ({
value: item.name,
label: item.name,
// group: item.type
}))
: []
),
"label"
)
),
"value"
),
},
],
[t]
);

const onReset = useCallback(() => {
setFilteredPickOrders(pickOrders)
}, [pickOrders])
const onReset = useCallback(() => {
setFilteredPickOrders(pickOrders);
}, [pickOrders]);

return (
<>
<SearchBox
criteria={searchCriteria}
onSearch={(query) => {
setFilteredPickOrders(
pickOrders.filter(
(po) => {
const poTargetDateStr = arrayToDayjs(po.targetDate)
return (
<>
<SearchBox
criteria={searchCriteria}
onSearch={(query) => {
setFilterArgs({ ...query }); // modify later
setFilteredPickOrders(
pickOrders.filter((po) => {
const poTargetDateStr = arrayToDayjs(po.targetDate);

// console.log(intersectionWith(po.items?.map(item => item.name), query.items))
return po.code.toLowerCase().includes(query.code.toLowerCase())
&& (isEmpty(query.targetDate) || poTargetDateStr.isSame(query.targetDate) || poTargetDateStr.isAfter(query.targetDate))
&& (isEmpty(query.targetDateTo) || poTargetDateStr.isSame(query.targetDateTo) || poTargetDateStr.isBefore(query.targetDateTo))
&& (intersectionWith(["All"], query.items).length > 0 || intersectionWith(po.items?.map(item => item.name), query.items).length > 0)
&& (query.status.toLowerCase() == "all" || po.status.toLowerCase().includes(query.status.toLowerCase()))
&& (query.type.toLowerCase() == "all" || po.type.toLowerCase().includes(query.type.toLowerCase()))
}
)
)
}}
onReset={onReset}
/>
<Tabs value={tabIndex} onChange={handleTabChange} variant="scrollable">
<Tab label={t("Pick Orders")} iconPosition="end" />
<Tab label={t("Consolidated Pick Orders")} iconPosition="end" />
</Tabs>
{tabIndex === 0 && <PickOrders filteredPickOrders={filteredPickOrders}/>}
</>
)
}
// console.log(intersectionWith(po.items?.map(item => item.name), query.items))
return (
po.code.toLowerCase().includes(query.code.toLowerCase()) &&
(isEmpty(query.targetDate) ||
poTargetDateStr.isSame(query.targetDate) ||
poTargetDateStr.isAfter(query.targetDate)) &&
(isEmpty(query.targetDateTo) ||
poTargetDateStr.isSame(query.targetDateTo) ||
poTargetDateStr.isBefore(query.targetDateTo)) &&
(intersectionWith(["All"], query.items).length > 0 ||
intersectionWith(
po.items?.map((item) => item.name),
query.items
).length > 0) &&
(query.status.toLowerCase() == "all" ||
po.status
.toLowerCase()
.includes(query.status.toLowerCase())) &&
(query.type.toLowerCase() == "all" ||
po.type.toLowerCase().includes(query.type.toLowerCase()))
);
})
);
}}
onReset={onReset}
/>
<Tabs value={tabIndex} onChange={handleTabChange} variant="scrollable">
<Tab label={t("Pick Orders")} iconPosition="end" />
<Tab label={t("Consolidated Pick Orders")} iconPosition="end" />
</Tabs>
{tabIndex === 0 && (
<PickOrders
filteredPickOrders={filteredPickOrders}
filterArgs={filterArgs}
/>
)}
{tabIndex === 1 && <ConsolidatedPickOrders filterArgs={filterArgs} />}
</>
);
};

export default PickOrderSearch;
export default PickOrderSearch;

+ 9
- 2
src/components/PickOrderSearch/PickOrderSearchWrapper.tsx Datei anzeigen

@@ -1,4 +1,4 @@
import { fetchPickOrders } from "@/app/api/pickOrder";
import { fetchPickOrders } from "@/app/api/pickorder";
import GeneralLoading from "../General/GeneralLoading";
import PickOrderSearch from "./PickOrderSearch";

@@ -10,7 +10,14 @@ const PickOrderSearchWrapper: React.FC & SubComponents = async () => {
const [
pickOrders
] = await Promise.all([
fetchPickOrders()
fetchPickOrders({
code: undefined,
targetDateFrom: undefined,
targetDateTo: undefined,
type: undefined,
status: undefined,
itemName: undefined,
})
])

return <PickOrderSearch pickOrders={pickOrders}/>


+ 138
- 81
src/components/PickOrderSearch/PickOrders.tsx Datei anzeigen

@@ -1,100 +1,157 @@
import { Button, Grid } from "@mui/material";
import { Button, CircularProgress, Grid } from "@mui/material";
import SearchResults, { Column } from "../SearchResults/SearchResults";
import { PickOrderResult } from "@/app/api/pickOrder";
import { PickOrderResult } from "@/app/api/pickorder";
import { useTranslation } from "react-i18next";
import { useCallback, useMemo, useState } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { isEmpty, upperCase, upperFirst } from "lodash";
import { arrayToDateString } from "@/app/utils/formatUtil";
import { consolidatePickOrder, fetchPickOrderClient } from "@/app/api/pickorder/actions";
import useUploadContext from "../UploadProvider/useUploadContext";

interface Props {
filteredPickOrders: PickOrderResult[],
filteredPickOrders: PickOrderResult[];
filterArgs: Record<string, any>;
}

const PickOrders: React.FC<Props> = ({
filteredPickOrders
}) => {
const { t } = useTranslation("pickOrder")
const [selectedRows, setSelectedRows] = useState<(string | number)[]>([]);
const PickOrders: React.FC<Props> = ({ filteredPickOrders, filterArgs }) => {
const { t } = useTranslation("pickOrder");
const [selectedRows, setSelectedRows] = useState<(string | number)[]>([]);
const [filteredPickOrder, setFilteredPickOrder] = useState(
[] as PickOrderResult[]
);
const { setIsUploading } = useUploadContext();
const [isLoading, setIsLoading] = useState(false);
const [pagingController, setPagingController] = useState({
pageNum: 0,
pageSize: 10,
});
const [totalCount, setTotalCount] = useState<number>();

const handleConsolidatedRows = useCallback(() => {
const handleConsolidatedRows = useCallback(async () => {
console.log(selectedRows);
setIsUploading(true);
try {
const res = await consolidatePickOrder(selectedRows as number[]);
if (res) {
console.log(res);
}
} catch {
setIsUploading(false);
}
fetchNewPagePickOrder(pagingController, filterArgs);
setIsUploading(false);
}, [selectedRows, pagingController]);

}, [selectedRows])
const fetchNewPagePickOrder = useCallback(
async (
pagingController: Record<string, number>,
filterArgs: Record<string, number>
) => {
setIsLoading(true);
const params = {
...pagingController,
...filterArgs,
};
const res = await fetchPickOrderClient(params)
if (res) {
console.log(res);
setFilteredPickOrder(res.records);
setTotalCount(res.total);
}
setIsLoading(false);
},
[]
);

const columns = useMemo<Column<PickOrderResult>[]>(() => [
{
name: "id",
label: "",
type: "checkbox",
disabled: (params) => {
return !isEmpty(params.consoCode);
}
},
{
name: "code",
label: t("Code"),
},
{
name: "consoCode",
label: t("Consolidated Code"),
renderCell: (params) => {
return params.consoCode ?? "N/A"
}
useEffect(() => {
fetchNewPagePickOrder(pagingController, filterArgs);
}, [fetchNewPagePickOrder, pagingController, filterArgs]);

const columns = useMemo<Column<PickOrderResult>[]>(
() => [
{
name: "id",
label: "",
type: "checkbox",
disabled: (params) => {
return !isEmpty(params.consoCode);
},
{
name: "type",
label: t("type"),
renderCell: (params) => {
return upperCase(params.type)
}
},
{
name: "code",
label: t("Code"),
},
{
name: "consoCode",
label: t("Consolidated Code"),
renderCell: (params) => {
return params.consoCode ?? "";
},
{
name: "items",
label: t("Items"),
renderCell: (params) => {
return params.items?.map((i) => i.name).join(", ")
}
},
{
name: "type",
label: t("type"),
renderCell: (params) => {
return upperCase(params.type);
},
{
name: "targetDate",
label: t("Target Date"),
renderCell: (params) => {
return arrayToDateString(params.targetDate)
}
},
{
name: "items",
label: t("Items"),
renderCell: (params) => {
return params.items?.map((i) => i.name).join(", ");
},
{
name: "releasedBy",
label: t("Released By"),
},
{
name: "targetDate",
label: t("Target Date"),
renderCell: (params) => {
return arrayToDateString(params.targetDate);
},
{
name: "status",
label: t("Status"),
renderCell: (params) => {
return upperFirst(params.status)
}
},
{
name: "releasedBy",
label: t("Released By"),
},
{
name: "status",
label: t("Status"),
renderCell: (params) => {
return upperFirst(params.status);
},
], [t])
},
],
[t]
);

return (
<Grid container rowGap={1}>
<Grid item xs={3}>
<Button
disabled={selectedRows.length < 1}
variant="outlined"
>
{t("Consolidate")}
</Button>
</Grid>
<Grid item xs={12}>
<SearchResults<PickOrderResult> items={filteredPickOrders} columns={columns} pagingController={{
pageNum: 0,
pageSize: 0
}}
checkboxIds={selectedRows}
setCheckboxIds={setSelectedRows}
/>
</Grid>
</Grid>
)
}
return (
<Grid container rowGap={1}>
<Grid item xs={3}>
<Button
disabled={selectedRows.length < 1}
variant="outlined"
onClick={handleConsolidatedRows}
>
{t("Consolidate")}
</Button>
</Grid>
<Grid item xs={12}>
{isLoading ? (
<CircularProgress size={40} />
) : (
<SearchResults<PickOrderResult>
items={filteredPickOrder}
columns={columns}
pagingController={pagingController}
setPagingController={setPagingController}
totalCount={totalCount}
checkboxIds={selectedRows!!}
setCheckboxIds={setSelectedRows}
/>
)}
</Grid>
</Grid>
);
};

export default PickOrders;
export default PickOrders;

+ 1
- 1
src/components/PoSearch/PoSearch.tsx Datei anzeigen

@@ -152,7 +152,7 @@ const PoSearch: React.FC<Props> = ({
setTotalCount(res.total);
}
},
[fetchPoListClient, pagingController]
[fetchPoListClient]
);

useEffect(() => {


+ 19
- 2
src/components/SearchResults/SearchResults.tsx Datei anzeigen

@@ -209,7 +209,24 @@ function SearchResults<T extends ResultWithId>({
};

// checkbox
const handleRowClick = useCallback((event: MouseEvent<unknown>, id: string | number) => {
const handleRowClick = useCallback((event: MouseEvent<unknown>, item: T, columns: Column<T>[]) => {
// check is disabled or not
var disabled = false
columns.forEach((col) => {
if (isCheckboxColumn(col) && col.disabled) {
disabled = col.disabled(item)
if (disabled) {
return;
}
}
})

if (disabled) {
return;
}

// set id
const id = item.id
if (setCheckboxIds) {
const selectedIndex = checkboxIds.indexOf(id);
let newSelected: (string | number)[] = [];
@@ -257,7 +274,7 @@ function SearchResults<T extends ResultWithId>({
hover
tabIndex={-1}
key={item.id}
onClick={setCheckboxIds ? (event) => handleRowClick(event, item.id) : undefined}
onClick={setCheckboxIds? (event) => handleRowClick(event, item, columns) : undefined}
role={setCheckboxIds ? "checkbox" : undefined}
>
{columns.map((column, idx) => {


+ 20
- 0
src/i18n/zh/common.json Datei anzeigen

@@ -1,4 +1,24 @@
{
"Overview": "概述",
"Qc Item": "品質檢驗項目",
"Dashboard": "儀表板",
"dashboard": "儀表板",
"Raw Material": "原料",
"Purchase Order": "採購訂單",
"Pick Order": "提料單",
"View item In-out And inventory Ledger": "存貨",
"Inventory": "存貨",
"Delivery": "送貨",
"Delivery Order": "送貨單",
"Scheduling": "生產計劃",
"Demand Forecast Setting": "粗排設定",
"Demand Forecast": "粗排",
"FG & Material Demand Forecast Detail": "成品 & 原料粗排細節",
"Detail Scheduling": "細排",
"FG Production Schedule": "成品生產計劃",
"Settings": "設定",
"Edit": "編輯",

"Search Criteria": "搜尋條件",
"All": "全部",
"No options": "沒有選項",


Laden…
Abbrechen
Speichern