瀏覽代碼

update qc master & qr scanner

feature/axios_provider
cyril.tsui 5 月之前
父節點
當前提交
19e3013a53
共有 10 個檔案被更改,包括 141 行新增47 行删除
  1. +15
    -2
      src/app/api/settings/qcItem/actions.ts
  2. +3
    -3
      src/app/api/settings/qcItem/index.ts
  3. +6
    -1
      src/app/utils/fetchUtil.ts
  4. +2
    -1
      src/components/QcItemSave/QcItemDetails.tsx
  5. +51
    -5
      src/components/QcItemSave/QcItemSave.tsx
  6. +2
    -2
      src/components/QcItemSave/QcItemSaveWrapper.tsx
  7. +41
    -17
      src/components/QcItemSearch/QcItemSearch.tsx
  8. +10
    -10
      src/components/QrCodeScanner/QrCodeScanner.tsx
  9. +3
    -2
      src/components/SearchResults/SearchResults.tsx
  10. +8
    -4
      src/components/Swal/CustomAlerts.tsx

+ 15
- 2
src/app/api/settings/qcItem/actions.ts 查看文件

@@ -2,7 +2,8 @@

import { serverFetchJson } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";
import { revalidateTag } from "next/cache";
import { revalidatePath, revalidateTag } from "next/cache";
import { QcItemResult } from '@/app/api/settings/qcItem';

export interface SaveQcItemInputs {
id?: number;
@@ -26,7 +27,19 @@ export const saveQcItem = async (data: SaveQcItemInputs) => {
headers: { "Content-Type": "application/json" },
})

revalidateTag(`qcItems`)
revalidateTag("qcItems")

return response
}

export const deleteQcItem = async (id: number) => {
const response = await serverFetchJson<QcItemResult[]>(`${BASE_API_URL}/qcItems/${id}`, {
method: "DELETE",
headers: { "Content-Type": "application/json" },
})

revalidateTag("qcItems")
revalidatePath("/(main)/settings/qcItem")

return response
}

+ 3
- 3
src/app/api/settings/qcItem/index.ts 查看文件

@@ -22,11 +22,11 @@ export const fetchQcItems = cache(async () => {
});
});

export const fetchQcItemDetail = cache(async (qcItemId: string) => {
export const fetchQcItemDetails = cache(async (qcItemId: string) => {
return serverFetchJson<SaveQcItemInputs>(
`${BASE_API_URL}/qcItems/qcItemDetail/${qcItemId}`,
`${BASE_API_URL}/qcItems/qcItemDetails/${qcItemId}`,
{
next: { tags: [`qcItemDetail_${qcItemId}`] }
next: { tags: [`qcItemDetails_${qcItemId}`] }
}
)
})

+ 6
- 1
src/app/utils/fetchUtil.ts 查看文件

@@ -45,6 +45,7 @@ export const serverFetch: typeof fetch = async (input, init) => {
...(accessToken
? {
Authorization: `Bearer ${accessToken}`,
}
: {}),
},
@@ -56,13 +57,17 @@ type FetchParams = Parameters<typeof fetch>;
export async function serverFetchJson<T>(...args: FetchParams) {
const response = await serverFetch(...args);
if (response.ok) {
if (response.status === 204) {
return response.status as T
}
return response.json() as T;
} else {
switch (response.status) {
case 401:
signOutUser();
default:
throw Error("Something went wrong fetching data in server.");
throw new ServerFetchError("Something went wrong fetching data in server.", response);
}
}
}


+ 2
- 1
src/components/QcItemSave/QcItemDetails.tsx 查看文件

@@ -38,9 +38,10 @@ const QcItemDetails = () => {
})}
/>
</Grid>
<Grid item xs={6}>
<Grid item xs={12}>
<TextField
label={t("Description")}
// multiline
fullWidth
{...register("description", {
maxLength: 100


+ 51
- 5
src/components/QcItemSave/QcItemSave.tsx 查看文件

@@ -1,12 +1,14 @@
"use client"

import { saveQcItem, SaveQcItemInputs } from "@/app/api/settings/qcItem/actions";
import { Stack } from "@mui/material";
import { deleteQcItem, saveQcItem, SaveQcItemInputs } from "@/app/api/settings/qcItem/actions";
import { Button, Stack } from "@mui/material";
import { useCallback } from "react";
import { FormProvider, SubmitHandler, useForm } from "react-hook-form";
import { errorDialogWithContent, submitDialog } from "../Swal/CustomAlerts";
import { deleteDialog, errorDialogWithContent, submitDialog, successDialog } from "../Swal/CustomAlerts";
import { useTranslation } from "react-i18next";
import { useRouter } from "next/navigation";
import QcItemDetails from "./QcItemDetails";
import { Check, Close, Delete } from "@mui/icons-material";

interface Props {
defaultInputs?: SaveQcItemInputs;
@@ -38,7 +40,10 @@ const QcItemSave: React.FC<Props> = ({

errorDialogWithContent(t("Submit Error"), errorContents, t)
} else {
router.push("/settings/qcItem")
await successDialog(
t("Submit Success"),
t,
() => router.push("/settings/qcItem"))
}
},
[]
@@ -51,6 +56,22 @@ const QcItemSave: React.FC<Props> = ({
}, []
)

const handleCancel = () => {
router.replace("/qcItem");
};

const handleDelete = () => {
deleteDialog(async () => {
await deleteQcItem(formProps.getValues("id")!);

await successDialog(
t("Delete Success"),
t,
() => router.replace("/settings/qcItem")
);
}, t);
};

return (
<>
<FormProvider {...formProps}>
@@ -59,7 +80,32 @@ const QcItemSave: React.FC<Props> = ({
component={"form"}
onSubmit={formProps.handleSubmit(onSubmit)}
>

<QcItemDetails />
<Stack direction="row" justifyContent="flex-end" gap={1}>
{defaultInputs?.id && <Button
variant="outlined"
color="error"
startIcon={<Delete />}
onClick={handleDelete}
>
{t("Delete")}
</Button>}
<Button
variant="outlined"
startIcon={<Close />}
onClick={handleCancel}
>
{t("Cancel")}
</Button>
<Button
name="submit"
variant="contained"
startIcon={<Check />}
type="submit"
>
{t("Submit")}
</Button>
</Stack>
</Stack>
</FormProvider>
</>


+ 2
- 2
src/components/QcItemSave/QcItemSaveWrapper.tsx 查看文件

@@ -1,7 +1,7 @@
import React from "react"
import QcItemSaveLoading from "./QcItemSaveLoading"
import QcItemSave from "./QcItemSave";
import { fetchQcItemDetail } from "@/app/api/settings/qcItem";
import { fetchQcItemDetails } from "@/app/api/settings/qcItem";

interface SubComponents {
Loading: typeof QcItemSaveLoading;
@@ -16,7 +16,7 @@ type Props = SaveQcItemProps
const QcItemSaveWrapper: React.FC<Props> & SubComponents = async (
props
) => {
const qcItem = props.id ? await fetchQcItemDetail(props.id) : undefined;
const qcItem = props.id ? await fetchQcItemDetails(props.id) : undefined;

return <QcItemSave defaultInputs={qcItem} />;
};


+ 41
- 17
src/components/QcItemSearch/QcItemSearch.tsx 查看文件

@@ -6,9 +6,12 @@ import { useTranslation } from "react-i18next";
import SearchResults, { Column } from "../SearchResults";
import EditNote from "@mui/icons-material/EditNote";
import { QcItemResult } from "@/app/api/settings/qcItem";
import { useRouter } from "next/navigation";
import { usePathname, useRouter } from "next/navigation";
import QrCodeScanner from "../QrCodeScanner";
import { Button } from "@mui/material";
import { Delete } from "@mui/icons-material";
import { deleteDialog, successDialog } from "../Swal/CustomAlerts";
import { deleteQcItem } from "@/app/api/settings/qcItem/actions";

interface Props {
qcItems: QcItemResult[];
@@ -20,6 +23,7 @@ type SearchParamNames = keyof SearchQuery;
const qcItemSearch: React.FC<Props> = ({ qcItems }) => {
const { t } = useTranslation("qcItems");
const router = useRouter();
const pathname = usePathname()

// If qcItem searching is done on the server-side, then no need for this.
const [filteredQcItems, setFilteredQcItems] = useState(qcItems);
@@ -40,11 +44,24 @@ const qcItemSearch: React.FC<Props> = ({ qcItems }) => {

const onQcItemClick = useCallback(
(qcItem: QcItemResult) => {
router.push(`/edit/${qcItem.id}`);
router.push(`${pathname}/edit?id=${qcItem.id}`);
},
[router]
);

const handleDelete = useCallback(
(qcItem: QcItemResult) => {
deleteDialog(async () => {
qcItems = await deleteQcItem(qcItem.id);
setFilteredQcItems(qcItems)

await successDialog(
t("Delete Success"),
t
);
}, t);
}, [])

const columns = useMemo<Column<QcItemResult>[]>(
() => [
{
@@ -55,28 +72,35 @@ const qcItemSearch: React.FC<Props> = ({ qcItems }) => {
},
{ name: "code", label: t("Code") },
{ name: "name", label: t("Name") },
{
name: "id",
label: t("Delete"),
onClick: handleDelete,
buttonIcon: <Delete />,
buttonColor: "error"
}
],
[t, onQcItemClick]
);

const [isOpenScanner, setOpenScanner] = useState(false)
const onOpenScanner = useCallback(() => {
setOpenScanner(true)
}, [])
// const [isOpenScanner, setOpenScanner] = useState(false)
// const onOpenScanner = useCallback(() => {
// setOpenScanner(true)
// }, [])

const onCloseScanner = useCallback(() => {
setOpenScanner(false)
}, [])
// const onCloseScanner = useCallback(() => {
// setOpenScanner(false)
// }, [])

const handleScanSuccess = useCallback((result: string) => {
console.log(result)
}, [])
// const handleScanSuccess = useCallback((result: string) => {
// console.log(result)
// }, [])

return (
<>
<QrCodeScanner isOpen={isOpenScanner} onClose={onCloseScanner} onScanSuccess={handleScanSuccess} />
<Button onClick={onOpenScanner}>abc</Button>
{/* <SearchBox
{/* <QrCodeScanner isOpen={isOpenScanner} onClose={onCloseScanner} onScanSuccess={handleScanSuccess} />
<Button onClick={onOpenScanner}>abc</Button> */}
<SearchBox
criteria={searchCriteria}
onSearch={(query) => {
setFilteredQcItems(
@@ -89,9 +113,9 @@ const qcItemSearch: React.FC<Props> = ({ qcItems }) => {
}}
onReset={onReset}
/>
<SearchResults<QcItemResult> items={filteredQcItems} columns={columns} /> */}
<SearchResults<QcItemResult> items={filteredQcItems} columns={columns} />
</>
)
)
};

export default qcItemSearch;

+ 10
- 10
src/components/QrCodeScanner/QrCodeScanner.tsx 查看文件

@@ -159,14 +159,14 @@ const QrCodeScanner: React.FC<QrCodeScannerProps> = ({
return (
<>
<Stack spacing={2}>
{title ? <Typography variant="overline" display="block" marginBlockEnd={1} paddingLeft={2}>
{title && <Typography variant="overline" display="block" marginBlockEnd={1} paddingLeft={2}>
{"Title"}
</Typography> : null}
</Typography>}
<Grid container alignItems="center" justifyContent="center" rowSpacing={2} columns={{ xs: 6, sm: 12 }}>
<Grid item xs={12}>
<div style={{ textAlign: "center", margin: "auto", justifyContent: "center" }} id="qr-reader" hidden={isScanned} />
</Grid>
{cameraList.length > 0 ? <Grid item xs={6} >
{cameraList.length > 0 && <Grid item xs={6} >
<Autocomplete
disableClearable
noOptionsText={t("No Options")}
@@ -183,11 +183,11 @@ const QrCodeScanner: React.FC<QrCodeScannerProps> = ({
/>
)}
/>
</Grid> : null}
</Grid>}
{
contents ? contents.map((string) => {
contents && contents.map((string) => {
return <Grid item xs={8}>{string}</Grid>
}) : null
})
}
</Grid>
<Stack direction="row" justifyContent={"flex-end"} spacing={2} sx={{ margin: 2 }}>
@@ -195,7 +195,7 @@ const QrCodeScanner: React.FC<QrCodeScannerProps> = ({
size="small"
onClick={switchScanStatus}
variant="contained"
sx={{ margin: 2 }}
// sx={{ margin: 2 }}
startIcon={isScanned ? <QrCodeScannerIcon /> : <StopCircleOutlinedIcon />}
>
{isScanned ? t("Start Scanning") : t("Stop Scanning")}
@@ -203,9 +203,9 @@ const QrCodeScanner: React.FC<QrCodeScannerProps> = ({
<Button
size="small"
onClick={handleScanCloseButton}
variant="contained"
color="error"
sx={{ margin: 2 }}
variant="outlined"
// color="error"
// sx={{ margin: 2 }}
startIcon={<CloseOutlinedIcon />}
>
{t("Cancel")}


+ 3
- 2
src/components/SearchResults/SearchResults.tsx 查看文件

@@ -11,7 +11,7 @@ import TablePagination, {
TablePaginationProps,
} from "@mui/material/TablePagination";
import TableRow from "@mui/material/TableRow";
import IconButton from "@mui/material/IconButton";
import IconButton, { IconButtonOwnProps } from "@mui/material/IconButton";

export interface ResultWithId {
id: string | number;
@@ -25,6 +25,7 @@ interface BaseColumn<T extends ResultWithId> {
interface ColumnWithAction<T extends ResultWithId> extends BaseColumn<T> {
onClick: (item: T) => void;
buttonIcon: React.ReactNode;
buttonColor?: IconButtonOwnProps["color"];
}

export type Column<T extends ResultWithId> =
@@ -91,7 +92,7 @@ function SearchResults<T extends ResultWithId>({
<TableCell key={`${columnName.toString()}-${idx}`}>
{isActionColumn(column) ? (
<IconButton
color="primary"
color={column.buttonColor ?? "primary"}
onClick={() => column.onClick(item)}
>
{column.buttonIcon}


+ 8
- 4
src/components/Swal/CustomAlerts.tsx 查看文件

@@ -2,7 +2,7 @@ import Swal, { SweetAlertOptions } from "sweetalert2";
import "./sweetalert2.css";
import { TFunction } from "i18next";

export type SweetAlertTitle = string | HTMLElement | JQuery | undefined
export type SweetAlertTitle = string | HTMLElement | JQuery | undefined
export type SweetAlertHtml = string | HTMLElement | JQuery | undefined
export type SweetAlertConfirmButtonText = string | undefined

@@ -29,13 +29,17 @@ export const popup = (options: SweetAlertOptions) => {
Swal.fire(options);
};

export const successDialog = (title: SweetAlertTitle, t: Transaction) => {
return Swal.fire({
export const successDialog = async (title: SweetAlertTitle, t: Transaction, confirmAction?: () => void) => {
const result = await Swal.fire({
icon: "success",
title: title,
confirmButtonText: t("Confirm"),
showConfirmButton: true,
});

if (result.isConfirmed && confirmAction) {
confirmAction();
}
};

export const successDialogWithContent = (title: SweetAlertTitle, html: SweetAlertHtml, t: Transaction) => {
@@ -81,7 +85,7 @@ export const submitDialog = async (
t: Transaction,
{ ...props } = {
title: t("Do you want to submit?") as SweetAlertTitle,
confirmButtonText: t("Submit") as SweetAlertConfirmButtonText,
confirmButtonText: t("Submit") as SweetAlertConfirmButtonText,
}
) => {
// console.log(props)


Loading…
取消
儲存