@acvl/frontend-components 0.0.35 → 0.0.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,7 +6,13 @@ export interface CSVMenuOptionProps {
6
6
  custom?: string;
7
7
  icon?: any;
8
8
  }
9
+ export interface ExtraCreateOptionProps {
10
+ title: string;
11
+ icon: any;
12
+ onClick: () => void;
13
+ }
9
14
  export interface CreateGridAPIProps {
15
+ extraOptions?: ExtraCreateOptionProps[];
10
16
  csv_upload?: {
11
17
  enable: boolean;
12
18
  extraOptions?: CSVMenuOptionProps[];
@@ -3,6 +3,7 @@ import React from "react";
3
3
  import { ButtonProps } from "@mui/material";
4
4
  export interface ControlledFileFieldProps extends BaseControlledFieldProps {
5
5
  extraProps?: {
6
+ multiple?: boolean;
6
7
  slotProps?: {
7
8
  input?: {
8
9
  accept?: string;
@@ -0,0 +1,18 @@
1
+ import React from "react";
2
+ import type { AutocompleteProps } from "@mui/material";
3
+ import { ComoOpcionesProps } from "../../../types";
4
+ export interface CrudDropdownProps {
5
+ name: string;
6
+ label?: string;
7
+ control: any;
8
+ fieldProps?: Partial<AutocompleteProps<any, any, any, any>>;
9
+ extraProps: {
10
+ queryArgs: ComoOpcionesProps;
11
+ crudExtraArgs?: Record<string, any>;
12
+ /** API field name for the display label. Default: "nombre" */
13
+ labelField?: string;
14
+ disableHT?: boolean;
15
+ };
16
+ }
17
+ declare const CrudDropdown: React.FC<CrudDropdownProps>;
18
+ export default CrudDropdown;
@@ -11,3 +11,4 @@ export { default as ControlledToggleButtons } from "./ControlledToggleButtons";
11
11
  export { default as ControlledTimePicker } from "./ControlledTimePicker";
12
12
  export { default as ControlledFileField } from "./ControlledFileField";
13
13
  export { default as ControlledSimpleSlider } from "./ControlledSimpleSlider";
14
+ export { default as CrudDropdown } from "./CrudDropdown";
@@ -21,6 +21,7 @@ export interface FieldProps {
21
21
  fieldName: string;
22
22
  value: any;
23
23
  method: any;
24
+ showOnDetail?: boolean;
24
25
  };
25
26
  filters?: FieldFilter[];
26
27
  navigation?: {
@@ -4,7 +4,7 @@ import { ReactNode } from "react";
4
4
  import { FieldValidationProps, EditReadOnlyFields } from "../types";
5
5
  import { PageType } from "../../../../types";
6
6
  export interface SectionProps {
7
- display?: boolean | FieldValidationProps;
7
+ display?: boolean | FieldValidationProps | FieldValidationProps[];
8
8
  data?: any;
9
9
  form?: any;
10
10
  bgColor?: string;
@@ -1,4 +1,4 @@
1
- import { ReactNode } from 'react';
1
+ import { ReactNode } from "react";
2
2
  export interface APIcontextProps {
3
3
  useLazyGetComoOpcionesQuery: any;
4
4
  useLazyGetHistorialQuery: any;
@@ -6,9 +6,10 @@ export interface APIcontextProps {
6
6
  useValidarCSVMutation: any;
7
7
  useSubirCSVMutation: any;
8
8
  useRemoverCSVMutation: any;
9
+ useGenericCrudMutation: any;
9
10
  }
10
11
  declare const APIContext: import("react").Context<APIcontextProps | null>;
11
- export declare const APIProvider: ({ children, hooks }: {
12
+ export declare const APIProvider: ({ children, hooks, }: {
12
13
  children: ReactNode;
13
14
  hooks: APIcontextProps;
14
15
  }) => import("react/jsx-runtime").JSX.Element;
package/dist/esm/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
- import { useTheme, useMediaQuery, Tooltip, IconButton, Button, Grid, FormGroup, FormHelperText as FormHelperText$1, Drawer, Box, Stack, CircularProgress, Typography as Typography$1, List as List$1, ListItem as ListItem$1, ListSubheader, styled as styled$1, LinearProgress, Autocomplete, Checkbox, Slider, Switch, InputAdornment as InputAdornment$1, ToggleButtonGroup, ToggleButton, Dialog, DialogTitle, DialogContent as DialogContent$1, DialogContentText, DialogActions as DialogActions$1, ButtonGroup, Menu, ListItemIcon, MenuItem as MenuItem$1, Link, Card, CardHeader, Avatar, CardContent, Divider as Divider$1, Popper, Grow as Grow$1, Paper, ClickAwayListener, Badge, TablePagination, NoSsr, tooltipClasses, Collapse, Fade as Fade$1, Slide, Zoom, Snackbar, alpha as alpha$1, lighten, createTheme as createTheme$1, responsiveFontSizes } from '@mui/material';
3
+ import { useTheme, useMediaQuery, Tooltip, IconButton, Button, Grid, FormGroup, FormHelperText as FormHelperText$1, Drawer, Box, Stack, CircularProgress, Typography as Typography$1, List as List$1, ListItem as ListItem$1, ListSubheader, styled as styled$1, LinearProgress, Autocomplete, Checkbox, Slider, Switch, InputAdornment as InputAdornment$1, ToggleButtonGroup, ToggleButton, TextField as TextField$1, Dialog, DialogTitle, DialogContent as DialogContent$1, DialogActions as DialogActions$1, DialogContentText, ButtonGroup, Menu, ListItemIcon, MenuItem as MenuItem$1, Link, Card, CardHeader, Avatar, CardContent, Divider as Divider$1, Popper, Grow as Grow$1, Paper, ClickAwayListener, Badge, TablePagination, NoSsr, tooltipClasses, Popover, ListItemButton, ListItemText as ListItemText$1, Collapse, Fade as Fade$1, Slide, Zoom, Snackbar, alpha as alpha$1, lighten, createTheme as createTheme$1, responsiveFontSizes } from '@mui/material';
4
4
  import EditIcon from '@mui/icons-material/Edit';
5
5
  import * as React from 'react';
6
6
  import React__default, { useCallback, useEffect, isValidElement, cloneElement, Children, createContext, useContext, useState, useMemo, useRef, useImperativeHandle, useLayoutEffect, Suspense, forwardRef } from 'react';
@@ -41,7 +41,7 @@ import BaseFocusTrap from '@mui/material/Unstable_TrapFocus';
41
41
  import DialogContent from '@mui/material/DialogContent';
42
42
  import MuiDialog, { dialogClasses } from '@mui/material/Dialog';
43
43
  import debounce from 'lodash/debounce';
44
- import { get, map, find, groupBy, forEach, findIndex, mergeWith, isArray, omit, set as set$1, every, includes, merge as merge$2, countBy, isNumber, isEmpty as isEmpty$1, isBoolean, isEqual as isEqual$1, debounce as debounce$1, some } from 'lodash';
44
+ import { get, map, find, groupBy, forEach, findIndex, mergeWith, isArray, omit, set as set$1, debounce as debounce$1, every, includes, merge as merge$2, countBy, isNumber, isEmpty as isEmpty$1, isBoolean, isEqual as isEqual$1, some } from 'lodash';
45
45
  import { produce } from 'immer';
46
46
  import set from 'lodash/set';
47
47
  import get$1 from 'lodash/get';
@@ -68,8 +68,8 @@ import { Visibility, VisibilityOff } from '@mui/icons-material';
68
68
  import merge$1 from 'lodash/merge';
69
69
  import UploadFileIcon from '@mui/icons-material/UploadFile';
70
70
  import ClearIcon$1 from '@mui/icons-material/Clear';
71
- import SaveIcon from '@mui/icons-material/Save';
72
71
  import AddIcon from '@mui/icons-material/Add';
72
+ import SaveIcon from '@mui/icons-material/Save';
73
73
  import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
74
74
  import LinearProgress$1 from '@mui/material/LinearProgress';
75
75
  import AttachFileIcon from '@mui/icons-material/AttachFile';
@@ -97,6 +97,7 @@ import DrawIcon from '@mui/icons-material/Draw';
97
97
  import CloseIcon from '@mui/icons-material/Close';
98
98
  import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
99
99
  import { skipToken } from '@reduxjs/toolkit/query';
100
+ import OpenInNewIcon from '@mui/icons-material/OpenInNew';
100
101
  import TabList from '@mui/lab/TabList';
101
102
  import TabPanel from '@mui/lab/TabPanel';
102
103
  import TabContext from '@mui/lab/TabContext';
@@ -25494,8 +25495,8 @@ const useHasPermission = (path) => {
25494
25495
  };
25495
25496
 
25496
25497
  const APIContext = createContext(null);
25497
- const APIProvider = ({ children, hooks }) => {
25498
- return (jsx(APIContext.Provider, { value: hooks, children: children }));
25498
+ const APIProvider = ({ children, hooks, }) => {
25499
+ return jsx(APIContext.Provider, { value: hooks, children: children });
25499
25500
  };
25500
25501
 
25501
25502
  const useAPI = () => {
@@ -26518,7 +26519,19 @@ const VisuallyHiddenInput = styled$1("input")({
26518
26519
  });
26519
26520
  const ControlledFileField = (props) => {
26520
26521
  return (jsx(Controller, { control: props.control, name: props.name, render: ({ field: { value, onChange, onBlur, ref }, fieldState: { error } }) => {
26521
- return (jsxs(FormGroup, { sx: { width: 1 }, color: "secondary", children: [value ? (jsxs(Stack, { direction: "row", spacing: 1, width: 1, alignItems: "center", children: [jsx(Typography$1, { children: value.name }), jsx(IconButton, { onClick: () => onChange(null), children: jsx(ClearIcon$1, { color: "error" }) })] })) : (jsxs(Button, { component: "label", variant: "contained", startIcon: jsx(UploadFileIcon, {}), sx: { maxWidth: 200 }, ...props.extraProps?.slotProps?.button, children: ["Cargar", jsx(VisuallyHiddenInput, { type: "file", accept: "image/*", ref: ref, onBlur: onBlur, onChange: (e) => onChange(e.target.files && e.target.files[0]), ...props.extraProps?.slotProps?.input })] })), jsx(FormHelperText$1, { error: !!error, children: error ? error.message : " " })] }));
26522
+ if (props.extraProps?.multiple) {
26523
+ const files = value ?? [];
26524
+ const handleAdd = (e) => {
26525
+ const incoming = e.target.files ? Array.from(e.target.files) : [];
26526
+ onChange([...files, ...incoming]);
26527
+ e.target.value = "";
26528
+ };
26529
+ const handleRemove = (index) => {
26530
+ onChange(files.filter((_, i) => i !== index));
26531
+ };
26532
+ return (jsxs(FormGroup, { sx: { width: 1 }, children: [jsxs(Stack, { spacing: 1, width: 1, children: [files.map((file, index) => (jsxs(Stack, { direction: "row", spacing: 1, alignItems: "center", sx: { minWidth: 0 }, children: [jsx(Typography$1, { noWrap: true, sx: { flex: 1 }, children: file.name }), jsx(IconButton, { onClick: () => handleRemove(index), children: jsx(ClearIcon$1, { color: "error" }) })] }, index))), jsxs(Button, { component: "label", variant: "contained", startIcon: jsx(UploadFileIcon, {}), sx: { maxWidth: 200 }, ...props.extraProps?.slotProps?.button, children: ["Cargar", jsx(VisuallyHiddenInput, { type: "file", multiple: true, accept: "image/*", ref: ref, onBlur: onBlur, onChange: handleAdd, ...props.extraProps?.slotProps?.input })] })] }), jsx(FormHelperText$1, { error: !!error, children: error ? error.message : " " })] }));
26533
+ }
26534
+ return (jsxs(FormGroup, { sx: { width: 1 }, color: "secondary", children: [value ? (jsxs(Stack, { direction: "row", spacing: 1, width: 1, alignItems: "center", sx: { minWidth: 0 }, children: [jsx(Typography$1, { noWrap: true, sx: { flex: 1 }, children: value.name }), jsx(IconButton, { onClick: () => onChange(null), children: jsx(ClearIcon$1, { color: "error" }) })] })) : (jsxs(Button, { component: "label", variant: "contained", startIcon: jsx(UploadFileIcon, {}), sx: { maxWidth: 200 }, ...props.extraProps?.slotProps?.button, children: ["Cargar", jsx(VisuallyHiddenInput, { type: "file", accept: "image/*", ref: ref, onBlur: onBlur, onChange: (e) => onChange(e.target.files && e.target.files[0]), ...props.extraProps?.slotProps?.input })] })), jsx(FormHelperText$1, { error: !!error, children: error ? error.message : " " })] }));
26522
26535
  } }));
26523
26536
  };
26524
26537
  ControlledFileField.displayName = "ControlledFileField";
@@ -26532,6 +26545,161 @@ const ControlledSlider = (props) => {
26532
26545
  } }));
26533
26546
  };
26534
26547
 
26548
+ const CREATE_OPTION = { value: "__create__", label: "" };
26549
+ const CrudDropdown = (props) => {
26550
+ const { name, label, control, fieldProps, extraProps: { queryArgs, crudExtraArgs = {}, labelField = "nombre", disableHT }, } = props;
26551
+ const [inputValue, setInputValue] = useState("");
26552
+ const [options, setOptions] = useState([]);
26553
+ const [open, setOpen] = useState(false);
26554
+ const [createOpen, setCreateOpen] = useState(false);
26555
+ const [editOpen, setEditOpen] = useState(false);
26556
+ const [deleteOpen, setDeleteOpen] = useState(false);
26557
+ const [activeOption, setActiveOption] = useState(null);
26558
+ const [dialogInput, setDialogInput] = useState("");
26559
+ const formValueRef = useRef(null);
26560
+ const formOnChangeRef = useRef(() => { });
26561
+ const api = useAPI();
26562
+ const [getOpciones, { isLoading, isFetching, isError }] = api.useLazyGetComoOpcionesQuery();
26563
+ const [genericCrud, { isLoading: isMutating }] = api.useGenericCrudMutation();
26564
+ const notifications = useNotifications();
26565
+ const args = useMemo(() => ({
26566
+ ...queryArgs,
26567
+ filters: { ...queryArgs.filters, nombre__icontains: inputValue },
26568
+ }), [inputValue, queryArgs]);
26569
+ const fetchOptions = useCallback(debounce$1((fetchArgs, cb) => {
26570
+ setOptions([]);
26571
+ getOpciones(fetchArgs)
26572
+ .unwrap()
26573
+ .then(cb)
26574
+ .catch(() => { });
26575
+ }, 500), []);
26576
+ const refreshOptions = () => {
26577
+ getOpciones(args, false)
26578
+ .unwrap()
26579
+ .then((fresh) => setOptions(fresh))
26580
+ .catch(() => { });
26581
+ };
26582
+ React__default.useEffect(() => {
26583
+ if (open)
26584
+ fetchOptions(args, setOptions);
26585
+ }, [args, fetchOptions, open]);
26586
+ const handleCreate = () => {
26587
+ genericCrud({
26588
+ endpoint: props.extraProps.queryArgs.endpoint,
26589
+ method: "POST",
26590
+ body: { [labelField]: dialogInput, ...crudExtraArgs },
26591
+ })
26592
+ .unwrap()
26593
+ .then(() => {
26594
+ notifications.show("Opción creada.", { severity: "success" });
26595
+ setCreateOpen(false);
26596
+ setDialogInput("");
26597
+ refreshOptions();
26598
+ })
26599
+ .catch(() => notifications.show("Error al crear.", { severity: "error" }));
26600
+ };
26601
+ const handleEdit = () => {
26602
+ if (!activeOption)
26603
+ return;
26604
+ genericCrud({
26605
+ endpoint: props.extraProps.queryArgs.endpoint,
26606
+ method: "PATCH",
26607
+ id: activeOption.value,
26608
+ body: { [labelField]: dialogInput, ...crudExtraArgs },
26609
+ })
26610
+ .unwrap()
26611
+ .then(() => {
26612
+ if (formValueRef.current?.value === activeOption.value) {
26613
+ formOnChangeRef.current({ value: activeOption.value, label: dialogInput });
26614
+ }
26615
+ notifications.show("Opción actualizada.", { severity: "success" });
26616
+ setEditOpen(false);
26617
+ setDialogInput("");
26618
+ setActiveOption(null);
26619
+ refreshOptions();
26620
+ })
26621
+ .catch(() => notifications.show("Error al actualizar.", { severity: "error" }));
26622
+ };
26623
+ const handleDelete = () => {
26624
+ if (!activeOption)
26625
+ return;
26626
+ genericCrud({
26627
+ endpoint: props.extraProps.queryArgs.endpoint,
26628
+ method: "DELETE",
26629
+ id: activeOption.value,
26630
+ })
26631
+ .unwrap()
26632
+ .then(() => {
26633
+ if (formValueRef.current?.value === activeOption.value) {
26634
+ formOnChangeRef.current(null);
26635
+ }
26636
+ notifications.show("Opción eliminada.", { severity: "success" });
26637
+ setDeleteOpen(false);
26638
+ setActiveOption(null);
26639
+ refreshOptions();
26640
+ })
26641
+ .catch(() => notifications.show("Error al eliminar.", { severity: "error" }));
26642
+ };
26643
+ return (jsxs(Fragment, { children: [jsx(Controller, { name: name, control: control, render: ({ field: { value, onChange }, fieldState: { error } }) => {
26644
+ formValueRef.current = value;
26645
+ formOnChangeRef.current = onChange;
26646
+ const handleChange = (_event, newValue) => {
26647
+ if (newValue?.value === "__create__") {
26648
+ setDialogInput("");
26649
+ setCreateOpen(true);
26650
+ return;
26651
+ }
26652
+ onChange(newValue);
26653
+ };
26654
+ return (jsx(Autocomplete, { id: name, open: open, onOpen: () => setOpen(true), onClose: () => setOpen(false), options: options, loading: isLoading || isFetching, value: value, onChange: handleChange, inputValue: inputValue, onInputChange: (_event, newInputValue) => setInputValue(newInputValue), isOptionEqualToValue: (option, val) => option.value === val.value, filterOptions: (x) => [CREATE_OPTION, ...x], getOptionLabel: (option) => option.value === "__create__" ? "" : option.label || "", fullWidth: true, size: "small", disabled: isError, renderOption: (optionProps, option) => {
26655
+ const { key, ...rest } = optionProps;
26656
+ if (option.value === "__create__") {
26657
+ return (jsxs(Box, { component: "li", ...rest, onClick: () => {
26658
+ setDialogInput("");
26659
+ setCreateOpen(true);
26660
+ }, sx: {
26661
+ justifyContent: "center !important",
26662
+ color: "primary.main",
26663
+ borderBottom: "1px solid",
26664
+ borderColor: "divider",
26665
+ py: "6px !important",
26666
+ gap: 0.5,
26667
+ }, children: [jsx(AddIcon, { fontSize: "small" }), jsx(Typography$1, { variant: "body2", children: "Agregar nuevo" })] }, key));
26668
+ }
26669
+ return (jsxs(Box, { component: "li", ...rest, sx: {
26670
+ display: "flex",
26671
+ alignItems: "center",
26672
+ pr: "4px !important",
26673
+ }, children: [jsx(Box, { sx: {
26674
+ flex: 1,
26675
+ overflow: "hidden",
26676
+ textOverflow: "ellipsis",
26677
+ }, children: option.label }), jsx(IconButton, { size: "small", tabIndex: -1, onMouseDown: (e) => {
26678
+ e.stopPropagation();
26679
+ e.preventDefault();
26680
+ }, onClick: (e) => {
26681
+ e.stopPropagation();
26682
+ setActiveOption(option);
26683
+ setDialogInput(option.label);
26684
+ setEditOpen(true);
26685
+ }, children: jsx(EditIcon, { fontSize: "inherit" }) }), jsx(IconButton, { size: "small", tabIndex: -1, onMouseDown: (e) => {
26686
+ e.stopPropagation();
26687
+ e.preventDefault();
26688
+ }, onClick: (e) => {
26689
+ e.stopPropagation();
26690
+ setActiveOption(option);
26691
+ setDeleteOpen(true);
26692
+ }, children: jsx(DeleteIcon, { fontSize: "inherit" }) })] }, key));
26693
+ }, renderInput: (params) => (jsx(TextField$1, { ...params, label: label, error: !!error, helperText: error ? error.message : disableHT ? "" : " ", slotProps: {
26694
+ input: {
26695
+ ...params.InputProps,
26696
+ endAdornment: (jsxs(Fragment, { children: [isLoading || isFetching ? (jsx(CircularProgress, { color: "inherit", size: 20 })) : null, params.InputProps.endAdornment] })),
26697
+ },
26698
+ } })), ...fieldProps }));
26699
+ } }), jsxs(Dialog, { open: createOpen, onClose: () => setCreateOpen(false), maxWidth: "xs", fullWidth: true, children: [jsx(DialogTitle, { children: "Agregar opci\u00F3n" }), jsx(DialogContent$1, { children: jsx(TextField$1, { autoFocus: true, fullWidth: true, size: "small", label: label, value: dialogInput, onChange: (e) => setDialogInput(e.target.value), onKeyDown: (e) => e.key === "Enter" && !!dialogInput && handleCreate(), sx: { mt: 1 } }) }), jsxs(DialogActions$1, { children: [jsx(Button, { color: "error", onClick: () => setCreateOpen(false), children: "Cancelar" }), jsx(Button, { color: "secondary", disabled: !dialogInput, loading: isMutating, onClick: handleCreate, children: "Guardar" })] })] }), jsxs(Dialog, { open: editOpen, onClose: () => setEditOpen(false), maxWidth: "xs", fullWidth: true, children: [jsx(DialogTitle, { children: "Editar opci\u00F3n" }), jsx(DialogContent$1, { children: jsx(TextField$1, { autoFocus: true, fullWidth: true, size: "small", label: label, value: dialogInput, onChange: (e) => setDialogInput(e.target.value), onKeyDown: (e) => e.key === "Enter" && !!dialogInput && handleEdit(), sx: { mt: 1 } }) }), jsxs(DialogActions$1, { children: [jsx(Button, { color: "error", onClick: () => setEditOpen(false), children: "Cancelar" }), jsx(Button, { color: "secondary", disabled: !dialogInput, loading: isMutating, onClick: handleEdit, children: "Guardar" })] })] }), jsxs(Dialog, { open: deleteOpen, onClose: () => setDeleteOpen(false), maxWidth: "xs", fullWidth: true, children: [jsx(DialogTitle, { children: "Eliminar opci\u00F3n" }), jsx(DialogContent$1, { children: jsxs(Typography$1, { children: ["\u00BFEliminar ", jsx("strong", { children: activeOption?.label }), "?"] }) }), jsxs(DialogActions$1, { children: [jsx(Button, { onClick: () => setDeleteOpen(false), children: "Cancelar" }), jsx(Button, { color: "error", loading: isMutating, onClick: handleDelete, children: "Eliminar" })] })] })] }));
26700
+ };
26701
+ CrudDropdown.displayName = "CrudDropdown";
26702
+
26535
26703
  const DeleteConfirmationDialog = (props) => {
26536
26704
  const notifications = useNotifications();
26537
26705
  const { pathname } = useLocation();
@@ -27655,7 +27823,12 @@ const CreateGridWrapper = (props) => {
27655
27823
  }), jsxs(MenuItem, { onClick: () => {
27656
27824
  navigate(props.navigation ? `${props.navigation}crear` : `crear`);
27657
27825
  handleClose();
27658
- }, disableRipple: true, children: [jsx(ListItemIcon, { children: jsx(DrawIcon, {}) }), "Manual"] })] }), props.csv_upload?.enable && (jsx(CSVUpload, { open: csv.open, drawerToggle: handleToggleClose, data: csv.data })), props.renderFunction({ dataGridProps: createGridProps })] }));
27826
+ }, disableRipple: true, children: [jsx(ListItemIcon, { children: jsx(DrawIcon, {}) }), "Manual"] }), props.extraOptions?.map((option, index) => {
27827
+ return (jsxs(MenuItem, { onClick: () => {
27828
+ option.onClick();
27829
+ handleClose();
27830
+ }, disableRipple: true, children: [jsx(ListItemIcon, { children: option.icon }), option.title] }, index));
27831
+ })] }), props.csv_upload?.enable && (jsx(CSVUpload, { open: csv.open, drawerToggle: handleToggleClose, data: csv.data })), props.renderFunction({ dataGridProps: createGridProps })] }));
27659
27832
  };
27660
27833
 
27661
27834
  const Base = (props) => {
@@ -27999,6 +28172,7 @@ const LinkField = ({ url, value, text }) => {
27999
28172
  return jsx(Link, { href: `${url}/${value}`, children: text });
28000
28173
  };
28001
28174
  const DetailField = (props) => {
28175
+ const [anchor, setAnchor] = useState(null);
28002
28176
  const hasLinkToViewPermission = useHasPermission(`${props.navigation?.permission}.view`);
28003
28177
  const value = useCallback(() => {
28004
28178
  const { fieldData, type, extraProps, detail_name } = props;
@@ -28018,7 +28192,7 @@ const DetailField = (props) => {
28018
28192
  ? dayjs(fieldData).format("YYYY-MM-DD HH:mm")
28019
28193
  : dayjs(fieldData).format("YYYY-MM-DD");
28020
28194
  case "ControlledFileField":
28021
- return fieldData ? "Archivo" : "N/A";
28195
+ return fieldData ? "Archivo(s)" : "N/A";
28022
28196
  default:
28023
28197
  if (!fieldData)
28024
28198
  return "N/A";
@@ -28036,7 +28210,13 @@ const DetailField = (props) => {
28036
28210
  return (jsx(LinkField, { url: props.navigation.url, value: get(props.fieldData, "value"), text: content }));
28037
28211
  }
28038
28212
  else if (props.type.displayName == "ControlledFileField" && content !== "N/A") {
28039
- return (jsx(Link, { href: props.fieldData, target: "_blank", rel: "noopener noreferrer", children: content }));
28213
+ const archivos = Array.isArray(props.fieldData)
28214
+ ? props.fieldData.filter(Boolean)
28215
+ : [props.fieldData];
28216
+ return (jsxs(Fragment, { children: [jsx(Badge, { badgeContent: archivos.length, color: "primary", sx: { "& .MuiBadge-badge": { fontSize: 10, minWidth: 16, height: 16 } }, children: jsx(Button, { size: "small", onClick: (e) => {
28217
+ e.stopPropagation();
28218
+ setAnchor(e.currentTarget);
28219
+ }, children: content }) }), jsx(Popover, { open: Boolean(anchor), anchorEl: anchor, onClose: () => setAnchor(null), anchorOrigin: { vertical: "bottom", horizontal: "center" }, transformOrigin: { vertical: "top", horizontal: "center" }, onClick: (e) => e.stopPropagation(), children: jsx(List$1, { dense: true, disablePadding: true, sx: { minWidth: 140 }, children: archivos.map((archivo, i) => (jsxs(ListItemButton, { component: "a", href: archivo.imagen, target: "_blank", rel: "noopener noreferrer", onClick: (e) => e.stopPropagation(), children: [jsx(ListItemText$1, { primary: `Archivo ${i + 1}` }), jsx(OpenInNewIcon, { fontSize: "small", sx: { ml: 1, opacity: 0.6 } })] }, i))) }) })] }));
28040
28220
  }
28041
28221
  else {
28042
28222
  if (Array.isArray(props.fieldData)) {
@@ -28084,6 +28264,8 @@ const Field = (props) => {
28084
28264
  const formData = props.form.watch();
28085
28265
  const show = useMemo(() => {
28086
28266
  if (props.dependantOn) {
28267
+ if (props.dependantOn.showOnDetail && props.pageType === "detail")
28268
+ return true;
28087
28269
  return props.dependantOn.method(get(props.data || formData, props.dependantOn.fieldName), props.dependantOn.value);
28088
28270
  }
28089
28271
  return true;
@@ -28112,6 +28294,7 @@ const FieldArrayField = (props) => {
28112
28294
  keyName: "_id",
28113
28295
  });
28114
28296
  const errors = props.form.formState.errors;
28297
+ const errorMessage = get(props.form.formState.errors, props.name + ".message");
28115
28298
  const getIndex = useCallback((id) => findIndex(fields, function (field) {
28116
28299
  return field._id == id;
28117
28300
  }), [fields]);
@@ -28192,31 +28375,27 @@ const FieldArrayField = (props) => {
28192
28375
  fieldProps: field.fieldProps,
28193
28376
  extraProps: field.extraProps,
28194
28377
  }) })] }, `grid-${index}`));
28195
- }) }) }), jsxs(Grid, { rowSpacing: 1, container: true, size: "grow", direction: "column", ...props.gridProps, children: [jsx(Grid, { size: 12, children: jsx(Typography$1, { variant: "body1", fontWeight: "bold", children: props.label }) }), jsx(Grid, { size: 12, children: jsx(DataGrid, { ...dataGridProps }) })] })] }));
28378
+ }) }) }), jsxs(Grid, { rowSpacing: 1, container: true, size: "grow", direction: "column", ...props.gridProps, children: [jsx(Grid, { size: 12, children: jsx(Typography$1, { variant: "body1", fontWeight: "bold", children: props.label }) }), errorMessage && (jsx(Grid, { size: 12, children: jsx(Typography$1, { variant: "body1", color: "error", children: errorMessage }) })), jsx(Grid, { size: 12, children: jsx(DataGrid, { ...dataGridProps }) })] })] }));
28196
28379
  };
28197
28380
 
28198
28381
  const Section = (props) => {
28199
28382
  const [expanded, setExpanded] = useState(true);
28200
28383
  const handleExpandChange = () => {
28201
- setExpanded(prevState => !prevState);
28384
+ setExpanded((prevState) => !prevState);
28202
28385
  };
28203
28386
  const renderField = (field, index) => {
28204
28387
  if (props.first && index == 0)
28205
- merge$2(get(field, 'fieldProps', {}), { autoFocus: true });
28388
+ merge$2(get(field, "fieldProps", {}), { autoFocus: true });
28206
28389
  return (jsx(Field, { fieldData: props.data && get(props.data, field.name), ...field, readOnly:
28207
28390
  // props.validation?.readOnlyFields.includes(field.name) ||
28208
- (props.edit?.readOnlyFields?.names.includes(field.name) && props.edit.readOnlyFields.validator.method(get(props.data, props.edit.readOnlyFields.validator.field), props.edit.readOnlyFields.validator.value)), form: props.form, pageType: props.pageType, data: props.data }, `F-${index}`));
28391
+ props.edit?.readOnlyFields?.names.includes(field.name) &&
28392
+ props.edit.readOnlyFields.validator.method(get(props.data, props.edit.readOnlyFields.validator.field), props.edit.readOnlyFields.validator.value), form: props.form, pageType: props.pageType, data: props.data }, `F-${index}`));
28209
28393
  };
28210
28394
  const renderArrayField = (field, index) => (jsx(FieldArrayField, { ...field, form: props.form, pageType: props.pageType }, `FA-${index}`));
28211
- return (jsxs(Grid, { size: 12, container: true, spacing: 2, bgcolor: props.bgColor, p: 2, borderRadius: 1, children: [(props.title || props.description) &&
28212
- jsxs(Grid, { size: 12, container: true, spacing: 0, children: [props.title &&
28213
- jsxs(Grid, { size: 12, container: true, onClick: handleExpandChange, sx: {
28214
- cursor: 'pointer'
28215
- }, children: [jsx(Grid, { children: expanded ? jsx(ExpandLessIcon, { fontSize: 'small' }) : jsx(ExpandMoreIcon, { fontSize: 'small' }) }), jsx(Grid, { children: jsx(Typography$1, { variant: 'h5', children: props.title }) })] }), props.description &&
28216
- jsx(Grid, { size: 12, children: jsx(Typography$1, { variant: 'caption', color: 'secondary.main', children: props.description }) })] }), expanded &&
28217
- jsxs(Grid, { size: 12, container: true, direction: 'row', children: [props.fields &&
28218
- props.fields.map((field, index) => renderField(field, index)), props.arrayFields &&
28219
- props.arrayFields.map((arrayField, index) => renderArrayField(arrayField, index)), props.content && props.content(props.data)] })] }));
28395
+ return (jsxs(Grid, { size: 12, container: true, spacing: 2, bgcolor: props.bgColor, p: 2, borderRadius: 1, children: [(props.title || props.description) && (jsxs(Grid, { size: 12, container: true, spacing: 0, children: [props.title && (jsxs(Grid, { size: 12, container: true, onClick: handleExpandChange, sx: {
28396
+ cursor: "pointer",
28397
+ }, children: [jsx(Grid, { children: expanded ? (jsx(ExpandLessIcon, { fontSize: "small" })) : (jsx(ExpandMoreIcon, { fontSize: "small" })) }), jsx(Grid, { children: jsx(Typography$1, { variant: "h5", children: props.title }) })] })), props.description && (jsx(Grid, { size: 12, children: jsx(Typography$1, { variant: "caption", color: "secondary.main", children: props.description }) }))] })), expanded && (jsxs(Grid, { size: 12, container: true, direction: "row", children: [props.fields && props.fields.map((field, index) => renderField(field, index)), props.arrayFields &&
28398
+ props.arrayFields.map((arrayField, index) => renderArrayField(arrayField, index)), props.content && props.content(props.data)] }))] }));
28220
28399
  };
28221
28400
 
28222
28401
  // ==============================|| VALIDATION ||============================== //
@@ -28425,7 +28604,7 @@ function DetailPage(props) {
28425
28604
  }
28426
28605
  }
28427
28606
  }, [data, dispatch, form, props.slots?.title]);
28428
- const [mutation, { isLoading: mutationIsLoading, isSuccess, data: mutationResponse },] = props.api.mutate.mutation();
28607
+ const [mutation, { isLoading: mutationIsLoading, isSuccess, data: mutationResponse }] = props.api.mutate.mutation();
28429
28608
  const handleCancel = () => {
28430
28609
  form.reset();
28431
28610
  navigate(-1);
@@ -28435,8 +28614,7 @@ function DetailPage(props) {
28435
28614
  // ==============================|| VALIDATION ||============================== //
28436
28615
  useValidateFieldData(props, form);
28437
28616
  // ==============================|| QUICK RETURN ||============================== //
28438
- if ((!canEdit && props.pageType == "edit") ||
28439
- (fetchError && fetchError.originalStatus == 403))
28617
+ if ((!canEdit && props.pageType == "edit") || (fetchError && fetchError.originalStatus == 403))
28440
28618
  return jsx(NotAllowed, {});
28441
28619
  if (fetchError && fetchError.originalStatus == 404)
28442
28620
  return jsx(NotFound, {});
@@ -28450,9 +28628,7 @@ function DetailPage(props) {
28450
28628
  props: props.slotProps?.parser,
28451
28629
  };
28452
28630
  const dataToSend = formParser(params);
28453
- mutation(props.edit.useFormData
28454
- ? dataToSend
28455
- : { ...dataToSend, ...props.api.mutate.args })
28631
+ mutation(props.edit.useFormData ? dataToSend : { ...dataToSend, ...props.api.mutate.args })
28456
28632
  .unwrap()
28457
28633
  .then(() => {
28458
28634
  if (!props.slotProps?.button?.save?.disableNavBack)
@@ -28478,17 +28654,26 @@ function DetailPage(props) {
28478
28654
  };
28479
28655
  const onError = (errors) => console.log("Validation Errors:", errors);
28480
28656
  // ==============================|| RENDER SECTIONS ||============================== //
28657
+ const validateDisplay = (field, method, value) => {
28658
+ const validateAgainst = form.watch(field)?.toString();
28659
+ return method(validateAgainst, value);
28660
+ };
28481
28661
  const renderContent = () => {
28482
28662
  let displayed_sections = -1;
28483
28663
  return (jsxs(Grid, { size: 12, container: true, direction: "column", position: "relative", pb: 2, children: [props.pageType == "detail" && data?.has_history && (jsx(HistorialDrawer, { ...props.slotProps?.historial })), props.sections.map((section, index) => {
28484
28664
  if (!section.display)
28485
28665
  return null;
28486
28666
  if (typeof section.display == "object") {
28487
- const validateAgainst = form
28488
- .watch(section.display.field)
28489
- ?.toString();
28490
- if (!section.display.method(validateAgainst, section.display.value))
28491
- return null;
28667
+ if (Array.isArray(section.display)) {
28668
+ if (!section.display
28669
+ .map((item) => validateDisplay(item.field, item.method, item.value))
28670
+ .every((item) => item == true))
28671
+ return null;
28672
+ }
28673
+ else {
28674
+ if (!validateDisplay(section.display.field, section.display.method, section.display.value))
28675
+ return null;
28676
+ }
28492
28677
  }
28493
28678
  displayed_sections += 1;
28494
28679
  return (jsx(Section, { data: data, form: form, bgColor: displayed_sections % 2 == 1 ? "white" : "#F7F7F7", pageType: props.pageType, ...section, first: displayed_sections == 0 }, index));
@@ -28498,8 +28683,7 @@ function DetailPage(props) {
28498
28683
  if (props.api.mutate.postAction &&
28499
28684
  isSuccess &&
28500
28685
  "post_action" in mutationResponse &&
28501
- mutationResponse["post_action"]["type"] ===
28502
- props.api.mutate.postAction.type) {
28686
+ mutationResponse["post_action"]["type"] === props.api.mutate.postAction.type) {
28503
28687
  switch (props.api.mutate.postAction.after) {
28504
28688
  // case 'create':
28505
28689
  // if (props.pageType === 'create') return addPropsToNode(props.api.mutate.postAction.node, mutationResponse);
@@ -28841,5 +29025,5 @@ const theme = () => {
28841
29025
  return themes;
28842
29026
  };
28843
29027
 
28844
- export { APIContext, APIProvider, ActiveCell, Base, ButtonWithDrawer, CSVUpload, ContentLayout, ControlledDate, ControlledDropdown, ControlledFileField, FileUploadBox as ControlledFileUploadBox, ControlledNumber, ControlledPhoneNumber, ControlledSelect, ControlledSlider as ControlledSimpleSlider, ControlledSlider$1 as ControlledSlider, ControlledSwitch, ControlledText, ControlledTimePicker, ControlledToggleButtons, CoverPage, CreateButtonBase, CreateButtonWithMenuOptions, DeleteButton, DeleteConfirmationDialog, DetailPage, EditButton, ExportCSV, HistorialDrawer, ListPage, Loadable, LoadingComponent, Logo, MainDrawer, MenuItemWithLoading, ModuleCard, MoreOptions, MyGridEditSingleSelect, NavigationContext, NavigationProvider, NormalOrIcon, NotAllowed, NotFound, ObjectProgressStack, SaveCancelButton, StatusBadge, StyledMenu, Tabs, Transitions, UncontrolledText, changeTitle, closeProgressSnack, dayjs, deleteColumn, editColumn, filterQuery, formParser, header as headerReducer, manualCacheRefresh, mergeSettings, openProgressSnack, query, renderEditCellWithErrorTooltip, setErrors, snackProgress_default as snackProgressReducer, tags, theme, updateProgressSnackData, useAPI, useHasPermission, useNavigation, valueLabel };
29028
+ export { APIContext, APIProvider, ActiveCell, Base, ButtonWithDrawer, CSVUpload, ContentLayout, ControlledDate, ControlledDropdown, ControlledFileField, FileUploadBox as ControlledFileUploadBox, ControlledNumber, ControlledPhoneNumber, ControlledSelect, ControlledSlider as ControlledSimpleSlider, ControlledSlider$1 as ControlledSlider, ControlledSwitch, ControlledText, ControlledTimePicker, ControlledToggleButtons, CoverPage, CreateButtonBase, CreateButtonWithMenuOptions, CrudDropdown, DeleteButton, DeleteConfirmationDialog, DetailPage, EditButton, ExportCSV, HistorialDrawer, ListPage, Loadable, LoadingComponent, Logo, MainDrawer, MenuItemWithLoading, ModuleCard, MoreOptions, MyGridEditSingleSelect, NavigationContext, NavigationProvider, NormalOrIcon, NotAllowed, NotFound, ObjectProgressStack, SaveCancelButton, StatusBadge, StyledMenu, Tabs, Transitions, UncontrolledText, changeTitle, closeProgressSnack, dayjs, deleteColumn, editColumn, filterQuery, formParser, header as headerReducer, manualCacheRefresh, mergeSettings, openProgressSnack, query, renderEditCellWithErrorTooltip, setErrors, snackProgress_default as snackProgressReducer, tags, theme, updateProgressSnackData, useAPI, useHasPermission, useNavigation, valueLabel };
28845
29029
  //# sourceMappingURL=index.js.map