Reac front end for psicometric app

AgregarManual.js 17KB

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