|
- "use client";
-
- import React, { useCallback, useEffect, useMemo, useState } from "react";
- import {
- Box,
- Button,
- Dialog,
- DialogActions,
- DialogContent,
- DialogTitle,
- Grid,
- Stack,
- Table,
- TableBody,
- TableCell,
- TableContainer,
- TableHead,
- TableRow,
- TextField,
- Typography,
- IconButton,
- CircularProgress,
- } from "@mui/material";
- import { useTranslation } from "react-i18next";
- import { Add, Delete, Edit } from "@mui/icons-material";
- import SearchBox, { Criterion } from "../SearchBox/SearchBox";
- import SearchResults, { Column } from "../SearchResults/SearchResults";
- import {
- saveItemQcCategoryMapping,
- deleteItemQcCategoryMapping,
- getItemQcCategoryMappings,
- fetchQcCategoriesForAll,
- fetchItemsForAll,
- getItemByCode,
- } from "@/app/api/settings/qcItemAll/actions";
- import {
- QcCategoryResult,
- ItemsResult,
- } from "@/app/api/settings/qcItemAll";
- import { ItemQcCategoryMappingInfo } from "@/app/api/settings/qcItemAll";
- import {
- deleteDialog,
- errorDialogWithContent,
- submitDialog,
- successDialog,
- } from "../Swal/CustomAlerts";
-
- type SearchQuery = Partial<Omit<QcCategoryResult, "id">>;
- type SearchParamNames = keyof SearchQuery;
-
- const Tab0ItemQcCategoryMapping: React.FC = () => {
- const { t } = useTranslation("qcItemAll");
- const [qcCategories, setQcCategories] = useState<QcCategoryResult[]>([]);
- const [filteredQcCategories, setFilteredQcCategories] = useState<QcCategoryResult[]>([]);
- const [selectedCategory, setSelectedCategory] = useState<QcCategoryResult | null>(null);
- const [mappings, setMappings] = useState<ItemQcCategoryMappingInfo[]>([]);
- const [openDialog, setOpenDialog] = useState(false);
- const [openAddDialog, setOpenAddDialog] = useState(false);
- const [itemCode, setItemCode] = useState<string>("");
- const [validatedItem, setValidatedItem] = useState<ItemsResult | null>(null);
- const [itemCodeError, setItemCodeError] = useState<string>("");
- const [validatingItemCode, setValidatingItemCode] = useState<boolean>(false);
- const [selectedType, setSelectedType] = useState<string>("IQC");
- const [loading, setLoading] = useState(true);
-
- useEffect(() => {
- const loadData = async () => {
- setLoading(true);
- try {
- // Only load categories list (same as Tab 2) - fast!
- const categories = await fetchQcCategoriesForAll();
- setQcCategories(categories || []);
- setFilteredQcCategories(categories || []);
- } catch (error) {
- console.error("Tab0: Error loading data:", error);
- setQcCategories([]);
- setFilteredQcCategories([]);
- if (error instanceof Error) {
- errorDialogWithContent(t("Error"), error.message, t);
- }
- } finally {
- setLoading(false);
- }
- };
- loadData();
- }, []);
-
- const handleViewMappings = useCallback(async (category: QcCategoryResult) => {
- setSelectedCategory(category);
- const mappingData = await getItemQcCategoryMappings(category.id);
- setMappings(mappingData);
- setOpenDialog(true);
- }, []);
-
- const handleAddMapping = useCallback(() => {
- if (!selectedCategory) return;
- setItemCode("");
- setValidatedItem(null);
- setItemCodeError("");
- setOpenAddDialog(true);
- }, [selectedCategory]);
-
- const handleItemCodeChange = useCallback(async (code: string) => {
- setItemCode(code);
- setValidatedItem(null);
- setItemCodeError("");
-
- if (!code || code.trim() === "") {
- return;
- }
-
- setValidatingItemCode(true);
- try {
- const item = await getItemByCode(code.trim());
- if (item) {
- setValidatedItem(item);
- setItemCodeError("");
- } else {
- setValidatedItem(null);
- setItemCodeError(t("Item code not found"));
- }
- } catch (error) {
- setValidatedItem(null);
- setItemCodeError(t("Error validating item code"));
- } finally {
- setValidatingItemCode(false);
- }
- }, [t]);
-
- const handleSaveMapping = useCallback(async () => {
- if (!selectedCategory || !validatedItem) return;
-
- await submitDialog(async () => {
- try {
- await saveItemQcCategoryMapping(
- validatedItem.id as number,
- selectedCategory.id,
- selectedType
- );
- // Close add dialog first
- setOpenAddDialog(false);
- setItemCode("");
- setValidatedItem(null);
- setItemCodeError("");
- // Reload mappings to update the view
- const mappingData = await getItemQcCategoryMappings(selectedCategory.id);
- setMappings(mappingData);
- // Show success message after closing dialogs
- await successDialog(t("Submit Success"), t);
- // Keep the view dialog open to show updated data
- } catch (error) {
- errorDialogWithContent(t("Submit Error"), String(error), t);
- }
- }, t);
- }, [selectedCategory, validatedItem, selectedType, t]);
-
- const handleDeleteMapping = useCallback(
- async (mappingId: number) => {
- if (!selectedCategory) return;
-
- deleteDialog(async () => {
- try {
- await deleteItemQcCategoryMapping(mappingId);
- await successDialog(t("Delete Success"), t);
- // Reload mappings
- const mappingData = await getItemQcCategoryMappings(selectedCategory.id);
- setMappings(mappingData);
- // No need to reload categories list - it doesn't change
- } catch (error) {
- errorDialogWithContent(t("Delete Error"), String(error), t);
- }
- }, t);
- },
- [selectedCategory, t]
- );
-
- const typeOptions = ["IQC", "IPQC", "OQC", "FQC"];
-
- const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
- () => [
- { label: t("Code"), paramName: "code", type: "text" },
- { label: t("Name"), paramName: "name", type: "text" },
- ],
- [t]
- );
-
- const onReset = useCallback(() => {
- setFilteredQcCategories(qcCategories);
- }, [qcCategories]);
-
- const columnWidthSx = (width = "10%") => {
- return { width: width, whiteSpace: "nowrap" };
- };
-
- const columns = useMemo<Column<QcCategoryResult>[]>(
- () => [
- { name: "code", label: t("Qc Category Code"), sx: columnWidthSx("20%") },
- { name: "name", label: t("Qc Category Name"), sx: columnWidthSx("40%") },
- {
- name: "id",
- label: t("Actions"),
- onClick: (category) => handleViewMappings(category),
- buttonIcon: <Edit />,
- buttonIcons: {} as any,
- sx: columnWidthSx("10%"),
- },
- ],
- [t, handleViewMappings]
- );
-
- if (loading) {
- return (
- <Box sx={{ display: "flex", justifyContent: "center", alignItems: "center", minHeight: "200px" }}>
- <CircularProgress />
- </Box>
- );
- }
-
- return (
- <Box>
- <SearchBox
- criteria={searchCriteria}
- onSearch={(query) => {
- setFilteredQcCategories(
- qcCategories.filter(
- (qc) =>
- (!query.code || qc.code.toLowerCase().includes(query.code.toLowerCase())) &&
- (!query.name || qc.name.toLowerCase().includes(query.name.toLowerCase()))
- )
- );
- }}
- onReset={onReset}
- />
- <SearchResults<QcCategoryResult>
- items={filteredQcCategories}
- columns={columns}
- />
-
- {/* View Mappings Dialog */}
- <Dialog open={openDialog} onClose={() => setOpenDialog(false)} maxWidth="md" fullWidth>
- <DialogTitle>
- {t("Mapping Details")} - {selectedCategory?.name}
- </DialogTitle>
- <DialogContent>
- <Stack spacing={2} sx={{ mt: 1 }}>
- <Box sx={{ display: "flex", justifyContent: "flex-end" }}>
- <Button
- variant="contained"
- startIcon={<Add />}
- onClick={handleAddMapping}
- >
- {t("Add Mapping")}
- </Button>
- </Box>
- <TableContainer>
- <Table>
- <TableHead>
- <TableRow>
- <TableCell>{t("Item Code")}</TableCell>
- <TableCell>{t("Item Name")}</TableCell>
- <TableCell>{t("Type")}</TableCell>
- <TableCell>{t("Actions")}</TableCell>
- </TableRow>
- </TableHead>
- <TableBody>
- {mappings.length === 0 ? (
- <TableRow>
- <TableCell colSpan={4} align="center">
- {t("No mappings found")}
- </TableCell>
- </TableRow>
- ) : (
- mappings.map((mapping) => (
- <TableRow key={mapping.id}>
- <TableCell>{mapping.itemCode}</TableCell>
- <TableCell>{mapping.itemName}</TableCell>
- <TableCell>{mapping.type}</TableCell>
- <TableCell>
- <IconButton
- color="error"
- size="small"
- onClick={() => handleDeleteMapping(mapping.id)}
- >
- <Delete />
- </IconButton>
- </TableCell>
- </TableRow>
- ))
- )}
- </TableBody>
- </Table>
- </TableContainer>
- </Stack>
- </DialogContent>
- <DialogActions>
- <Button onClick={() => setOpenDialog(false)}>{t("Cancel")}</Button>
- </DialogActions>
- </Dialog>
-
- {/* Add Mapping Dialog */}
- <Dialog open={openAddDialog} onClose={() => setOpenAddDialog(false)} maxWidth="sm" fullWidth>
- <DialogTitle>{t("Add Mapping")}</DialogTitle>
- <DialogContent>
- <Stack spacing={2} sx={{ mt: 2 }}>
- <TextField
- label={t("Item Code")}
- value={itemCode}
- onChange={(e) => handleItemCodeChange(e.target.value)}
- error={!!itemCodeError}
- helperText={itemCodeError || (validatedItem ? `${validatedItem.code} - ${validatedItem.name}` : t("Enter item code to validate"))}
- fullWidth
- disabled={validatingItemCode}
- InputProps={{
- endAdornment: validatingItemCode ? <CircularProgress size={20} /> : null,
- }}
- />
- <TextField
- select
- label={t("Select Type")}
- value={selectedType}
- onChange={(e) => setSelectedType(e.target.value)}
- SelectProps={{
- native: true,
- }}
- fullWidth
- >
- {typeOptions.map((type) => (
- <option key={type} value={type}>
- {type}
- </option>
- ))}
- </TextField>
- </Stack>
- </DialogContent>
- <DialogActions>
- <Button onClick={() => setOpenAddDialog(false)}>{t("Cancel")}</Button>
- <Button
- variant="contained"
- onClick={handleSaveMapping}
- disabled={!validatedItem}
- >
- {t("Save")}
- </Button>
- </DialogActions>
- </Dialog>
- </Box>
- );
- };
-
- export default Tab0ItemQcCategoryMapping;
|