OscarGil03 hace 11 meses
padre
commit
95e224ddc7

+ 92 - 0
src/routers/routes.jsx

@@ -25,6 +25,10 @@ import { Usuarios, UsuarioDetalle } from "../views/admin/usuarios";
 
 
 /* CATÁLOGOS */
 /* CATÁLOGOS */
 import { Productos, ProductoDetalle } from "../views/catalogos/productos";
 import { Productos, ProductoDetalle } from "../views/catalogos/productos";
+import { Estados, EstadoDetalle } from "../views/catalogos/estados";
+import { Municipios, MunicipioDetalle } from "../views/catalogos/municipios";
+import { TipoMovilizaciones, TipoMovilizacionDetalle } from "../views/catalogos/tipoMovilizaciones";
+import { FinMovilizaciones, FinMovilizacionDetalle } from "../views/catalogos/finMovilizaciones";
 import { Condicionantes, CondicionanteDetalle } from "../views/condicionantes";
 import { Condicionantes, CondicionanteDetalle } from "../views/condicionantes";
 /* CATÁLOGOS */
 /* CATÁLOGOS */
 import { Perfil } from "../views/perfil";
 import { Perfil } from "../views/perfil";
@@ -195,6 +199,94 @@ const dashboardRoutes = [
               },
               },
             ],
             ],
           },
           },
+          {
+            layout: "dashboard",
+            path: "/estados",
+            name: "Estados",
+            icon: <ApartmentOutlined />,
+            sidebar: "single",
+            ver: "MENU-ADMIN",
+            routes: [
+              {
+                path: "/",
+                element: Estados,
+              },
+              {
+                path: "/agregar",
+                element: EstadoDetalle,
+              },
+              {
+                path: "/editar",
+                element: EstadoDetalle,
+              },
+            ],
+          },
+          {
+            layout: "dashboard",
+            path: "/municipios",
+            name: "Municipios",
+            icon: <ApartmentOutlined />,
+            sidebar: "single",
+            ver: "MENU-ADMIN",
+            routes: [
+              {
+                path: "/",
+                element: Municipios,
+              },
+              {
+                path: "/agregar",
+                element: MunicipioDetalle,
+              },
+              {
+                path: "/editar",
+                element: MunicipioDetalle,
+              },
+            ],
+          },
+          {
+            layout: "dashboard",
+            path: "/tipoMovilizaciones",
+            name: "Tipos de Movilización",
+            icon: <ApartmentOutlined />,
+            sidebar: "single",
+            ver: "MENU-ADMIN",
+            routes: [
+              {
+                path: "/",
+                element: TipoMovilizaciones,
+              },
+              {
+                path: "/agregar",
+                element: TipoMovilizacionDetalle,
+              },
+              {
+                path: "/editar",
+                element: TipoMovilizacionDetalle,
+              },
+            ],
+          },
+          {
+            layout: "dashboard",
+            path: "/finMovilizaciones",
+            name: "Fines de Movilización",
+            icon: <ApartmentOutlined />,
+            sidebar: "single",
+            ver: "MENU-ADMIN",
+            routes: [
+              {
+                path: "/",
+                element: FinMovilizaciones,
+              },
+              {
+                path: "/agregar",
+                element: FinMovilizacionDetalle,
+              },
+              {
+                path: "/editar",
+                element: FinMovilizacionDetalle,
+              },
+            ],
+          },
         ],
         ],
       },
       },
     ],
     ],

+ 136 - 0
src/views/catalogos/estados/EstadoDetalle.jsx

@@ -0,0 +1,136 @@
+import { Form, Input, Button, Spin, Row, Col } from 'antd'
+import { useEffect, useMemo, useState } from 'react'
+import HttpService from '../../../services/httpService'
+import { respuestas } from '../../../utilities'
+import { useNavigate } from 'react-router-dom'
+import { useQuery, useModel } from '../../../hooks'
+import { commonRules } from '../../../constants/rules'
+
+
+const endpoints = {
+  estado: "estado",
+};
+
+const EstadoDetalle = () => {
+  const [form] = Form.useForm()
+  const navigate = useNavigate()
+  const [loading, setLoading] = useState(false)
+  const query = useQuery()
+  const id = query.get("id")
+  const [request, setRequest] = useState({})
+
+  // const extraParams = useMemo(() => ({
+  //   idAgenda: id,
+  // }), [id])
+
+  const requestParams = useMemo(() => ({
+    name: endpoints.estado,
+    id,
+    // extraParams
+  }), [id])
+
+
+  const { model, modelLoading } = useModel(request)
+
+  useEffect(() => {
+    if (id) {
+      setRequest(requestParams)
+    }
+    return () => {
+      setRequest({})
+    }
+  }, [id, requestParams])
+
+  useEffect(() => {
+    if (model) {
+      form.setFieldsValue({ 
+        ...model,
+      })
+    }
+  }, [form, model])
+
+  const onFinish = async (values) => {
+    try {
+      setLoading(true);
+
+      let body = {
+        ...values,
+      };
+
+      if (id) {
+        body.id = id
+      }
+
+      const res = await HttpService.post(`${endpoints.estado}/guardar`, body);
+      respuestas(res);
+      if (res?.status === 200) {
+        navigate('/administracion/catalogos/estados')
+      }
+    } catch (error) {
+      console.log(error);
+      setLoading(false);
+    } finally {
+      setLoading(false);
+    }
+  }
+
+  if (modelLoading) {
+    return <Spin
+      size="large"
+      style={{ display: "block", margin: "auto", marginTop: "50px" }}
+    />
+  }
+
+
+  return (
+    <Form
+      layout="vertical"
+      name="basic"
+      form={form}
+      onFinish={onFinish}
+      onFinishFailed={() => { }}
+    >
+      <Row gutter={16}>
+        <Col span={24}>
+          <h2>Información del Estado</h2>
+        </Col>
+        <Col md={8} xs={12}>
+          <Form.Item
+            label="Nombre"
+            name="nombre"
+            rules={[
+              commonRules.requerido,
+            ]}
+          >
+            <Input />
+          </Form.Item>
+        </Col>
+        <Col md={8} xs={12}>
+          <Form.Item
+            label="Abreviatura"
+            name="abreviacion"
+            rules={[
+              commonRules.requerido,
+            ]}
+          >
+            <Input />
+          </Form.Item>
+        </Col>
+        <Col span={24}>
+          <Form.Item>
+            <Button
+              type="primary"
+              htmlType="submit"
+              style={{ marginTop: "30px" }}
+              loading={loading}
+            >
+              Guardar
+            </Button>
+          </Form.Item>
+        </Col>
+      </Row>
+    </Form>
+  )
+}
+
+export default EstadoDetalle

+ 158 - 0
src/views/catalogos/estados/Estados.jsx

@@ -0,0 +1,158 @@
+import { useRef, useState } from "react";
+import { Form, Modal, Tooltip, notification } from "antd";
+import { DeleteOutlined, PlusOutlined } from "@ant-design/icons";
+import { Tabla } from "../../../components";
+import { SimpleTableLayout } from "../../../components/layouts";
+import { ActionsButton } from "../../../components";
+import { isEllipsis } from "../../../utilities";
+import { Link, useNavigate } from "react-router-dom";
+import Formulario from "./Formulario";
+import HttpService from "../../../services/httpService";
+
+const endPoint = "estado";
+
+const Estados = () => {
+  let tablaRef = useRef(null);
+  const navigate = useNavigate();
+  const [form] = Form.useForm();
+  const [buscarParams, setBuscarParams] = useState({
+    padre: true,
+  });
+
+  const onFinish = (values) => {
+    const { q } = values;
+    const params = {
+      q: q ?? "",
+      padre: true,
+    };
+    setBuscarParams(params);    
+  };
+
+  const botones = [
+    {
+      onClick: () => navigate(`/administracion/catalogos/estados/agregar`),
+      props: { disabled: false, type: "primary", block: false },
+      text: "Nuevo",
+      icon: <PlusOutlined />,
+    },
+  ];
+
+  const linkText = (value, row, key) => (
+    <Link
+      to={`/administracion/catalogos/estados/editar?id=${row.id}`}
+      style={{ color: "black" }}
+    >
+      {isEllipsis(columns, key) ? (
+        <Tooltip title={value}>{value}</Tooltip>
+      ) : (
+        value
+      )}
+    </Link>
+  );
+
+  const eliminarRegistro = (nombre, id, url, alTerminar) => {
+    if (!id) return;
+    Modal.confirm({
+      title: "Eliminar",
+      content: `¿Está seguro de eliminar "${nombre}"?`,
+      icon: <DeleteOutlined style={{ color: "#ff0000" }} />,
+      okText: "Eliminar",
+      okButtonProps: {
+        type: "primary",
+        danger: true,
+      },
+      cancelText: "Cancelar",
+      onOk: async () => {
+        try {
+          let body = { id: id };
+          if (typeof id === "object") {
+            body = id;
+          }
+          const res = await HttpService.delete(url, body);
+          if (res && res.status === 200) {
+            notification.success({
+              message: "Éxito",
+              description: res?.mensaje,
+            });
+            alTerminar && alTerminar();
+          } else if (res?.status === 400) {
+            notification.error({
+              message: "Atención",
+              description: res?.mensaje,
+            });
+          }
+        } catch (error) {
+          console.log(error);
+          notification.error({
+            message: "Error",
+            description: error,
+          });
+          return "error";
+        }
+      },
+    });
+  };
+
+  const columns = [
+    {
+      title: "Acciones",
+      key: "correo",
+      dataIndex: "correo",
+      width: 100,
+      align: "center",
+      render: (_, item) => (
+        <ActionsButton
+          data={[
+            {
+              label: "Editar",
+              onClick: () =>
+                navigate(`/administracion/catalogos/estados/editar?id=${item?.id}`),
+            },
+            {
+              label: "Eliminar",
+              onClick: () => {
+                eliminarRegistro(item?.nombre, item?.id, endPoint+'/eliminar', () =>
+                  tablaRef?.current?.refresh()
+                );
+              },
+              danger: true,
+            },
+          ]}
+        />
+      ),
+    },
+    {
+      title: "Nombre",
+      key: "nombre",
+      dataIndex: "nombre",
+      render: linkText,
+    },
+    {
+      title: "Abreviatura",
+      key: "abreviacion",
+      dataIndex: "abreviacion",
+      render: linkText,
+    },
+  ];
+
+  return (
+    <SimpleTableLayout
+      btnGroup={{
+        btnGroup: botones,
+      }}
+    >
+      <Formulario
+        form={form}
+        onFinish={onFinish} 
+      />
+      <Tabla
+        columns={columns}
+        nameURL={endPoint}
+        extraParams={buscarParams}
+        scroll={{ x: "30vw" }}
+      />
+    </SimpleTableLayout>
+  );
+};
+
+export default Estados;

+ 55 - 0
src/views/catalogos/estados/Formulario.jsx

@@ -0,0 +1,55 @@
+import { Form, Input, Button, Row, Col } from 'antd'
+import PropTypes from 'prop-types'
+
+// const selectores = {
+//   consejoElectoral: {
+//     name: "v1/consejo-electoral",
+//   },
+// }
+
+const Formulario = ({
+  form,
+  onFinish,
+}) => {
+  return (
+    <Form
+      layout="vertical"
+      name="basic"
+      form={form}
+      initialValues={{ remember: true }}
+      onFinish={onFinish}
+      onFinishFailed={() => { }}
+    >
+      <Row gutter={16}>
+        <Col span={6}>
+
+
+          <Form.Item
+            label="Búsqueda"
+            name="q"
+          >
+            <Input />
+          </Form.Item>
+        </Col>
+        <Col span={6}>
+          <Form.Item>
+            <Button
+              type="primary"
+              htmlType="submit"
+              style={{ marginTop: "30px" }}
+            >
+              Buscar
+            </Button>
+          </Form.Item>
+        </Col>
+      </Row>  
+    </Form>
+  )
+}
+
+export default Formulario
+
+Formulario.propTypes = {
+  form: PropTypes.object.isRequired,
+  onFinish: PropTypes.func.isRequired,
+}

+ 7 - 0
src/views/catalogos/estados/index.js

@@ -0,0 +1,7 @@
+import Estados from './Estados'
+import EstadoDetalle from './EstadoDetalle'
+
+export {
+  Estados,
+  EstadoDetalle
+}

+ 144 - 0
src/views/catalogos/finMovilizaciones/FinMovilizacionDetalle.jsx

@@ -0,0 +1,144 @@
+import { Form, Input, Button, Spin, Row, Col } from 'antd'
+import { useEffect, useMemo, useState } from 'react'
+import HttpService from '../../../services/httpService'
+import { respuestas } from '../../../utilities'
+import { useNavigate } from 'react-router-dom'
+import { useQuery, useModel } from '../../../hooks'
+import { commonRules } from '../../../constants/rules'
+
+
+const endpoints = {
+  finMovilizacion: "fin-movilizacion",
+};
+
+const FinMovilizacionDetalleDetalle = () => {
+  const [form] = Form.useForm()
+  const navigate = useNavigate()
+  const [loading, setLoading] = useState(false)
+  const query = useQuery()
+  const id = query.get("id")
+  const [request, setRequest] = useState({})
+
+  // const extraParams = useMemo(() => ({
+  //   idAgenda: id,
+  // }), [id])
+
+  const requestParams = useMemo(() => ({
+    name: endpoints.finMovilizacion,
+    id,
+    // extraParams
+  }), [id])
+
+
+  const { model, modelLoading } = useModel(request)
+
+  useEffect(() => {
+    if (id) {
+      setRequest(requestParams)
+    }
+    return () => {
+      setRequest({})
+    }
+  }, [id, requestParams])
+
+  useEffect(() => {
+    if (model) {
+      form.setFieldsValue({ 
+        ...model,
+      })
+    }
+  }, [form, model])
+
+  const onFinish = async (values) => {
+    try {
+      setLoading(true);
+
+      let body = {
+        ...values,
+      };
+
+      if (id) {
+        body.id = id
+      }
+
+      const res = await HttpService.post(`${endpoints.finMovilizacion}/guardar`, body);
+      respuestas(res);
+      if (res?.status === 200) {
+        navigate('/administracion/catalogos/finMovilizaciones')
+      }
+    } catch (error) {
+      console.log(error);
+      setLoading(false);
+    } finally {
+      setLoading(false);
+    }
+  }
+
+  if (modelLoading) {
+    return <Spin
+      size="large"
+      style={{ display: "block", margin: "auto", marginTop: "50px" }}
+    />
+  }
+
+  const handleKeyPress = (event) => {
+    const charCode = event.which ? event.which : event.keyCode;
+    if (charCode < 48 || charCode > 57) {
+      event.preventDefault();
+    }
+  };
+
+
+
+  return (
+    <Form
+      layout="vertical"
+      name="basic"
+      form={form}
+      onFinish={onFinish}
+      onFinishFailed={() => { }}
+    >
+      <Row gutter={16}>
+        <Col span={24}>
+          <h2>Información del Fin de Movilizacion</h2>
+        </Col>
+        <Col md={8} xs={12}>
+          <Form.Item
+            label="Nombre"
+            name="nombre"
+            rules={[
+              commonRules.requerido,
+            ]}
+          >
+            <Input />
+          </Form.Item>
+        </Col>
+        <Col md={8} xs={12}>
+          <Form.Item
+            label="ID Sagarhpa"
+            name="idSagarhpa"
+            rules={[
+              commonRules.requerido,
+            ]}
+          >
+            <Input onKeyPress={handleKeyPress} maxLength={10}/>
+          </Form.Item>
+        </Col>
+        <Col span={24}>
+          <Form.Item>
+            <Button
+              type="primary"
+              htmlType="submit"
+              style={{ marginTop: "30px" }}
+              loading={loading}
+            >
+              Guardar
+            </Button>
+          </Form.Item>
+        </Col>
+      </Row>
+    </Form>
+  )
+}
+
+export default FinMovilizacionDetalleDetalle

+ 158 - 0
src/views/catalogos/finMovilizaciones/FinMovilizaciones.jsx

@@ -0,0 +1,158 @@
+import { useRef, useState } from "react";
+import { Form, Modal, Tooltip, notification } from "antd";
+import { DeleteOutlined, PlusOutlined } from "@ant-design/icons";
+import { Tabla } from "../../../components";
+import { SimpleTableLayout } from "../../../components/layouts";
+import { ActionsButton } from "../../../components";
+import { isEllipsis } from "../../../utilities";
+import { Link, useNavigate } from "react-router-dom";
+import Formulario from "./Formulario";
+import HttpService from "../../../services/httpService";
+
+const endPoint = "fin-movilizacion";
+
+const FinMovilizaciones = () => {
+  let tablaRef = useRef(null);
+  const navigate = useNavigate();
+  const [form] = Form.useForm();
+  const [buscarParams, setBuscarParams] = useState({
+    padre: true,
+  });
+
+  const onFinish = (values) => {
+    const { q } = values;
+    const params = {
+      q: q ?? "",
+      padre: true,
+    };
+    setBuscarParams(params);    
+  };
+
+  const botones = [
+    {
+      onClick: () => navigate(`/administracion/catalogos/finMovilizaciones/agregar`),
+      props: { disabled: false, type: "primary", block: false },
+      text: "Nuevo",
+      icon: <PlusOutlined />,
+    },
+  ];
+
+  const linkText = (value, row, key) => (
+    <Link
+      to={`/administracion/catalogos/finMovilizaciones/editar?id=${row.id}`}
+      style={{ color: "black" }}
+    >
+      {isEllipsis(columns, key) ? (
+        <Tooltip title={value}>{value}</Tooltip>
+      ) : (
+        value
+      )}
+    </Link>
+  );
+
+  const eliminarRegistro = (nombre, id, url, alTerminar) => {
+    if (!id) return;
+    Modal.confirm({
+      title: "Eliminar",
+      content: `¿Está seguro de eliminar "${nombre}"?`,
+      icon: <DeleteOutlined style={{ color: "#ff0000" }} />,
+      okText: "Eliminar",
+      okButtonProps: {
+        type: "primary",
+        danger: true,
+      },
+      cancelText: "Cancelar",
+      onOk: async () => {
+        try {
+          let body = { id: id };
+          if (typeof id === "object") {
+            body = id;
+          }
+          const res = await HttpService.delete(url, body);
+          if (res && res.status === 200) {
+            notification.success({
+              message: "Éxito",
+              description: res?.mensaje,
+            });
+            alTerminar && alTerminar();
+          } else if (res?.status === 400) {
+            notification.error({
+              message: "Atención",
+              description: res?.mensaje,
+            });
+          }
+        } catch (error) {
+          console.log(error);
+          notification.error({
+            message: "Error",
+            description: error,
+          });
+          return "error";
+        }
+      },
+    });
+  };
+
+  const columns = [
+    {
+      title: "Acciones",
+      key: "correo",
+      dataIndex: "correo",
+      width: 100,
+      align: "center",
+      render: (_, item) => (
+        <ActionsButton
+          data={[
+            {
+              label: "Editar",
+              onClick: () =>
+                navigate(`/administracion/catalogos/finMovilizaciones/editar?id=${item?.id}`),
+            },
+            {
+              label: "Eliminar",
+              onClick: () => {
+                eliminarRegistro(item?.nombre, item?.id, endPoint+'/eliminar', () =>
+                  tablaRef?.current?.refresh()
+                );
+              },
+              danger: true,
+            },
+          ]}
+        />
+      ),
+    },
+    {
+      title: "Nombre",
+      key: "nombre",
+      dataIndex: "nombre",
+      render: linkText,
+    },
+    {
+      title: "ID Sagarhpa",
+      key: "idSagarhpa",
+      dataIndex: "idSagarhpa",
+      render: linkText,
+    },
+  ];
+
+  return (
+    <SimpleTableLayout
+      btnGroup={{
+        btnGroup: botones,
+      }}
+    >
+      <Formulario
+        form={form}
+        onFinish={onFinish} 
+      />
+      <Tabla
+        columns={columns}
+        nameURL={endPoint}
+        extraParams={buscarParams}
+        scroll={{ x: "30vw" }}
+      />
+    </SimpleTableLayout>
+  );
+};
+
+export default FinMovilizaciones;

+ 55 - 0
src/views/catalogos/finMovilizaciones/Formulario.jsx

@@ -0,0 +1,55 @@
+import { Form, Input, Button, Row, Col } from 'antd'
+import PropTypes from 'prop-types'
+
+// const selectores = {
+//   consejoElectoral: {
+//     name: "v1/consejo-electoral",
+//   },
+// }
+
+const Formulario = ({
+  form,
+  onFinish,
+}) => {
+  return (
+    <Form
+      layout="vertical"
+      name="basic"
+      form={form}
+      initialValues={{ remember: true }}
+      onFinish={onFinish}
+      onFinishFailed={() => { }}
+    >
+      <Row gutter={16}>
+        <Col span={6}>
+
+
+          <Form.Item
+            label="Búsqueda"
+            name="q"
+          >
+            <Input />
+          </Form.Item>
+        </Col>
+        <Col span={6}>
+          <Form.Item>
+            <Button
+              type="primary"
+              htmlType="submit"
+              style={{ marginTop: "30px" }}
+            >
+              Buscar
+            </Button>
+          </Form.Item>
+        </Col>
+      </Row>  
+    </Form>
+  )
+}
+
+export default Formulario
+
+Formulario.propTypes = {
+  form: PropTypes.object.isRequired,
+  onFinish: PropTypes.func.isRequired,
+}

+ 7 - 0
src/views/catalogos/finMovilizaciones/index.js

@@ -0,0 +1,7 @@
+import FinMovilizaciones from './FinMovilizaciones'
+import FinMovilizacionDetalle from './FinMovilizacionDetalle'
+
+export {
+  FinMovilizaciones,
+  FinMovilizacionDetalle
+}

+ 55 - 0
src/views/catalogos/municipios/Formulario.jsx

@@ -0,0 +1,55 @@
+import { Form, Input, Button, Row, Col } from 'antd'
+import PropTypes from 'prop-types'
+
+// const selectores = {
+//   consejoElectoral: {
+//     name: "v1/consejo-electoral",
+//   },
+// }
+
+const Formulario = ({
+  form,
+  onFinish,
+}) => {
+  return (
+    <Form
+      layout="vertical"
+      name="basic"
+      form={form}
+      initialValues={{ remember: true }}
+      onFinish={onFinish}
+      onFinishFailed={() => { }}
+    >
+      <Row gutter={16}>
+        <Col span={6}>
+
+
+          <Form.Item
+            label="Búsqueda"
+            name="q"
+          >
+            <Input />
+          </Form.Item>
+        </Col>
+        <Col span={6}>
+          <Form.Item>
+            <Button
+              type="primary"
+              htmlType="submit"
+              style={{ marginTop: "30px" }}
+            >
+              Buscar
+            </Button>
+          </Form.Item>
+        </Col>
+      </Row>  
+    </Form>
+  )
+}
+
+export default Formulario
+
+Formulario.propTypes = {
+  form: PropTypes.object.isRequired,
+  onFinish: PropTypes.func.isRequired,
+}

+ 134 - 0
src/views/catalogos/municipios/MunicipioDetalle.jsx

@@ -0,0 +1,134 @@
+import { Form, Input, Button, Spin, Row, Col } from 'antd';
+import { useEffect, useMemo, useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import HttpService from '../../../services/httpService';
+import { useQuery, useModel } from '../../../hooks';
+import { commonRules } from '../../../constants/rules';
+import { Select } from "../../../components";
+
+const endpoints = {
+  municipio: "municipio",
+};
+
+const MunicipioDetalle = () => {
+  const [form] = Form.useForm();
+  const navigate = useNavigate();
+  const [loading, setLoading] = useState(false);
+  const query = useQuery();
+  const id = query.get("id");
+  const [estado, setEstado] = useState("");
+  const [timer, setTimer] = useState(null);
+
+  const extraParams = useMemo(() => ({
+    idMunicipio: id,
+    idEstado: id,
+  }), [id]);
+
+  const estadoExtraParams = useMemo(() => {
+    let params = {};
+    if (estado !== "") {
+      params.nombre = estado;
+    }
+    return params;
+  }, [estado]);
+
+  const requestParams = useMemo(() => ({
+    name: endpoints.municipio,
+    id: id,
+    extraParams: extraParams,
+    expand: 'estado',
+  }), [id, extraParams]);
+
+  const { model, modelLoading } = useModel(requestParams);
+
+  useEffect(() => {
+    if (model) {
+      form.setFieldsValue({
+        ...model,
+        idEstado: model.estado?.id,
+      });
+    }
+  }, [form, model]);
+
+  const onFinish = async (values) => {
+    try {
+      setLoading(true);
+      const body = { ...values, id };
+      const res = await HttpService.post(`${endpoints.municipio}/guardar`, body);
+      if (res?.status === 200) {
+        navigate('/administracion/catalogos/municipios');
+      }
+    } catch (error) {
+      console.log(error);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  if (modelLoading) {
+    return <Spin size="large" style={{ display: "block", margin: "auto", marginTop: "50px" }} />;
+  }
+
+  const onSearch = (value) => {
+    clearTimeout(timer);
+    const newTimer = setTimeout(() => {
+      setEstado(value);
+    }, 300);
+    setTimer(newTimer);
+  };
+
+  return (
+    <Form
+      layout="vertical"
+      name="basic"
+      form={form}
+      onFinish={onFinish}
+      onFinishFailed={() => {}}
+    >
+      <Row gutter={16}>
+        <Col span={24}>
+          <h2>Información del Municipio</h2>
+        </Col>
+        <Col md={8} xs={12}>
+          <Form.Item
+            label="Nombre"
+            name="nombre"
+            rules={[commonRules.requerido]}
+          >
+            <Input />
+          </Form.Item>
+        </Col>
+        <Col md={8} xs={12}>
+          <Form.Item
+            label="Estado"
+            name="idEstado"
+            rules={[commonRules.requerido]}
+          >
+            <Select
+              modelsParams={{ name: 'estado', ordenar: 'nombre' }}
+              labelProp="nombre"
+              valueProp="id"
+              append={[model?.estado]}
+              onSearch={onSearch}
+              extraParams={estadoExtraParams}
+            />
+          </Form.Item>
+        </Col>
+        <Col span={24}>
+          <Form.Item>
+            <Button
+              type="primary"
+              htmlType="submit"
+              style={{ marginTop: "30px" }}
+              loading={loading}
+            >
+              Guardar
+            </Button>
+          </Form.Item>
+        </Col>
+      </Row>
+    </Form>
+  );
+};
+
+export default MunicipioDetalle;

+ 159 - 0
src/views/catalogos/municipios/Municipios.jsx

@@ -0,0 +1,159 @@
+import { useRef, useState } from "react";
+import { Form, Modal, Tooltip, notification } from "antd";
+import { DeleteOutlined, PlusOutlined } from "@ant-design/icons";
+import { Tabla } from "../../../components";
+import { SimpleTableLayout } from "../../../components/layouts";
+import { ActionsButton } from "../../../components";
+import { isEllipsis } from "../../../utilities";
+import { Link, useNavigate } from "react-router-dom";
+import Formulario from "./Formulario";
+import HttpService from "../../../services/httpService";
+
+const endPoint = "municipio";
+
+const Municipios = () => {
+  let tablaRef = useRef(null);
+  const navigate = useNavigate();
+  const [form] = Form.useForm();
+  const [buscarParams, setBuscarParams] = useState({
+    padre: true,
+  });
+
+  const onFinish = (values) => {
+    const { q } = values;
+    const params = {
+      q: q ?? "",
+      padre: true,
+    };
+    setBuscarParams(params);    
+  };
+
+  const botones = [
+    {
+      onClick: () => navigate(`/administracion/catalogos/municipios/agregar`),
+      props: { disabled: false, type: "primary", block: false },
+      text: "Nuevo",
+      icon: <PlusOutlined />,
+    },
+  ];
+
+  const linkText = (value, row, key) => (
+    <Link
+      to={`/administracion/catalogos/municipios/editar?id=${row.id}`}
+      style={{ color: "black" }}
+    >
+      {isEllipsis(columns, key) ? (
+        <Tooltip title={value}>{value}</Tooltip>
+      ) : (
+        value
+      )}
+    </Link>
+  );
+
+  const eliminarRegistro = (nombre, id, url, alTerminar) => {
+    if (!id) return;
+    Modal.confirm({
+      title: "Eliminar",
+      content: `¿Está seguro de eliminar "${nombre}"?`,
+      icon: <DeleteOutlined style={{ color: "#ff0000" }} />,
+      okText: "Eliminar",
+      okButtonProps: {
+        type: "primary",
+        danger: true,
+      },
+      cancelText: "Cancelar",
+      onOk: async () => {
+        try {
+          let body = { id: id };
+          if (typeof id === "object") {
+            body = id;
+          }
+          const res = await HttpService.delete(url, body);
+          if (res && res.status === 200) {
+            notification.success({
+              message: "Éxito",
+              description: res?.mensaje,
+            });
+            alTerminar && alTerminar();
+          } else if (res?.status === 400) {
+            notification.error({
+              message: "Atención",
+              description: res?.mensaje,
+            });
+          }
+        } catch (error) {
+          console.log(error);
+          notification.error({
+            message: "Error",
+            description: error,
+          });
+          return "error";
+        }
+      },
+    });
+  };
+
+  const columns = [
+    {
+      title: "Acciones",
+      key: "acciones",
+      dataIndex: "acciones",
+      width: 100,
+      align: "center",
+      render: (_, item) => (
+        <ActionsButton
+          data={[
+            {
+              label: "Editar",
+              onClick: () =>
+                navigate(`/administracion/catalogos/municipios/editar?id=${item?.id}`),
+            },
+            {
+              label: "Eliminar",
+              onClick: () => {
+                eliminarRegistro(item?.nombre, item?.id, endPoint+'/eliminar', () =>
+                  tablaRef?.current?.refresh()
+                );
+              },
+              danger: true,
+            },
+          ]}
+        />
+      ),
+    },
+    {
+      title: "Nombre",
+      key: "nombre",
+      dataIndex: "nombre",
+      render: linkText,
+    },
+    {
+      title: "Estado",
+      key: "estado",
+      dataIndex: ["estado", "nombre"], 
+      render: (value) => value,
+    },
+  ];
+
+  return (
+    <SimpleTableLayout
+      btnGroup={{
+        btnGroup: botones,
+      }}
+    >
+      <Formulario
+        form={form}
+        onFinish={onFinish} 
+      />
+      <Tabla
+        columns={columns}
+        nameURL={endPoint}
+        expand="estado"
+        extraParams={buscarParams}
+        scroll={{ x: "30vw" }}
+      />
+    </SimpleTableLayout>
+  );
+};
+
+export default Municipios;

+ 7 - 0
src/views/catalogos/municipios/index.js

@@ -0,0 +1,7 @@
+import Municipios from './Municipios'
+import MunicipioDetalle from './MunicipioDetalle'
+
+export {
+  Municipios,
+  MunicipioDetalle
+}

+ 55 - 0
src/views/catalogos/tipoMovilizaciones/Formulario.jsx

@@ -0,0 +1,55 @@
+import { Form, Input, Button, Row, Col } from 'antd'
+import PropTypes from 'prop-types'
+
+// const selectores = {
+//   consejoElectoral: {
+//     name: "v1/consejo-electoral",
+//   },
+// }
+
+const Formulario = ({
+  form,
+  onFinish,
+}) => {
+  return (
+    <Form
+      layout="vertical"
+      name="basic"
+      form={form}
+      initialValues={{ remember: true }}
+      onFinish={onFinish}
+      onFinishFailed={() => { }}
+    >
+      <Row gutter={16}>
+        <Col span={6}>
+
+
+          <Form.Item
+            label="Búsqueda"
+            name="q"
+          >
+            <Input />
+          </Form.Item>
+        </Col>
+        <Col span={6}>
+          <Form.Item>
+            <Button
+              type="primary"
+              htmlType="submit"
+              style={{ marginTop: "30px" }}
+            >
+              Buscar
+            </Button>
+          </Form.Item>
+        </Col>
+      </Row>  
+    </Form>
+  )
+}
+
+export default Formulario
+
+Formulario.propTypes = {
+  form: PropTypes.object.isRequired,
+  onFinish: PropTypes.func.isRequired,
+}

+ 144 - 0
src/views/catalogos/tipoMovilizaciones/TipoMovilizacionDetalle.jsx

@@ -0,0 +1,144 @@
+import { Form, Input, Button, Spin, Row, Col } from 'antd'
+import { useEffect, useMemo, useState } from 'react'
+import HttpService from '../../../services/httpService'
+import { respuestas } from '../../../utilities'
+import { useNavigate } from 'react-router-dom'
+import { useQuery, useModel } from '../../../hooks'
+import { commonRules } from '../../../constants/rules'
+
+
+const endpoints = {
+  tipoMovilizacion: "tipo-movilizacion",
+};
+
+const TipoMovilizacionDetalleDetalle = () => {
+  const [form] = Form.useForm()
+  const navigate = useNavigate()
+  const [loading, setLoading] = useState(false)
+  const query = useQuery()
+  const id = query.get("id")
+  const [request, setRequest] = useState({})
+
+  // const extraParams = useMemo(() => ({
+  //   idAgenda: id,
+  // }), [id])
+
+  const requestParams = useMemo(() => ({
+    name: endpoints.tipoMovilizacion,
+    id,
+    // extraParams
+  }), [id])
+
+
+  const { model, modelLoading } = useModel(request)
+
+  useEffect(() => {
+    if (id) {
+      setRequest(requestParams)
+    }
+    return () => {
+      setRequest({})
+    }
+  }, [id, requestParams])
+
+  useEffect(() => {
+    if (model) {
+      form.setFieldsValue({ 
+        ...model,
+      })
+    }
+  }, [form, model])
+
+  const onFinish = async (values) => {
+    try {
+      setLoading(true);
+
+      let body = {
+        ...values,
+      };
+
+      if (id) {
+        body.id = id
+      }
+
+      const res = await HttpService.post(`${endpoints.tipoMovilizacion}/guardar`, body);
+      respuestas(res);
+      if (res?.status === 200) {
+        navigate('/administracion/catalogos/tipoMovilizaciones')
+      }
+    } catch (error) {
+      console.log(error);
+      setLoading(false);
+    } finally {
+      setLoading(false);
+    }
+  }
+
+  if (modelLoading) {
+    return <Spin
+      size="large"
+      style={{ display: "block", margin: "auto", marginTop: "50px" }}
+    />
+  }
+
+  const handleKeyPress = (event) => {
+    const charCode = event.which ? event.which : event.keyCode;
+    if (charCode < 48 || charCode > 57) {
+      event.preventDefault();
+    }
+  };
+
+
+
+  return (
+    <Form
+      layout="vertical"
+      name="basic"
+      form={form}
+      onFinish={onFinish}
+      onFinishFailed={() => { }}
+    >
+      <Row gutter={16}>
+        <Col span={24}>
+          <h2>Información del Tipo de Movilizacion</h2>
+        </Col>
+        <Col md={8} xs={12}>
+          <Form.Item
+            label="Nombre"
+            name="nombre"
+            rules={[
+              commonRules.requerido,
+            ]}
+          >
+            <Input />
+          </Form.Item>
+        </Col>
+        <Col md={8} xs={12}>
+          <Form.Item
+            label="ID Sagarhpa"
+            name="idSagarhpa"
+            rules={[
+              commonRules.requerido,
+            ]}
+          >
+            <Input onKeyPress={handleKeyPress} maxLength={10}/>
+          </Form.Item>
+        </Col>
+        <Col span={24}>
+          <Form.Item>
+            <Button
+              type="primary"
+              htmlType="submit"
+              style={{ marginTop: "30px" }}
+              loading={loading}
+            >
+              Guardar
+            </Button>
+          </Form.Item>
+        </Col>
+      </Row>
+    </Form>
+  )
+}
+
+export default TipoMovilizacionDetalleDetalle

+ 158 - 0
src/views/catalogos/tipoMovilizaciones/TipoMovilizaciones.jsx

@@ -0,0 +1,158 @@
+import { useRef, useState } from "react";
+import { Form, Modal, Tooltip, notification } from "antd";
+import { DeleteOutlined, PlusOutlined } from "@ant-design/icons";
+import { Tabla } from "../../../components";
+import { SimpleTableLayout } from "../../../components/layouts";
+import { ActionsButton } from "../../../components";
+import { isEllipsis } from "../../../utilities";
+import { Link, useNavigate } from "react-router-dom";
+import Formulario from "./Formulario";
+import HttpService from "../../../services/httpService";
+
+const endPoint = "tipo-movilizacion";
+
+const TipoMovilizaciones = () => {
+  let tablaRef = useRef(null);
+  const navigate = useNavigate();
+  const [form] = Form.useForm();
+  const [buscarParams, setBuscarParams] = useState({
+    padre: true,
+  });
+
+  const onFinish = (values) => {
+    const { q } = values;
+    const params = {
+      q: q ?? "",
+      padre: true,
+    };
+    setBuscarParams(params);    
+  };
+
+  const botones = [
+    {
+      onClick: () => navigate(`/administracion/catalogos/tipoMovilizaciones/agregar`),
+      props: { disabled: false, type: "primary", block: false },
+      text: "Nuevo",
+      icon: <PlusOutlined />,
+    },
+  ];
+
+  const linkText = (value, row, key) => (
+    <Link
+      to={`/administracion/catalogos/tipoMovilizaciones/editar?id=${row.id}`}
+      style={{ color: "black" }}
+    >
+      {isEllipsis(columns, key) ? (
+        <Tooltip title={value}>{value}</Tooltip>
+      ) : (
+        value
+      )}
+    </Link>
+  );
+
+  const eliminarRegistro = (nombre, id, url, alTerminar) => {
+    if (!id) return;
+    Modal.confirm({
+      title: "Eliminar",
+      content: `¿Está seguro de eliminar "${nombre}"?`,
+      icon: <DeleteOutlined style={{ color: "#ff0000" }} />,
+      okText: "Eliminar",
+      okButtonProps: {
+        type: "primary",
+        danger: true,
+      },
+      cancelText: "Cancelar",
+      onOk: async () => {
+        try {
+          let body = { id: id };
+          if (typeof id === "object") {
+            body = id;
+          }
+          const res = await HttpService.delete(url, body);
+          if (res && res.status === 200) {
+            notification.success({
+              message: "Éxito",
+              description: res?.mensaje,
+            });
+            alTerminar && alTerminar();
+          } else if (res?.status === 400) {
+            notification.error({
+              message: "Atención",
+              description: res?.mensaje,
+            });
+          }
+        } catch (error) {
+          console.log(error);
+          notification.error({
+            message: "Error",
+            description: error,
+          });
+          return "error";
+        }
+      },
+    });
+  };
+
+  const columns = [
+    {
+      title: "Acciones",
+      key: "correo",
+      dataIndex: "correo",
+      width: 100,
+      align: "center",
+      render: (_, item) => (
+        <ActionsButton
+          data={[
+            {
+              label: "Editar",
+              onClick: () =>
+                navigate(`/administracion/catalogos/tipoMovilizaciones/editar?id=${item?.id}`),
+            },
+            {
+              label: "Eliminar",
+              onClick: () => {
+                eliminarRegistro(item?.nombre, item?.id, endPoint+'/eliminar', () =>
+                  tablaRef?.current?.refresh()
+                );
+              },
+              danger: true,
+            },
+          ]}
+        />
+      ),
+    },
+    {
+      title: "Nombre",
+      key: "nombre",
+      dataIndex: "nombre",
+      render: linkText,
+    },
+    {
+      title: "ID Sagarhpa",
+      key: "idSagarhpa",
+      dataIndex: "idSagarhpa",
+      render: linkText,
+    },
+  ];
+
+  return (
+    <SimpleTableLayout
+      btnGroup={{
+        btnGroup: botones,
+      }}
+    >
+      <Formulario
+        form={form}
+        onFinish={onFinish} 
+      />
+      <Tabla
+        columns={columns}
+        nameURL={endPoint}
+        extraParams={buscarParams}
+        scroll={{ x: "30vw" }}
+      />
+    </SimpleTableLayout>
+  );
+};
+
+export default TipoMovilizaciones;

+ 7 - 0
src/views/catalogos/tipoMovilizaciones/index.js

@@ -0,0 +1,7 @@
+import TipoMovilizaciones from './TipoMovilizaciones'
+import TipoMovilizacionDetalle from './TipoMovilizacionDetalle'
+
+export {
+  TipoMovilizaciones,
+  TipoMovilizacionDetalle
+}