Reac front end for psicometric app

AgregarManual.js 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. import React, { memo } from 'react';
  2. import * as Yup from 'yup';
  3. import { useFormik, Form, FormikProvider } from 'formik';
  4. import toast from 'react-hot-toast';
  5. import { AdapterDateFns as DateFnsUtils } from '@mui/x-date-pickers/AdapterDateFns';
  6. import { DesktopDatePicker } from '@mui/x-date-pickers/DesktopDatePicker';
  7. import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
  8. import {
  9. Button, Stack, TextField, MenuItem, FormControl, InputLabel, Select,
  10. Backdrop, CircularProgress, Box, Divider,
  11. Tabs, Tab, FormGroup, Checkbox, FormControlLabel,
  12. Dialog, DialogContent, DialogTitle, DialogActions,
  13. DialogContentText,
  14. } from '@mui/material';
  15. import Autocomplete, { createFilterOptions } from '@mui/material/Autocomplete';
  16. import { Service } from '../../Utils/HTTP';
  17. import { useQuery, useMutation, useQueryClient } from 'react-query';
  18. import { TabPanel } from './TabPanel'
  19. import { useSelector } from 'react-redux';
  20. const filter = createFilterOptions();
  21. async function getPuestoSuperior(puesto, auth) {
  22. if (puesto.length < 2) return []
  23. let rest = new Service(`/plaza/keypuestosup?keyword=${puesto}`)
  24. let result = await rest.get(auth.token)
  25. // console.log(result)
  26. if (result?.data?.length > 0) {
  27. result = result.data.map((item) => ({ 'title': item.nombre, id: item.id }))
  28. return result;
  29. }
  30. return [];
  31. }
  32. async function savePuestoSuperior(input, auth) {
  33. let rest = new Service("/plaza/puestosuperior")
  34. let body = {
  35. "active": 1,
  36. "nombre": input.title,
  37. "decription": input.id,
  38. "modifyday": "2023-02-12T23:55:26.007",
  39. "createday": "2023-02-12T23:55:26.007",
  40. "id": null,
  41. "descripction": input.id,
  42. "modify_day": "2023-02-12T23:55:26.007",
  43. }
  44. let result = await rest.post(body, auth.token);
  45. let { id, nombre } = result;
  46. return { id, nombre }
  47. }
  48. function Manual(props) {
  49. const auth = useSelector((state) => state.token)
  50. const getCategories = async () => {
  51. let rest = new Service("/categoria/getAll")
  52. return await rest.getQuery(auth.token)
  53. }
  54. const getTest = async () => {
  55. let rest = new Service("/tests/getAll")
  56. return await rest.getQuery(auth.token)
  57. }
  58. const { data } = useQuery('categories', getCategories);
  59. const { data: testsCatalog } = useQuery('tests', getTest);
  60. const queryClient = useQueryClient();
  61. const NewPlazaSchema = Yup.object().shape({
  62. nombrepuesto: Yup.string().required('El nombre es requerido').min(5, "El nombre del puesto debe ser mayor a 5 caracteres").max(100),
  63. puestosuperior: Yup.number().required("El puesto superior es requerido"),
  64. aredepto: Yup.number().required('Escoge alguna área'),
  65. fecha: Yup.date("Ingresa una fecha válida"),
  66. notas: Yup.string("Ingresa una nota válida").min(5, "Ingresa una nota válida").max(150),
  67. tests: Yup.array()
  68. })
  69. const [departamento, setDepartamento] = React.useState('');
  70. const [open, setOpen] = React.useState(false);
  71. const [date, setDate] = React.useState(new Date());
  72. const [tab, setTab] = React.useState(0);
  73. const [checklist, setChecklist] = React.useState([]);
  74. const [openDialog, toggleOpenDialog] = React.useState(false);
  75. const [openSugg, setOpenSugg] = React.useState(false);
  76. const [options, setOptions] = React.useState([]);
  77. const [dialogValue, setDialogValueHook] = React.useState({
  78. title: '',
  79. id: '',
  80. });
  81. let setDialogValue = (value) => {
  82. // console.log('llamada', value)
  83. // setValues({...values, puestosuperior: value?.title })
  84. setDialogValueHook(value)
  85. }
  86. const loading = openSugg && options.length === 0;
  87. React.useEffect(() => {
  88. let active = true;
  89. if (!loading) {
  90. return undefined;
  91. }
  92. (async () => {
  93. let puestos = await getPuestoSuperior(dialogValue.title, auth)
  94. if (active) {
  95. setOptions(puestos);
  96. }
  97. })();
  98. return () => {
  99. active = false;
  100. };
  101. }, [loading, dialogValue, auth]);
  102. const handleClose = () => false
  103. const changeDepartamento = (event) => {
  104. setDepartamento(event.target.value);
  105. };
  106. const handleCloseDialog = () => {
  107. setDialogValue({
  108. title: '',
  109. id: '',
  110. });
  111. toggleOpenDialog(false);
  112. };
  113. const handleSubmitDialog = async (event) => {
  114. event.preventDefault();
  115. console.log('to save: ', dialogValue)
  116. let { id, nombre } = await savePuestoSuperior(dialogValue, auth)
  117. if (id) {
  118. setDialogValue({
  119. title: nombre,
  120. id: id,
  121. });
  122. }
  123. setDialogValue({
  124. title: dialogValue.title,
  125. id: dialogValue.id
  126. });
  127. handleCloseDialog();
  128. };
  129. const AutoCompleteChange = (event, newValue) => {
  130. console.log('newValue', newValue)
  131. setValues({ ...values, puestosuperior: newValue?.id })
  132. if (typeof newValue === 'string') {
  133. setTimeout(() => {
  134. toggleOpenDialog(true);
  135. setDialogValue({
  136. title: newValue,
  137. id: '',
  138. });
  139. });
  140. } else if (newValue && newValue.inputValue) {
  141. toggleOpenDialog(true);
  142. setDialogValue({
  143. title: newValue.inputValue,
  144. id: '',
  145. });
  146. } else {
  147. setDialogValue(newValue);
  148. }
  149. }
  150. const agregarPuesto = async (puesto) => {
  151. let rest = new Service('/plaza/save');
  152. return await rest.postQuery(puesto, auth.token);
  153. }
  154. const puestoMutation = useMutation(agregarPuesto)
  155. let { visible, onClose } = props
  156. const formik = useFormik({
  157. initialValues: {
  158. nombrepuesto: "",
  159. puestosuperior: null,
  160. aredepto: 1,
  161. fecha: date,
  162. notas: "",
  163. tests: []
  164. },
  165. onSubmit: (fields, { resetForm }) => {
  166. if (fields.tests.length === 0) {
  167. toast.error("Recuerda que seleccionar al menos un test")
  168. setTab(1)
  169. return
  170. }
  171. setOpen(true)
  172. fields['fecha'] = new Date(fields.fecha).toISOString();
  173. fields['areadeptoplz_id'] = 1;
  174. fields['id'] = -1;
  175. puestoMutation.mutate(fields, {
  176. onSuccess: () => {
  177. setOpen(false)
  178. resetForm();
  179. onClose();
  180. queryClient.invalidateQueries('puestos')
  181. toast.success("Puesto Agregado")
  182. },
  183. onError: () => {
  184. setOpen(false)
  185. toast.error("Ups!! Ocurrio un error, inténtalo más tarde")
  186. }
  187. })
  188. },
  189. validationSchema: NewPlazaSchema,
  190. });
  191. const changeTab = (_event, newValue) => setTab(newValue);
  192. const { errors, touched, handleSubmit, getFieldProps, values, setValues } = formik;
  193. // console.log({ values })
  194. const addPrueba = (check, id) => {
  195. let { tests } = values
  196. let temp;
  197. if (check) {
  198. temp = [...tests, { id }]
  199. } else {
  200. temp = tests.filter(test => test.id !== id);
  201. }
  202. setChecklist(temp.map(test => test.id))
  203. setValues({ ...values, tests: temp })
  204. };
  205. return (
  206. <Dialog
  207. open={visible}
  208. fullWidth={true}
  209. maxWidth="md"
  210. aria-labelledby="contained-modal-title-vcenter"
  211. onClose={onClose}>
  212. <DialogTitle className="modal-title" style={{ color: '#252525' }}>
  213. <button onClick={onClose} type="button" className="close" data-dismiss="modal">&times;</button>
  214. Agregar Puesto
  215. </DialogTitle>
  216. <DialogContent className="modal-body">
  217. <Tabs value={tab} onChange={changeTab} aria-label="basic tabs example">
  218. <Tab label="Información" />
  219. <Tab label="Pruebas" />
  220. </Tabs>
  221. <Dialog open={openDialog} onClose={handleCloseDialog}>
  222. <form onSubmit={handleSubmitDialog}>
  223. <DialogTitle>Agrega un nuevo Puesto</DialogTitle>
  224. <DialogContent>
  225. <DialogContentText>
  226. Agrega la descripcion del puesto
  227. </DialogContentText>
  228. <TextField
  229. autoFocus
  230. margin="dense"
  231. id="name"
  232. value={dialogValue?.title}
  233. onChange={(event) =>
  234. setDialogValue({
  235. ...dialogValue,
  236. title: event.target.value,
  237. })
  238. }
  239. label="Puesto"
  240. type="text"
  241. variant="standard"
  242. />
  243. <TextField
  244. margin="dense"
  245. id="name"
  246. value={dialogValue?.id}
  247. onChange={(event) =>
  248. setDialogValue({
  249. ...dialogValue,
  250. id: event.target.value,
  251. })
  252. }
  253. label="Descripción"
  254. type="text"
  255. variant="standard"
  256. />
  257. </DialogContent>
  258. <DialogActions>
  259. <Button onClick={handleCloseDialog}>Cancelar</Button>
  260. <Button type="submit">Agregar</Button>
  261. </DialogActions>
  262. </form>
  263. </Dialog>
  264. <FormikProvider style={{ paddingTop: 25 }} value={formik}>
  265. <Form autoComplete="off" noValidate onSubmit={handleSubmit}>
  266. <TabPanel value={tab} index={1}>
  267. <Stack spacing={1}>
  268. <Box style={{ paddingTop: 5, paddingLeft: 15 }}>
  269. <Divider />
  270. <h4 style={{ paddingTop: 10, paddingBottom: 10 }}>Selecciona los test a realizar</h4>
  271. <Divider />
  272. <FormGroup>
  273. {
  274. testsCatalog ?
  275. testsCatalog.data.map(test => (
  276. <FormControlLabel
  277. key={test.id}
  278. control={
  279. <Checkbox
  280. checked={checklist.includes((test.id))}
  281. onChange={(event) => addPrueba(event.target.checked, test.id)}
  282. />
  283. }
  284. label={test.nombre}
  285. />
  286. )) : null
  287. }
  288. </FormGroup>
  289. </Box>
  290. </Stack>
  291. </TabPanel>
  292. <TabPanel value={tab} index={0}>
  293. <Stack spacing={3}>
  294. <Stack direction={{ xs: 'column', sm: 'row' }} spacing={4}>
  295. <TextField
  296. label="Nombre"
  297. fullWidth
  298. {...getFieldProps('nombrepuesto')}
  299. helperText={touched.nombrepuesto && errors.nombrepuesto}
  300. error={Boolean(touched.nombrepuesto && errors.nombrepuesto)}
  301. />
  302. <FormControl fullWidth>
  303. <Autocomplete
  304. fullWidth
  305. value={dialogValue}
  306. onChange={AutoCompleteChange}
  307. open={openSugg}
  308. onOpen={() => {
  309. setOpenSugg(true);
  310. }}
  311. onClose={() => {
  312. setOpenSugg(false);
  313. }}
  314. isOptionEqualToValue={(option, value) => option.title === value.title}
  315. filterOptions={(options, params) => {
  316. const filtered = filter(options, params);
  317. if (params.inputValue !== '') {
  318. filtered.push({
  319. inputValue: params.inputValue,
  320. title: `Add "${params.inputValue}"`,
  321. });
  322. }
  323. return filtered;
  324. }}
  325. id="puesto_superior_autocomplete"
  326. options={options}
  327. loading={loading}
  328. getOptionLabel={(option) => {
  329. if (typeof option === 'string') {
  330. return option;
  331. }
  332. if (option.inputValue) {
  333. return option.inputValue;
  334. }
  335. return option.title;
  336. }}
  337. selectOnFocus
  338. clearOnBlur
  339. handleHomeEndKeys
  340. renderOption={(props, option) => <li {...props}>{option.title}</li>}
  341. freeSolo
  342. renderInput={(params) => (
  343. <TextField
  344. {...params}
  345. {...getFieldProps('puestosuperior')}
  346. error={Boolean(touched.puestosuperior && errors.puestosuperior)}
  347. label="Puesto Superior"
  348. InputProps={{
  349. ...params.InputProps,
  350. onChange: (event) => {
  351. // let title = event.target.value;
  352. // console.log('titulo',title)
  353. setOptions([]);
  354. setDialogValue({
  355. title: event.target.value,
  356. id: '',
  357. });
  358. },
  359. endAdornment: (
  360. <React.Fragment>
  361. {loading ? <CircularProgress color="inherit" size={20} /> : null}
  362. {params.InputProps.endAdornment}
  363. </React.Fragment>
  364. ),
  365. }}
  366. />
  367. )}
  368. />
  369. </FormControl>
  370. </Stack>
  371. <Stack direction={{ xs: 'column', sm: 'row' }} spacing={4}>
  372. <FormControl fullWidth>
  373. <InputLabel id="demo-simple-select-label">Departamento</InputLabel>
  374. <Select
  375. labelId="demo-simple-select-label"
  376. value={departamento}
  377. label="Departamento"
  378. onChange={changeDepartamento}
  379. {...getFieldProps('aredepto')}
  380. error={Boolean(touched.aredepto && errors.aredepto)} >
  381. {
  382. data ?
  383. data.data.map(cate => {
  384. return (
  385. <MenuItem key={cate.id} value={cate.id}>{cate.nombre}</MenuItem>
  386. )
  387. })
  388. : <MenuItem>Null</MenuItem>
  389. }
  390. </Select>
  391. </FormControl>
  392. <LocalizationProvider
  393. dateAdapter={DateFnsUtils}>
  394. <DesktopDatePicker
  395. label="Fecha Creación"
  396. fullWidth
  397. inputFormat="dd/MM/yyyy"
  398. {...getFieldProps('fecha')}
  399. value={date}
  400. onChange={setDate}
  401. renderInput={(params) =>
  402. <TextField
  403. disabled={true}
  404. label="Fecha Creación"
  405. fullWidth
  406. {...params}
  407. />}
  408. />
  409. </LocalizationProvider>
  410. </Stack>
  411. <Stack direction={{ xs: 'column', sm: 'row' }} spacing={4}>
  412. <TextField
  413. id="filled-multiline-static"
  414. multiline
  415. rows={4}
  416. variant="filled"
  417. label="Notas"
  418. fullWidth
  419. type="text"
  420. {...getFieldProps('notas')}
  421. error={Boolean(touched.notas && errors.notas)}
  422. helperText={touched.notas && errors.notas}
  423. />
  424. </Stack>
  425. </Stack>
  426. </TabPanel>
  427. <DialogActions>
  428. <Button
  429. type="submit"
  430. className="registerBtn"
  431. variant="contained"
  432. sx={{ mt: 1, mr: 1 }} >
  433. {'Guardar'}
  434. </Button>
  435. </DialogActions>
  436. </Form>
  437. </FormikProvider>
  438. </DialogContent>
  439. <Backdrop
  440. sx={{ color: '#fd4b4b', zIndex: (theme) => theme.zIndex.drawer + 1 }}
  441. open={open}
  442. onClick={handleClose} >
  443. <CircularProgress color="inherit" />
  444. </Backdrop>
  445. </Dialog>
  446. )
  447. }
  448. export default memo(Manual);