You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

271 rivejä
8.0 KiB

  1. "use client";
  2. import React, { useCallback, useEffect, useMemo, useState } from "react";
  3. import CustomInputForm from "../CustomInputForm";
  4. import { useRouter, useSearchParams } from "next/navigation";
  5. import { useTranslation } from "react-i18next";
  6. import {
  7. FieldErrors,
  8. FormProvider,
  9. SubmitErrorHandler,
  10. SubmitHandler,
  11. useForm,
  12. useFormContext,
  13. } from "react-hook-form";
  14. import { CreateTeamInputs } from "@/app/api/team/actions";
  15. import { Staff4TransferList, fetchStaffCombo } from "@/app/api/staff/actions";
  16. import { StaffResult, StaffTeamTable } from "@/app/api/staff";
  17. import SearchResults, { Column } from "../SearchResults";
  18. import { Clear, PersonAdd, PersonRemove, Search } from "@mui/icons-material";
  19. import { Card } from "reactstrap";
  20. import {
  21. Box,
  22. CardContent,
  23. Grid,
  24. IconButton,
  25. InputAdornment,
  26. Stack,
  27. Tab,
  28. Tabs,
  29. TabsProps,
  30. TextField,
  31. Typography,
  32. } from "@mui/material";
  33. import { differenceBy } from "lodash";
  34. import StarsIcon from "@mui/icons-material/Stars";
  35. export interface Props {
  36. allStaffs: StaffResult[];
  37. teamLead: number;
  38. }
  39. const Allocation: React.FC<Props> = ({ allStaffs: staff, teamLead }) => {
  40. const { t } = useTranslation();
  41. const searchParams = useSearchParams();
  42. const idString = searchParams.get("id");
  43. const {
  44. setValue,
  45. getValues,
  46. formState: { defaultValues },
  47. reset,
  48. resetField,
  49. } = useFormContext<CreateTeamInputs>();
  50. // let firstFilter: StaffResult[] = []
  51. const initialStaffs = staff.map((s) => ({ ...s }));
  52. const [filteredStaff, setFilteredStaff] = useState(initialStaffs);
  53. const [selectedStaff, setSelectedStaff] = useState<typeof filteredStaff>(() => {
  54. const rearrangedStaff = filteredStaff.sort((a, b) => {
  55. if (a.id === teamLead) return -1;
  56. if (b.id === teamLead) return 1;
  57. return 0;
  58. });
  59. return rearrangedStaff.filter((s) => getValues("addStaffIds")?.includes(s.id))
  60. }
  61. );
  62. const [seletedTeamLead, setSeletedTeamLead] = useState<number>();
  63. const [deletedStaffIds, setDeletedStaffIds] = useState<number[]>([]);
  64. // Adding / Removing staff
  65. const addStaff = useCallback((staff: StaffResult) => {
  66. setSelectedStaff((s) => [...s, staff]);
  67. // setDeletedStaffIds((s) => s.filter((s) => s === selectedStaff.id))
  68. }, []);
  69. const removeStaff = useCallback((staff: StaffResult) => {
  70. setSelectedStaff((s) => s.filter((s) => s.id !== staff.id));
  71. // setDeletedStaffIds((s) => s)
  72. setDeletedStaffIds((prevIds) => [...prevIds, staff.id]);
  73. }, []);
  74. const setTeamLead = useCallback(
  75. (staff: StaffResult) => {
  76. setSeletedTeamLead(staff.id);
  77. const rearrangedList = getValues("addStaffIds").reduce<number[]>(
  78. (acc, num, index) => {
  79. if (num === staff.id && index !== 0) {
  80. acc.splice(index, 1);
  81. acc.unshift(num);
  82. }
  83. return acc;
  84. },
  85. getValues("addStaffIds")
  86. );
  87. // console.log(rearrangedList);
  88. // console.log(selectedStaff);
  89. const rearrangedStaff = rearrangedList.map((id) => {
  90. return selectedStaff.find((staff) => staff.id === id);
  91. });
  92. console.log(rearrangedStaff);
  93. setSelectedStaff(rearrangedStaff as StaffResult[]);
  94. setValue("addStaffIds", rearrangedList);
  95. },
  96. [addStaff, selectedStaff]
  97. );
  98. const clearSubsidiary = useCallback(() => {
  99. if (defaultValues !== undefined) {
  100. resetField("addStaffIds");
  101. setSelectedStaff(
  102. initialStaffs.filter((s) => defaultValues.addStaffIds?.includes(s.id))
  103. );
  104. }
  105. }, [defaultValues]);
  106. // Sync with form
  107. useEffect(() => {
  108. setValue(
  109. "addStaffIds",
  110. selectedStaff.map((s) => s.id)
  111. );
  112. }, [selectedStaff, setValue]);
  113. useEffect(() => {
  114. setValue("deleteStaffIds", deletedStaffIds)
  115. console.log(deletedStaffIds)
  116. }, [deletedStaffIds]);
  117. const StaffPoolColumns = useMemo<Column<StaffResult>[]>(
  118. () => [
  119. {
  120. label: t("Add"),
  121. name: "id",
  122. onClick: addStaff,
  123. buttonIcon: <PersonAdd />,
  124. },
  125. { label: t("Staff Id"), name: "staffId" },
  126. { label: t("Staff Name"), name: "name" },
  127. { label: t("Current Position"), name: "currentPosition" },
  128. ],
  129. [addStaff, t]
  130. );
  131. const allocatedStaffColumns = useMemo<Column<StaffResult>[]>(
  132. () => [
  133. {
  134. label: t("Remove"),
  135. name: "action",
  136. onClick: removeStaff,
  137. buttonIcon: <PersonRemove />,
  138. },
  139. { label: t("Staff Id"), name: "staffId" },
  140. { label: t("Staff Name"), name: "name" },
  141. { label: t("Current Position"), name: "currentPosition" },
  142. {
  143. label: t("Team Lead"),
  144. name: "action",
  145. onClick: setTeamLead,
  146. buttonIcon: <StarsIcon />,
  147. },
  148. ],
  149. [removeStaff, selectedStaff, t]
  150. );
  151. const [query, setQuery] = React.useState("");
  152. const onQueryInputChange = React.useCallback<
  153. React.ChangeEventHandler<HTMLInputElement>
  154. >((e) => {
  155. setQuery(e.target.value);
  156. }, []);
  157. const clearQueryInput = React.useCallback(() => {
  158. setQuery("");
  159. }, []);
  160. React.useEffect(() => {
  161. // setFilteredStaff(
  162. // initialStaffs.filter((s) => {
  163. // const q = query.toLowerCase();
  164. // // s.staffId.toLowerCase().includes(q)
  165. // // const q = query.toLowerCase();
  166. // // return s.name.toLowerCase().includes(q);
  167. // // s.code.toString().includes(q) ||
  168. // // (s.brNo != null && s.brNo.toLowerCase().includes(q))
  169. // })
  170. // );
  171. }, [staff, query]);
  172. useEffect(() => {
  173. // console.log(getValues("addStaffIds"))
  174. }, [initialStaffs]);
  175. const resetStaff = React.useCallback(() => {
  176. clearQueryInput();
  177. clearSubsidiary();
  178. }, [clearQueryInput, clearSubsidiary]);
  179. const formProps = useForm({});
  180. // Tab related
  181. const [tabIndex, setTabIndex] = React.useState(0);
  182. const handleTabChange = React.useCallback<NonNullable<TabsProps["onChange"]>>(
  183. (_e, newValue) => {
  184. setTabIndex(newValue);
  185. },
  186. []
  187. );
  188. return (
  189. <>
  190. <FormProvider {...formProps}>
  191. <Card sx={{ display: "block" }}>
  192. <CardContent
  193. sx={{ display: "flex", flexDirection: "column", gap: 1 }}
  194. >
  195. <Stack gap={2}>
  196. <Typography variant="overline" display="block">
  197. {t("staff")}
  198. </Typography>
  199. <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
  200. <Grid item xs={6} display="flex" alignItems="center">
  201. <Search sx={{ marginInlineEnd: 1 }} />
  202. <TextField
  203. variant="standard"
  204. fullWidth
  205. onChange={onQueryInputChange}
  206. value={query}
  207. placeholder={t("Search by staff ID, name or position.")}
  208. InputProps={{
  209. endAdornment: query && (
  210. <InputAdornment position="end">
  211. <IconButton onClick={clearQueryInput}>
  212. <Clear />
  213. </IconButton>
  214. </InputAdornment>
  215. ),
  216. }}
  217. />
  218. </Grid>
  219. </Grid>
  220. <Tabs value={tabIndex} onChange={handleTabChange}>
  221. <Tab label={t("Staff Pool")} />
  222. <Tab
  223. label={`${t("Allocated Staff")} (${selectedStaff.length})`}
  224. />
  225. </Tabs>
  226. <Box sx={{ marginInline: -3 }}>
  227. {tabIndex === 0 && (
  228. <SearchResults
  229. noWrapper
  230. items={differenceBy(filteredStaff, selectedStaff, "id")}
  231. columns={StaffPoolColumns}
  232. />
  233. )}
  234. {tabIndex === 1 && (
  235. <SearchResults
  236. noWrapper
  237. items={selectedStaff}
  238. columns={allocatedStaffColumns}
  239. />
  240. )}
  241. </Box>
  242. </Stack>
  243. </CardContent>
  244. </Card>
  245. </FormProvider>
  246. </>
  247. );
  248. };
  249. export default Allocation;