Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 

208 linhas
5.7 KiB

  1. "use client";
  2. import React, { useCallback, useEffect, useMemo, useState } from "react";
  3. import { useTranslation } from "react-i18next";
  4. import {
  5. FieldErrors,
  6. FormProvider,
  7. SubmitErrorHandler,
  8. SubmitHandler,
  9. useForm,
  10. useFormContext,
  11. } from "react-hook-form";
  12. import {
  13. Box,
  14. Card,
  15. CardContent,
  16. Grid,
  17. IconButton,
  18. InputAdornment,
  19. Stack,
  20. Tab,
  21. Tabs,
  22. TabsProps,
  23. TextField,
  24. Typography,
  25. } from "@mui/material";
  26. import { differenceBy } from "lodash";
  27. import { CreateGroupInputs, auth } from "@/app/api/group/actions";
  28. import SearchResults, { Column } from "../SearchResults";
  29. import { Add, Clear, Remove, Search } from "@mui/icons-material";
  30. import { UserResult } from "@/app/api/user";
  31. export interface Props {
  32. users: UserResult[];
  33. }
  34. const UserAllocation: React.FC<Props> = ({ users }) => {
  35. console.log(users)
  36. const { t } = useTranslation();
  37. const {
  38. setValue,
  39. getValues,
  40. formState: { defaultValues },
  41. reset,
  42. resetField,
  43. } = useFormContext<CreateGroupInputs>();
  44. const initialUsers = users.map((u) => ({ ...u })).sort((a, b) => a.id - b.id).filter((u) => u.groupId === null);
  45. const [filteredUsers, setFilteredUsers] = useState(initialUsers);
  46. const [selectedUsers, setSelectedUsers] = useState<typeof filteredUsers>(
  47. () => {
  48. return filteredUsers.filter(
  49. (s) => getValues("addUserIds")?.includes(s.id)
  50. );
  51. }
  52. );
  53. // Adding / Removing Auth
  54. const addUser = useCallback((users: UserResult) => {
  55. setSelectedUsers((a) => [...a, users]);
  56. }, []);
  57. const removeUser = useCallback((users: UserResult) => {
  58. setSelectedUsers((a) => a.filter((a) => a.id !== users.id));
  59. }, []);
  60. const clearUser = useCallback(() => {
  61. if (defaultValues !== undefined) {
  62. resetField("addUserIds");
  63. setSelectedUsers(
  64. initialUsers.filter((s) => defaultValues.addUserIds?.includes(s.id))
  65. );
  66. }
  67. }, [defaultValues]);
  68. // Sync with form
  69. useEffect(() => {
  70. setValue(
  71. "addUserIds",
  72. selectedUsers.map((u) => u.id)
  73. );
  74. }, [selectedUsers, setValue]);
  75. const UserPoolColumns = useMemo<Column<UserResult>[]>(
  76. () => [
  77. {
  78. label: t("Add"),
  79. name: "id",
  80. onClick: addUser,
  81. buttonIcon: <Add />,
  82. },
  83. { label: t("Username"), name: "username" },
  84. { label: t("Staff Name"), name: "name" },
  85. ],
  86. [addUser, t]
  87. );
  88. const allocatedUserColumns = useMemo<Column<UserResult>[]>(
  89. () => [
  90. {
  91. label: t("Remove"),
  92. name: "id",
  93. onClick: removeUser,
  94. buttonIcon: <Remove color="warning" />,
  95. },
  96. { label: t("Username"), name: "username" },
  97. { label: t("Staff Name"), name: "name" },
  98. ],
  99. [removeUser, selectedUsers, t]
  100. );
  101. const [query, setQuery] = React.useState("");
  102. const onQueryInputChange = React.useCallback<
  103. React.ChangeEventHandler<HTMLInputElement>
  104. >((e) => {
  105. setQuery(e.target.value);
  106. }, []);
  107. const clearQueryInput = React.useCallback(() => {
  108. setQuery("");
  109. }, []);
  110. React.useEffect(() => {
  111. const q = query.toLowerCase();
  112. setFilteredUsers(
  113. initialUsers.filter((u) => (
  114. u.username.toLowerCase().includes(q) ||
  115. u.name.toLowerCase().includes(q)
  116. ))
  117. );
  118. }, [users, query]);
  119. const resetUser = React.useCallback(() => {
  120. clearQueryInput();
  121. clearUser();
  122. }, [clearQueryInput, clearUser]);
  123. const formProps = useForm({});
  124. // Tab related
  125. const [tabIndex, setTabIndex] = React.useState(0);
  126. const handleTabChange = React.useCallback<NonNullable<TabsProps["onChange"]>>(
  127. (_e, newValue) => {
  128. setTabIndex(newValue);
  129. },
  130. []
  131. );
  132. return (
  133. <>
  134. <FormProvider {...formProps}>
  135. <Card sx={{ display: "block" }}>
  136. <CardContent
  137. sx={{ display: "flex", flexDirection: "column", gap: 1 }}
  138. >
  139. <Stack gap={2}>
  140. <Typography variant="overline" display="block">
  141. {/* {t("User")} */}
  142. </Typography>
  143. <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}>
  144. <Grid item xs={6} display="flex" alignItems="center">
  145. <Search sx={{ marginInlineEnd: 1 }} />
  146. <TextField
  147. variant="standard"
  148. fullWidth
  149. onChange={onQueryInputChange}
  150. value={query}
  151. placeholder={t("Search by ") + t("Username") + " / " + t("Staff Name")}
  152. InputProps={{
  153. endAdornment: query && (
  154. <InputAdornment position="end">
  155. <IconButton onClick={clearQueryInput}>
  156. <Clear />
  157. </IconButton>
  158. </InputAdornment>
  159. ),
  160. }}
  161. />
  162. </Grid>
  163. </Grid>
  164. <Tabs value={tabIndex} onChange={handleTabChange}>
  165. <Tab label={t("User Pool")} />
  166. <Tab
  167. label={`${t("Allocated Users")} (${selectedUsers.length})`}
  168. />
  169. </Tabs>
  170. <Box sx={{ marginInline: -3 }}>
  171. {tabIndex === 0 && (
  172. <SearchResults
  173. noWrapper
  174. items={differenceBy(filteredUsers, selectedUsers, "id")}
  175. columns={UserPoolColumns}
  176. />
  177. )}
  178. {tabIndex === 1 && (
  179. <SearchResults
  180. noWrapper
  181. items={selectedUsers}
  182. columns={allocatedUserColumns}
  183. />
  184. )}
  185. </Box>
  186. </Stack>
  187. </CardContent>
  188. </Card>
  189. </FormProvider>
  190. </>
  191. );
  192. };
  193. export default UserAllocation;