index.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. import React, { useState, useEffect } from "react";
  2. import {
  3. makeStyles,
  4. Paper,
  5. Grid,
  6. TextField,
  7. Table,
  8. TableHead,
  9. TableBody,
  10. TableCell,
  11. TableRow,
  12. IconButton,
  13. FormControl,
  14. InputLabel,
  15. MenuItem,
  16. Select
  17. } from "@material-ui/core";
  18. import { Formik, Form, Field } from 'formik';
  19. import ButtonWithSpinner from "../ButtonWithSpinner";
  20. import ConfirmationModal from "../ConfirmationModal";
  21. import { Edit as EditIcon } from "@material-ui/icons";
  22. import { toast } from "react-toastify";
  23. import usePlans from "../../hooks/usePlans";
  24. import { i18n } from "../../translate/i18n";
  25. const useStyles = makeStyles(theme => ({
  26. root: {
  27. width: '100%'
  28. },
  29. mainPaper: {
  30. width: '100%',
  31. flex: 1,
  32. padding: theme.spacing(2)
  33. },
  34. fullWidth: {
  35. width: '100%'
  36. },
  37. tableContainer: {
  38. width: '100%',
  39. overflowX: "scroll",
  40. ...theme.scrollbarStyles
  41. },
  42. textfield: {
  43. width: '100%'
  44. },
  45. textRight: {
  46. textAlign: 'right'
  47. },
  48. row: {
  49. paddingTop: theme.spacing(2),
  50. paddingBottom: theme.spacing(2)
  51. },
  52. control: {
  53. paddingRight: theme.spacing(1),
  54. paddingLeft: theme.spacing(1)
  55. },
  56. buttonContainer: {
  57. textAlign: 'right',
  58. padding: theme.spacing(1)
  59. }
  60. }));
  61. export function PlanManagerForm(props) {
  62. const { onSubmit, onDelete, onCancel, initialValue, loading } = props;
  63. const classes = useStyles()
  64. const [record, setRecord] = useState({
  65. name: '',
  66. users: 0,
  67. connections: 0,
  68. queues: 0,
  69. value: 0,
  70. useCampaigns: true,
  71. useSchedules: true,
  72. useInternalChat: true,
  73. useExternalApi: true,
  74. useKanban: true,
  75. useOpenAi: true,
  76. useIntegrations: true,
  77. });
  78. useEffect(() => {
  79. setRecord(initialValue)
  80. }, [initialValue])
  81. const handleSubmit = async (data) => {
  82. onSubmit(data)
  83. }
  84. return (
  85. <Formik
  86. enableReinitialize
  87. className={classes.fullWidth}
  88. initialValues={record}
  89. onSubmit={(values, { resetForm }) =>
  90. setTimeout(() => {
  91. handleSubmit(values)
  92. resetForm()
  93. }, 500)
  94. }
  95. >
  96. {(values) => (
  97. <Form className={classes.fullWidth}>
  98. <Grid spacing={1} justifyContent="flex-start" container>
  99. {/* NOME */}
  100. <Grid xs={12} sm={6} md={2} item>
  101. <Field
  102. as={TextField}
  103. label={i18n.t("plans.form.name")}
  104. name="name"
  105. variant="outlined"
  106. className={classes.fullWidth}
  107. margin="dense"
  108. />
  109. </Grid>
  110. {/* USUARIOS */}
  111. <Grid xs={12} sm={6} md={1} item>
  112. <Field
  113. as={TextField}
  114. label={i18n.t("plans.form.users")}
  115. name="users"
  116. variant="outlined"
  117. className={classes.fullWidth}
  118. margin="dense"
  119. type="number"
  120. />
  121. </Grid>
  122. {/* CONEXOES */}
  123. <Grid xs={12} sm={6} md={1} item>
  124. <Field
  125. as={TextField}
  126. label={i18n.t("plans.form.connections")}
  127. name="connections"
  128. variant="outlined"
  129. className={classes.fullWidth}
  130. margin="dense"
  131. type="number"
  132. />
  133. </Grid>
  134. {/* FILAS */}
  135. <Grid xs={12} sm={6} md={1} item>
  136. <Field
  137. as={TextField}
  138. label={i18n.t("plans.form.queues")}
  139. name="queues"
  140. variant="outlined"
  141. className={classes.fullWidth}
  142. margin="dense"
  143. type="number"
  144. />
  145. </Grid>
  146. {/* VALOR */}
  147. <Grid xs={12} sm={6} md={1} item>
  148. <Field
  149. as={TextField}
  150. label={i18n.t("plans.form.value")}
  151. name="value"
  152. variant="outlined"
  153. className={classes.fullWidth}
  154. margin="dense"
  155. type="text"
  156. />
  157. </Grid>
  158. {/* CAMPANHAS */}
  159. <Grid xs={12} sm={6} md={2} item>
  160. <FormControl margin="dense" variant="outlined" fullWidth>
  161. <InputLabel htmlFor="useCampaigns-selection">{i18n.t("plans.form.campaigns")}</InputLabel>
  162. <Field
  163. as={Select}
  164. id="useCampaigns-selection"
  165. label={i18n.t("plans.form.campaigns")}
  166. labelId="useCampaigns-selection-label"
  167. name="useCampaigns"
  168. margin="dense"
  169. >
  170. <MenuItem value={true}>{i18n.t("plans.form.enabled")}</MenuItem>
  171. <MenuItem value={false}>{i18n.t("plans.form.disabled")}</MenuItem>
  172. </Field>
  173. </FormControl>
  174. </Grid>
  175. {/* AGENDAMENTOS */}
  176. <Grid xs={12} sm={8} md={2} item>
  177. <FormControl margin="dense" variant="outlined" fullWidth>
  178. <InputLabel htmlFor="useSchedules-selection">{i18n.t("plans.form.schedules")}</InputLabel>
  179. <Field
  180. as={Select}
  181. id="useSchedules-selection"
  182. label={i18n.t("plans.form.schedules")}
  183. labelId="useSchedules-selection-label"
  184. name="useSchedules"
  185. margin="dense"
  186. >
  187. <MenuItem value={true}>{i18n.t("plans.form.enabled")}</MenuItem>
  188. <MenuItem value={false}>{i18n.t("plans.form.disabled")}</MenuItem>
  189. </Field>
  190. </FormControl>
  191. </Grid>
  192. {/* CHAT INTERNO */}
  193. <Grid xs={12} sm={8} md={2} item>
  194. <FormControl margin="dense" variant="outlined" fullWidth>
  195. <InputLabel htmlFor="useInternalChat-selection">{i18n.t("plans.form.internalChat")}</InputLabel>
  196. <Field
  197. as={Select}
  198. id="useInternalChat-selection"
  199. label={i18n.t("plans.form.internalChat")}
  200. labelId="useInternalChat-selection-label"
  201. name="useInternalChat"
  202. margin="dense"
  203. >
  204. <MenuItem value={true}>{i18n.t("plans.form.enabled")}</MenuItem>
  205. <MenuItem value={false}>{i18n.t("plans.form.disabled")}</MenuItem>
  206. </Field>
  207. </FormControl>
  208. </Grid>
  209. {/* API Externa */}
  210. <Grid xs={12} sm={8} md={4} item>
  211. <FormControl margin="dense" variant="outlined" fullWidth>
  212. <InputLabel htmlFor="useExternalApi-selection">{i18n.t("plans.form.externalApi")}</InputLabel>
  213. <Field
  214. as={Select}
  215. id="useExternalApi-selection"
  216. label={i18n.t("plans.form.externalApi")}
  217. labelId="useExternalApi-selection-label"
  218. name="useExternalApi"
  219. margin="dense"
  220. >
  221. <MenuItem value={true}>{i18n.t("plans.form.enabled")}</MenuItem>
  222. <MenuItem value={false}>{i18n.t("plans.form.disabled")}</MenuItem>
  223. </Field>
  224. </FormControl>
  225. </Grid>
  226. {/* KANBAN */}
  227. <Grid xs={12} sm={8} md={2} item>
  228. <FormControl margin="dense" variant="outlined" fullWidth>
  229. <InputLabel htmlFor="useKanban-selection">{i18n.t("plans.form.kanban")}</InputLabel>
  230. <Field
  231. as={Select}
  232. id="useKanban-selection"
  233. label={i18n.t("plans.form.kanban")}
  234. labelId="useKanban-selection-label"
  235. name="useKanban"
  236. margin="dense"
  237. >
  238. <MenuItem value={true}>{i18n.t("plans.form.enabled")}</MenuItem>
  239. <MenuItem value={false}>{i18n.t("plans.form.disabled")}</MenuItem>
  240. </Field>
  241. </FormControl>
  242. </Grid>
  243. {/* OPENAI */}
  244. <Grid xs={12} sm={8} md={2} item>
  245. <FormControl margin="dense" variant="outlined" fullWidth>
  246. <InputLabel htmlFor="useOpenAi-selection">Open.Ai</InputLabel>
  247. <Field
  248. as={Select}
  249. id="useOpenAi-selection"
  250. label="Talk.Ai"
  251. labelId="useOpenAi-selection-label"
  252. name="useOpenAi"
  253. margin="dense"
  254. >
  255. <MenuItem value={true}>{i18n.t("plans.form.enabled")}</MenuItem>
  256. <MenuItem value={false}>{i18n.t("plans.form.disabled")}</MenuItem>
  257. </Field>
  258. </FormControl>
  259. </Grid>
  260. {/* INTEGRACOES */}
  261. <Grid xs={12} sm={8} md={2} item>
  262. <FormControl margin="dense" variant="outlined" fullWidth>
  263. <InputLabel htmlFor="useIntegrations-selection">
  264. {i18n.t("plans.form.integrations")}
  265. </InputLabel>
  266. <Field
  267. as={Select}
  268. id="useIntegrations-selection"
  269. label={i18n.t("plans.form.integrations")}
  270. labelId="useIntegrations-selection-label"
  271. name="useIntegrations"
  272. margin="dense"
  273. >
  274. <MenuItem value={true}>{i18n.t("plans.form.enabled")}</MenuItem>
  275. <MenuItem value={false}>{i18n.t("plans.form.disabled")}</MenuItem>
  276. </Field>
  277. </FormControl>
  278. </Grid>
  279. </Grid>
  280. <Grid spacing={2} justifyContent="flex-end" container>
  281. <Grid sm={3} md={2} item>
  282. <ButtonWithSpinner className={classes.fullWidth} loading={loading} onClick={() => onCancel()} variant="contained">
  283. {i18n.t("plans.form.clear")}
  284. </ButtonWithSpinner>
  285. </Grid>
  286. {record.id !== undefined ? (
  287. <Grid sm={3} md={2} item>
  288. <ButtonWithSpinner className={classes.fullWidth} loading={loading} onClick={() => onDelete(record)} variant="contained" color="secondary">
  289. {i18n.t("plans.form.delete")}
  290. </ButtonWithSpinner>
  291. </Grid>
  292. ) : null}
  293. <Grid sm={3} md={2} item>
  294. <ButtonWithSpinner className={classes.fullWidth} loading={loading} type="submit" variant="contained" color="primary">
  295. {i18n.t("plans.form.save")}
  296. </ButtonWithSpinner>
  297. </Grid>
  298. </Grid>
  299. </Form>
  300. )}
  301. </Formik>
  302. )
  303. }
  304. export function PlansManagerGrid(props) {
  305. const { records, onSelect } = props
  306. const classes = useStyles()
  307. const renderCampaigns = (row) => {
  308. return row.useCampaigns === false ? `${i18n.t("plans.form.no")}` : `${i18n.t("plans.form.yes")}`;
  309. };
  310. const renderSchedules = (row) => {
  311. return row.useSchedules === false ? `${i18n.t("plans.form.no")}` : `${i18n.t("plans.form.yes")}`;
  312. };
  313. const renderInternalChat = (row) => {
  314. return row.useInternalChat === false ? `${i18n.t("plans.form.no")}` : `${i18n.t("plans.form.yes")}`;
  315. };
  316. const renderExternalApi = (row) => {
  317. return row.useExternalApi === false ? `${i18n.t("plans.form.no")}` : `${i18n.t("plans.form.yes")}`;
  318. };
  319. const renderKanban = (row) => {
  320. return row.useKanban === false ? `${i18n.t("plans.form.no")}` : `${i18n.t("plans.form.yes")}`;
  321. };
  322. const renderOpenAi = (row) => {
  323. return row.useOpenAi === false ? `${i18n.t("plans.form.no")}` : `${i18n.t("plans.form.yes")}`;
  324. };
  325. const renderIntegrations = (row) => {
  326. return row.useIntegrations === false ? `${i18n.t("plans.form.no")}` : `${i18n.t("plans.form.yes")}`;
  327. };
  328. return (
  329. <Paper className={classes.tableContainer}>
  330. <Table
  331. className={classes.fullWidth}
  332. // size="small"
  333. padding="none"
  334. aria-label="a dense table"
  335. >
  336. <TableHead>
  337. <TableRow>
  338. <TableCell align="center" style={{ width: '1%' }}>#</TableCell>
  339. <TableCell align="left">{i18n.t("plans.form.name")}</TableCell>
  340. <TableCell align="center">{i18n.t("plans.form.users")}</TableCell>
  341. <TableCell align="center">{i18n.t("plans.form.connections")}</TableCell>
  342. <TableCell align="center">{i18n.t("plans.form.queues")}</TableCell>
  343. <TableCell align="center">{i18n.t("plans.form.value")}</TableCell>
  344. <TableCell align="center">{i18n.t("plans.form.campaigns")}</TableCell>
  345. <TableCell align="center">{i18n.t("plans.form.schedules")}</TableCell>
  346. <TableCell align="center">{i18n.t("plans.form.internalChat")}</TableCell>
  347. <TableCell align="center">{i18n.t("plans.form.externalApi")}</TableCell>
  348. <TableCell align="center">{i18n.t("plans.form.kanban")}</TableCell>
  349. <TableCell align="center">Open.Ai</TableCell>
  350. <TableCell align="center">{i18n.t("plans.form.integrations")}</TableCell>
  351. </TableRow>
  352. </TableHead>
  353. <TableBody>
  354. {records.map((row) => (
  355. <TableRow key={row.id}>
  356. <TableCell align="center" style={{ width: '1%' }}>
  357. <IconButton onClick={() => onSelect(row)} aria-label="delete">
  358. <EditIcon />
  359. </IconButton>
  360. </TableCell>
  361. <TableCell align="left">{row.name || '-'}</TableCell>
  362. <TableCell align="center">{row.users || '-'}</TableCell>
  363. <TableCell align="center">{row.connections || '-'}</TableCell>
  364. <TableCell align="center">{row.queues || '-'}</TableCell>
  365. <TableCell align="center">{i18n.t("plans.form.money")} {row.value ? row.value.toLocaleString('pt-br', { minimumFractionDigits: 2 }) : '00.00'}</TableCell>
  366. <TableCell align="center">{renderCampaigns(row)}</TableCell>
  367. <TableCell align="center">{renderSchedules(row)}</TableCell>
  368. <TableCell align="center">{renderInternalChat(row)}</TableCell>
  369. <TableCell align="center">{renderExternalApi(row)}</TableCell>
  370. <TableCell align="center">{renderKanban(row)}</TableCell>
  371. <TableCell align="center">{renderOpenAi(row)}</TableCell>
  372. <TableCell align="center">{renderIntegrations(row)}</TableCell>
  373. </TableRow>
  374. ))}
  375. </TableBody>
  376. </Table>
  377. </Paper>
  378. )
  379. }
  380. export default function PlansManager() {
  381. const classes = useStyles()
  382. const { list, save, update, remove } = usePlans()
  383. const [showConfirmDialog, setShowConfirmDialog] = useState(false)
  384. const [loading, setLoading] = useState(false)
  385. const [records, setRecords] = useState([])
  386. const [record, setRecord] = useState({
  387. name: '',
  388. users: 0,
  389. connections: 0,
  390. queues: 0,
  391. value: 0,
  392. useCampaigns: true,
  393. useSchedules: true,
  394. useInternalChat: true,
  395. useExternalApi: true,
  396. useKanban: true,
  397. useOpenAi: true,
  398. useIntegrations: true,
  399. })
  400. useEffect(() => {
  401. async function fetchData() {
  402. await loadPlans()
  403. }
  404. fetchData()
  405. // eslint-disable-next-line react-hooks/exhaustive-deps
  406. }, [record])
  407. const loadPlans = async () => {
  408. setLoading(true)
  409. try {
  410. const planList = await list()
  411. setRecords(planList)
  412. } catch (e) {
  413. toast.error(i18n.t("plans.toasts.errorList"))
  414. }
  415. setLoading(false)
  416. }
  417. const handleSubmit = async (data) => {
  418. setLoading(true)
  419. console.log(data)
  420. try {
  421. if (data.id !== undefined) {
  422. await update(data)
  423. } else {
  424. await save(data)
  425. }
  426. await loadPlans()
  427. handleCancel()
  428. toast.success(i18n.t("plans.toasts.success"))
  429. } catch (e) {
  430. toast.error(i18n.t("plans.toasts.error"))
  431. }
  432. setLoading(false)
  433. }
  434. const handleDelete = async () => {
  435. setLoading(true)
  436. try {
  437. await remove(record.id)
  438. await loadPlans()
  439. handleCancel()
  440. toast.success(i18n.t("plans.toasts.success"))
  441. } catch (e) {
  442. toast.error(i18n.t("plans.toasts.errorOperation"))
  443. }
  444. setLoading(false)
  445. }
  446. const handleOpenDeleteDialog = () => {
  447. setShowConfirmDialog(true)
  448. }
  449. const handleCancel = () => {
  450. setRecord({
  451. id: undefined,
  452. name: '',
  453. users: 0,
  454. connections: 0,
  455. queues: 0,
  456. value: 0,
  457. useCampaigns: true,
  458. useSchedules: true,
  459. useInternalChat: true,
  460. useExternalApi: true,
  461. useKanban: true,
  462. useOpenAi: true,
  463. useIntegrations: true
  464. })
  465. }
  466. const handleSelect = (data) => {
  467. let useCampaigns = data.useCampaigns === false ? false : true
  468. let useSchedules = data.useSchedules === false ? false : true
  469. let useInternalChat = data.useInternalChat === false ? false : true
  470. let useExternalApi = data.useExternalApi === false ? false : true
  471. let useKanban = data.useKanban === false ? false : true
  472. let useOpenAi = data.useOpenAi === false ? false : true
  473. let useIntegrations = data.useIntegrations === false ? false : true
  474. setRecord({
  475. id: data.id,
  476. name: data.name || '',
  477. users: data.users || 0,
  478. connections: data.connections || 0,
  479. queues: data.queues || 0,
  480. value: data.value?.toLocaleString('pt-br', { minimumFractionDigits: 0 }) || 0,
  481. useCampaigns,
  482. useSchedules,
  483. useInternalChat,
  484. useExternalApi,
  485. useKanban,
  486. useOpenAi,
  487. useIntegrations
  488. })
  489. }
  490. return (
  491. <Paper className={classes.mainPaper} elevation={0}>
  492. <Grid spacing={2} container>
  493. <Grid xs={12} item>
  494. <PlanManagerForm
  495. initialValue={record}
  496. onDelete={handleOpenDeleteDialog}
  497. onSubmit={handleSubmit}
  498. onCancel={handleCancel}
  499. loading={loading}
  500. />
  501. </Grid>
  502. <Grid xs={12} item>
  503. <PlansManagerGrid
  504. records={records}
  505. onSelect={handleSelect}
  506. />
  507. </Grid>
  508. </Grid>
  509. <ConfirmationModal
  510. title={i18n.t("plans.confirm.title")}
  511. open={showConfirmDialog}
  512. onClose={() => setShowConfirmDialog(false)}
  513. onConfirm={() => handleDelete()}
  514. >
  515. {i18n.t("plans.confirm.message")}
  516. </ConfirmationModal>
  517. </Paper>
  518. )
  519. }