FPSMS-frontend
25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 

227 satır
6.9 KiB

  1. "use client";
  2. import React, { useCallback, useEffect, useMemo, useState } from "react";
  3. import SearchBox, { Criterion } from "../SearchBox";
  4. import { useTranslation } from "react-i18next";
  5. import SearchResults, { Column } from "../SearchResults";
  6. import EditNote from "@mui/icons-material/EditNote";
  7. import { fetchQcCategoriesForAll } from "@/app/api/settings/qcItemAll/actions";
  8. import { QcCategoryResult } from "@/app/api/settings/qcItemAll";
  9. import {
  10. deleteDialog,
  11. errorDialogWithContent,
  12. submitDialog,
  13. successDialog,
  14. } from "../Swal/CustomAlerts";
  15. import {
  16. deleteQcCategoryWithValidation,
  17. canDeleteQcCategory,
  18. saveQcCategoryWithValidation,
  19. SaveQcCategoryInputs,
  20. } from "@/app/api/settings/qcItemAll/actions";
  21. import Delete from "@mui/icons-material/Delete";
  22. import { Add } from "@mui/icons-material";
  23. import { Button, Dialog, DialogActions, DialogContent, DialogTitle, Stack } from "@mui/material";
  24. import QcCategoryDetails from "../QcCategorySave/QcCategoryDetails";
  25. import { FormProvider, useForm } from "react-hook-form";
  26. type SearchQuery = Partial<Omit<QcCategoryResult, "id">>;
  27. type SearchParamNames = keyof SearchQuery;
  28. const Tab2QcCategoryManagement: React.FC = () => {
  29. const { t } = useTranslation("qcItemAll");
  30. const [qcCategories, setQcCategories] = useState<QcCategoryResult[]>([]);
  31. const [filteredQcCategories, setFilteredQcCategories] = useState<QcCategoryResult[]>([]);
  32. const [openDialog, setOpenDialog] = useState(false);
  33. const [editingCategory, setEditingCategory] = useState<QcCategoryResult | null>(null);
  34. useEffect(() => {
  35. loadCategories();
  36. }, []);
  37. const loadCategories = async () => {
  38. const categories = await fetchQcCategoriesForAll();
  39. setQcCategories(categories);
  40. setFilteredQcCategories(categories);
  41. };
  42. const formProps = useForm<SaveQcCategoryInputs>({
  43. defaultValues: {
  44. code: "",
  45. name: "",
  46. description: "",
  47. },
  48. });
  49. const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
  50. () => [
  51. { label: t("Code"), paramName: "code", type: "text" },
  52. { label: t("Name"), paramName: "name", type: "text" },
  53. ],
  54. [t]
  55. );
  56. const onReset = useCallback(() => {
  57. setFilteredQcCategories(qcCategories);
  58. }, [qcCategories]);
  59. const handleEdit = useCallback((qcCategory: QcCategoryResult) => {
  60. setEditingCategory(qcCategory);
  61. formProps.reset({
  62. id: qcCategory.id,
  63. code: qcCategory.code,
  64. name: qcCategory.name,
  65. description: qcCategory.description || "",
  66. });
  67. setOpenDialog(true);
  68. }, [formProps]);
  69. const handleAdd = useCallback(() => {
  70. setEditingCategory(null);
  71. formProps.reset({
  72. code: "",
  73. name: "",
  74. description: "",
  75. });
  76. setOpenDialog(true);
  77. }, [formProps]);
  78. const handleSubmit = useCallback(async (data: SaveQcCategoryInputs) => {
  79. await submitDialog(async () => {
  80. try {
  81. const response = await saveQcCategoryWithValidation(data);
  82. if (response.errors) {
  83. let errorContents = "";
  84. for (const [key, value] of Object.entries(response.errors)) {
  85. formProps.setError(key as keyof SaveQcCategoryInputs, {
  86. type: "custom",
  87. message: value,
  88. });
  89. errorContents = errorContents + t(value) + "<br>";
  90. }
  91. errorDialogWithContent(t("Submit Error"), errorContents, t);
  92. } else {
  93. await successDialog(t("Submit Success"), t);
  94. setOpenDialog(false);
  95. await loadCategories();
  96. }
  97. } catch (error) {
  98. errorDialogWithContent(t("Submit Error"), String(error), t);
  99. }
  100. }, t);
  101. }, [formProps, t]);
  102. const handleDelete = useCallback(async (qcCategory: QcCategoryResult) => {
  103. // Check if can delete first
  104. const canDelete = await canDeleteQcCategory(qcCategory.id); // This is a server action, token handled server-side
  105. if (!canDelete) {
  106. errorDialogWithContent(
  107. t("Cannot Delete"),
  108. t("Cannot delete QcCategory. It has {itemCount} item(s) and {qcItemCount} qc item(s) linked to it.").replace("{itemCount}", "some").replace("{qcItemCount}", "some"),
  109. t
  110. );
  111. return;
  112. }
  113. deleteDialog(async () => {
  114. try {
  115. const response = await deleteQcCategoryWithValidation(qcCategory.id);
  116. if (!response.success || !response.canDelete) {
  117. errorDialogWithContent(
  118. t("Delete Error"),
  119. response.message || t("Cannot Delete"),
  120. t
  121. );
  122. } else {
  123. await successDialog(t("Delete Success"), t);
  124. await loadCategories();
  125. }
  126. } catch (error) {
  127. errorDialogWithContent(t("Delete Error"), String(error), t);
  128. }
  129. }, t);
  130. }, [t]);
  131. const columnWidthSx = (width = "10%") => {
  132. return { width: width, whiteSpace: "nowrap" };
  133. };
  134. const columns = useMemo<Column<QcCategoryResult>[]>(
  135. () => [
  136. {
  137. name: "id",
  138. label: t("Details"),
  139. onClick: handleEdit,
  140. buttonIcon: <EditNote />,
  141. sx: columnWidthSx("5%"),
  142. },
  143. { name: "code", label: t("Code"), sx: columnWidthSx("15%") },
  144. { name: "name", label: t("Name"), sx: columnWidthSx("30%") },
  145. {
  146. name: "id",
  147. label: t("Delete"),
  148. onClick: handleDelete,
  149. buttonIcon: <Delete />,
  150. buttonColor: "error",
  151. sx: columnWidthSx("5%"),
  152. },
  153. ],
  154. [t, handleEdit, handleDelete]
  155. );
  156. return (
  157. <>
  158. <Stack direction="row" justifyContent="flex-end" sx={{ mb: 2 }}>
  159. <Button
  160. variant="contained"
  161. startIcon={<Add />}
  162. onClick={handleAdd}
  163. >
  164. {t("Create Qc Category")}
  165. </Button>
  166. </Stack>
  167. <SearchBox
  168. criteria={searchCriteria}
  169. onSearch={(query) => {
  170. setFilteredQcCategories(
  171. qcCategories.filter(
  172. (qc) =>
  173. (!query.code || qc.code.toLowerCase().includes(query.code.toLowerCase())) &&
  174. (!query.name || qc.name.toLowerCase().includes(query.name.toLowerCase()))
  175. )
  176. );
  177. }}
  178. onReset={onReset}
  179. />
  180. <SearchResults<QcCategoryResult>
  181. items={filteredQcCategories}
  182. columns={columns}
  183. />
  184. {/* Add/Edit Dialog */}
  185. <Dialog open={openDialog} onClose={() => setOpenDialog(false)} maxWidth="md" fullWidth>
  186. <DialogTitle>
  187. {editingCategory ? t("Edit Qc Category") : t("Create Qc Category")}
  188. </DialogTitle>
  189. <FormProvider {...formProps}>
  190. <form onSubmit={formProps.handleSubmit(handleSubmit)}>
  191. <DialogContent>
  192. <QcCategoryDetails />
  193. </DialogContent>
  194. <DialogActions>
  195. <Button onClick={() => setOpenDialog(false)}>{t("Cancel")}</Button>
  196. <Button type="submit" variant="contained">
  197. {t("Submit")}
  198. </Button>
  199. </DialogActions>
  200. </form>
  201. </FormProvider>
  202. </Dialog>
  203. </>
  204. );
  205. };
  206. export default Tab2QcCategoryManagement;