Compare commits

...

13 Commits

Author SHA1 Message Date
Elizabeth 556a8a2848 Front completo :D 2024-05-01 15:10:23 -05:00
Andres Alvarez 77058335a3 cambio ingreso 2024-04-01 09:38:18 -05:00
Andres Alvarez 3b7faaae03 Conexon servicios 2024-03-21 08:22:11 -05:00
Andres Alvarez 2018f925cc correccion registro 2024-02-15 19:29:15 -05:00
Andres Alvarez bfa3be924a correcciones 2024-02-14 08:22:59 -05:00
Andres Alvarez 31dc8c9478 Merge branch 'products' into develop 2024-02-08 16:14:16 -05:00
Andres Alvarez 7406a5d855 agregar clientes en factura 2024-02-08 15:54:31 -05:00
EliAlarcon dce24eb54b Cambio traducción en nav de product 2024-02-08 15:40:22 -05:00
EliAlarcon c5f3ea74d4 Formulario Registro Producto y organización de ventanas de producto 2024-02-08 15:35:19 -05:00
Andres Alvarez 3f21087779 creacion de factura 2024-02-07 08:36:51 -05:00
EliAlarcon d52c64c102 Componente BuscarProducto 2024-02-05 18:09:33 -05:00
Elizabeth da9758d875 Tabla Detalle Productos 2024-02-02 16:50:21 -05:00
EliAlarcon f8b1b795a3 Creación componente producto 2024-02-02 10:10:28 -05:00
75 changed files with 4156 additions and 706 deletions

2
.gitignore vendored
View File

@ -35,5 +35,5 @@ yarn-error.log*
!.yarn/versions
.pnp.*
yarn.lock
/yarn.lock
package-lock.json

View File

@ -25,6 +25,7 @@
"draft-js": "0.11.7",
"draftjs-to-html": "0.9.1",
"firebase": "10.7.1",
"formik": "^2.4.5",
"framer-motion": "10.18.0",
"history": "5.3.0",
"i18next": "23.7.16",
@ -56,7 +57,9 @@
"stylis": "4.3.1",
"stylis-plugin-rtl": "2.1.1",
"type-fest": "4.9.0",
"uuid": "^9.0.1",
"web-vitals": "3.5.1",
"yup": "^1.3.3",
"zod": "3.22.4"
},
"peerDependencies": {

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -32,7 +32,7 @@ function FuseLoading(props: FuseLoadingProps) {
className="-mb-16 text-13 font-medium sm:text-20"
color="text.secondary"
>
Loading
Cargando
</Typography>
<Box
id="spinner"

View File

@ -171,7 +171,8 @@ export const defaultThemeOptions = {
MuiDialog: {
styleOverrides: {
paper: {
borderRadius: 16
borderRadius: 10,
padding: 5
}
}
},

View File

@ -32751,8 +32751,8 @@
"password": "admin",
"role": "admin",
"data": {
"displayName": "Abbott Keitch",
"photoURL": "assets/images/avatars/brian-hughes.jpg",
"displayName": "Andres Alvarez",
"photoURL": "assets/images/avatars/user.jpg",
"email": "admin@qsoftec.com",
"settings": {
"layout": {},

View File

@ -31,9 +31,20 @@ export type SignInPayload = {
};
export type SignUpPayload = {
displayName: string;
usuUsuario: string;
usuNombre: string;
detCodigo: number;
empIdentificacion: string;
empRazonSocial: string;
empNombreComercial: string;
empContacto: string;
empDireccion: string;
empMail: string;
empCodContribuyente: string;
empDescripcion: string;
empLlevaContabilidad: string;
password: string;
email: string;
passwordConfirm: string;
};
type AuthContext = {

View File

@ -1,8 +1,10 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */
import { useState, useEffect, useCallback } from 'react';
import axios, { AxiosError, AxiosResponse } from 'axios';
import jwtDecode, { JwtPayload } from 'jwt-decode';
import _ from '@lodash';
import { PartialDeep } from 'type-fest';
import { loginIn } from 'src/app/services/user.service';
const defaultAuthConfig = {
tokenStorageKey: 'jwt_access_token',
@ -47,6 +49,10 @@ export type JwtAuth<User, SignInPayload, SignUpPayload> = {
setIsLoading: (isLoading: boolean) => void;
};
type UserLogin = {
user: string;
password: string;
};
/**
* useJwtAuth hook
* Description: This hook handles the authentication flow using JWT
@ -94,7 +100,7 @@ const useJwtAuth = <User, SignInPayload, SignUpPayload>(
/**
* Handle sign-in success
*/
const handleSignInSuccess = useCallback((userData: User, accessToken: string) => {
const handleSignInSuccess = useCallback((userData: User, accessToken) => {
setSession(accessToken);
setIsAuthenticated(true);
@ -215,9 +221,18 @@ const useJwtAuth = <User, SignInPayload, SignUpPayload>(
/**
* Sign in
*/
const signIn = async (credentials: SignInPayload) => {
const signIn = async (credentials: UserLogin) => {
const response = axios.post(authConfig.signInUrl, credentials);
/* const {error, bodyOut} = await loginIn(credentials.email, credentials.password);
if(error.codigo === '0'){
const userData = bodyOut.data[0];
handleSignInSuccess(userData);
}
return bodyOut.data[0]; */
response.then(
(res: AxiosResponse<{ user: User; access_token: string }>) => {
const userData = res?.data?.user;
@ -236,7 +251,7 @@ const useJwtAuth = <User, SignInPayload, SignUpPayload>(
}
);
return response;
// return response;
};
/**

View File

@ -0,0 +1,49 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */
import { v4 as uuidV4 } from 'uuid';
interface Header {
dispositivo: string;
canal: string | null;
medio: string | null;
aplicacion: string;
tipoTransaccion: string | null;
usuario: string | number;
uuid: string;
fechaHora: string | null;
idioma: string | null;
empresa: string | null;
geolocalizacion: string | null;
}
interface User {
usercode: string | number;
}
const user: User = JSON.parse(localStorage.getItem('user'));
const headerIn: Header = {
dispositivo: 'WeLaptop',
canal: null,
medio: null,
aplicacion: 'SINCOARV2',
tipoTransaccion: null,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
usuario: user ? user.usercode : 'user',
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
uuid: uuidV4(),
fechaHora: null,
idioma: null,
empresa: null,
geolocalizacion: null
};
const url = 'http://services.qsoftec.com:18080/inventario-rs-services-1.0-SNAPSHOT/servicios'; // test
// let url = 'https://tramitesarmas.ccffaa.mil.ec/sincoar/servicios' //produccion
const config = {
api: url,
headerIn
};
export default config;

View File

@ -2,7 +2,8 @@ const locale = {
APPLICATIONS: 'Applications',
EXAMPLE: 'Example',
DASHBOARD: 'Tablero',
INVOICE: 'Factura'
INVOICE: 'Factura',
PRODUCT: 'Producto'
};
export default locale;

View File

@ -14,18 +14,10 @@ i18next.addResourceBundle('es', 'navigation', es);
* The navigationConfig object is an array of navigation items for the Fuse application.
*/
const navigationConfig: FuseNavItemType[] = [
{
id: 'example-component',
title: 'Example',
translate: 'EXAMPLE',
type: 'item',
icon: 'heroicons-outline:star',
url: 'example'
},
{
id: 'dashboard-component',
title: 'Tablero',
translate: 'TABLERO',
title: 'Dashboard',
translate: 'DASHBOARD',
type: 'item',
icon: 'heroicons-outline:star',
url: 'dashboards/project'
@ -37,8 +29,35 @@ const navigationConfig: FuseNavItemType[] = [
type: 'item',
icon: 'heroicons-outline:document-text',
url: 'invoice/list'
},
{
id: 'producto-component',
title: 'Producto',
translate: 'PRODUCT',
type: 'item',
icon: 'heroicons-outline:clipboard-check',
url: 'product/list'
},
{
id: 'cliente-component',
title: 'Cliente',
translate: 'Cliente',
type: 'item',
icon: 'heroicons-outline:user',
url: 'client/list'
},
{
id: 'firma-component',
title: 'signature',
translate: 'Firma',
type: 'item',
icon: 'heroicons-outline:pencil',
url: 'signature/form'
}
];
export default navigationConfig;

View File

@ -7,12 +7,22 @@ import SignInConfig from '../main/sign-in/SignInConfig';
import SignUpConfig from '../main/sign-up/SignUpConfig';
import SignOutConfig from '../main/sign-out/SignOutConfig';
import Error404Page from '../main/404/Error404Page';
import ExampleConfig from '../main/example/ExampleConfig';
import ProjectDashboardAppConfig from '../main/dashboard/project/ProjectDashboardAppConfig';
import ProductoConfigs from '../main/producto/ProductoConfig';
import InvoiceConfigs from '../main/invoice/InvoiceConfig';
import ClientConfig from '../main/client/ClientConfig';
import firmaConfigs from '../main/firmaElectronica/FirmaConfig';
const routeConfigs: FuseRouteConfigsType = [ExampleConfig, SignOutConfig, SignInConfig, SignUpConfig, ProjectDashboardAppConfig, ...InvoiceConfigs];
const routeConfigs: FuseRouteConfigsType = [
SignOutConfig,
SignInConfig,
SignUpConfig,
ProjectDashboardAppConfig,
...ProductoConfigs,
...InvoiceConfigs,
...ClientConfig,
...firmaConfigs
];
/**
* The routes of the application.
@ -21,7 +31,7 @@ const routes: FuseRoutesType = [
...FuseUtils.generateRoutesFromConfigs(routeConfigs, settingsConfig.defaultAuth),
{
path: '/',
element: <Navigate to="/example" />,
element: <Navigate to="/dashboards/project" />,
auth: settingsConfig.defaultAuth
},
{

View File

@ -16,8 +16,8 @@ function Error404Page() {
>
<Box
component="svg"
width="100%"
height="100%"
width="80%"
height="80%"
viewBox="0 0 1075 585"
fill="none"
preserveAspectRatio="xMidYMax slice"
@ -243,7 +243,7 @@ function Error404Page() {
color="text.secondary"
className="mt-8 text-center text-lg font-medium tracking-tight md:text-xl"
>
The page you requested could not be found.
La página a la que se quiere acceder no existe.
</Typography>
</motion.div>
<Link

View File

@ -0,0 +1,37 @@
import DemoContent from '@fuse/core/DemoContent';
import FusePageSimple from '@fuse/core/FusePageSimple';
import { useTranslation } from 'react-i18next';
import { styled } from '@mui/material/styles';
import ClientRender from './ClientRender';
const Root = styled(FusePageSimple)(({ theme }) => ({
'& .FusePageSimple-header': {
backgroundColor: theme.palette.background.paper,
borderBottomWidth: 1,
borderStyle: 'solid',
borderColor: theme.palette.divider
},
'& .FusePageSimple-content': {},
'& .FusePageSimple-sidebarHeader': {},
'& .FusePageSimple-sidebarContent': {}
}));
function Client() {
const { t } = useTranslation('clients');
return(
<Root
header={
<div className='p-24'>
<h4>{t('CLIENTES')}</h4>
</div>
}
content={
<ClientRender></ClientRender>
}
/>
)
}
export default Client;

View File

@ -0,0 +1,10 @@
import { FuseRouteConfigsType } from "@fuse/utils/FuseUtils";
import DetalleClienteConfig from "./detalleCliente/DetalleClienteConfig";
import FormularioClienteConfig from "./formularioCliente/FormularioClienteConfig";
const clientConfigs: FuseRouteConfigsType = [
DetalleClienteConfig,
FormularioClienteConfig
]
export default clientConfigs;

View File

@ -0,0 +1,11 @@
import DetalleCliente from "./detalleCliente/DetalleCliente"
const ClientRender = () => {
return (
<div className="w-full p-12 pt-16 sm:pt-24 lg:ltr:pr-0 lg:rtl:pl-0">
<DetalleCliente></DetalleCliente>
</div>
)
}
export default ClientRender;

View File

@ -0,0 +1,9 @@
import ListDetalleClienteRender from './DetalleClienteRender'
const DetalleCliente = () => {
return (
<ListDetalleClienteRender/>
)
}
export default DetalleCliente;

View File

@ -0,0 +1,19 @@
import {lazy} from 'react';
const Cliente = lazy(() => import('./DetalleCliente'));
const DetalleClienteConfig = {
settings: {
layout: {
config: {}
}
},
routes: [
{
path: 'client/list',
element: <Cliente/>
}
]
};
export default DetalleClienteConfig;

View File

@ -0,0 +1,185 @@
import FusePageSimple from '@fuse/core/FusePageSimple';
import FuseSvgIcon from '@fuse/core/FuseSvgIcon';
import {
Button,
IconButton,
Paper,
Table,
TableBody,
TableCell,
TableHead,
TablePagination,
TableRow,
Typography,
styled
} from "@mui/material";
import clsx from 'clsx';
import { format } from 'date-fns';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import ResponsiveDialog from 'src/components/widgets/DialogDelete';
const Root = styled(FusePageSimple)(({ theme }) => ({
'& .FusePageSimple-header': {
backgroundColor: theme.palette.background.paper,
boxShadow: `inset 0 0 0 1px ${theme.palette.divider}`
}
}));
function ListDetalleClienteRender() {
const { t } = useTranslation('clients');
const columns = [
'id',
'Nombre Comercial',
'Razon Social',
'Identificación',
'Teléfono',
'Correo Electrónico',
'Acciones',
];
const rows = [
{
id: 111,
nombreComercial:'Susana Noroña',
razonSocial: 'Susana Noroña',
identificacion: '1720336948',
telefono: '0984294253',
correo: 'cammmmm@test.com',
action:true,
},
{
id: 555,
nombreComercial:'Camila Morales',
razonSocial: 'Camila Morales',
identificacion: '1720336948',
telefono: '0984294253',
correo: 'cammmmm@test.com',
action:true,
},
{
id: 777,
nombreComercial:'Cristian Hernandez',
razonSocial: 'Cristian Hernandez',
identificacion: '1720336948',
telefono: '0984294253',
correo: 'cammmmm@test.com',
action:true,
},
];
return (
<Root
header={
<div className="p-24">
<h4>{t('CLIENTES')}</h4>
</div>
}
content={
<Paper className="flex flex-col flex-auto p-24 shadow rounded-2 overflow-hidden m-10">
<div className="flex md:flex-row justify-between flex-col">
<div>
<Typography className="mr-16 text-lg font-medium tracking-tight leading-6 truncate">
{t('Detalle de clientes')}
</Typography>
</div>
<div>
<Button
size='small'
variant='contained'
color='secondary'
component={Link}
to={'/client/form'}
startIcon={
<FuseSvgIcon
className='text-48 text-white'
size={24}
color='action'
>
heroicons-outline:plus
</FuseSvgIcon>
}
>
Registrar Cliente
</Button>
</div>
</div>
<div className="table-responsive mt-24">
<Table className='simple w-full min-w-full h-full'>
<TableHead>
<TableRow>
{columns.map((column, index) => (
<TableCell key={index}>
<Typography
color="text.secondary"
className="font-semibold text-12 whitespace-nowrap"
>
{column}
</Typography>
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{rows.map((row, index) => (
<TableRow key={index}>
{Object.entries(row).map(([key, value]) => {
switch (key) {
case 'action': {
return (
<TableCell
key={key}
component='th'
scope='row'
className='text-center'
>
<div className='flex items-center justify-left'>
<Typography>{value}</Typography>
<IconButton size='small' color='inherit'>
<FuseSvgIcon>
heroicons-outline:pencil-alt
</FuseSvgIcon>
</IconButton>
<ResponsiveDialog></ResponsiveDialog>
</div>
</TableCell>
);
}
default: {
return (
<TableCell key={key} component='th' scope='row'>
<Typography>{value}</Typography>
</TableCell>
);
}
}
})}
</TableRow>
))}
</TableBody>
</Table>
</div>
</Paper>
}
/>
);
}
export default ListDetalleClienteRender;

View File

@ -0,0 +1,73 @@
export interface Client{
nombreComercial: string;
razonSocial: string;
identificacion: string;
direccion: string;
telefono: string;
correoElectronico: string;
}
/* const FormularioClienteRender = () => {
return (
<Card className="m-20 p-24">
<CardHeader title="Registro cliente" className="text-16 font-bold" />
<Divider />
<Formik
initialValues={{
nombreComercial: "",
razonSocial: "",
identificacion: "",
direccion: "",
telefono: "",
correoElectronico: "",
}}
validationSchema={validationSchema}
onSubmit={(values, { setSubmitting }) => {
// Aquí manejas la lógica de envío del formulario
console.log(values);
setSubmitting(false);
}}
>
{({ isSubmitting }) => (
<Form>
<CardContent className="flex">
<Grid className="grid grid-cols-1 sm:grid-cols-6 gap-10 w-full min-w-0">
<Field
as={TextField}
className="sm:col-span-3 lg:col-span-3 mt-20"
required
label="Nombre Comercial"
id="nombreComercial"
name="nombreComercial"
variant="outlined"
fullWidth
size="small"
/>
<ErrorMessage
name="nombreComercial"
component="div"
className="text-red-500"
/>
{/* Agrega el resto de los campos y sus mensajes de error aquí */}
{/* <Button
className="w-320"
variant="contained"
size="small"
color="secondary"
type="submit"
disabled={isSubmitting}
>
Guardar
</Button>
</Grid>
</CardContent>
</Form>
)}
</Formik>
</Card>
);
};
*/}

View File

@ -0,0 +1,10 @@
import React from 'react'
import FormularioClienteRender from './FormularioClienteRender'
const FormularioCliente = () => {
return (
<FormularioClienteRender/>
)
}
export default FormularioCliente

View File

@ -0,0 +1,19 @@
import {lazy} from 'react';
const FormularioCliente = lazy(() => import('./FormularioCliente'));
const FormularioClienteConfig = {
settings: {
layout: {
config: {}
}
},
routes: [
{
path: 'client/form',
element: <FormularioCliente/>
}
]
};
export default FormularioClienteConfig;

View File

@ -0,0 +1,188 @@
import FusePageSimple from "@fuse/core/FusePageSimple";
import {
Autocomplete,
Card,
CardActions,
CardContent,
CardHeader,
Divider,
TextField,
styled,
Button,
Grid,
} from "@mui/material";
import { useState } from "react";
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";
const FormularioClienteRender = () => {
const [datosCargados, setDatosCargados] = useState(false);
const validationSchema = Yup.object().shape({
nombreComercial: Yup.string().required('El nombre comercial es requerido'),
razonSocial: Yup.string().required('La razón social es requerida'),
identificacion: Yup.string()
.required('La cédula o RUC es requerida')
.min(10, 'La cédula debe tener 10 digitos')
.max(13, 'El RUC debe tener 13 digitos'),
direccion: Yup.string().required('La dirección es requerida'),
telefono: Yup.string()
.required('El teléfono es requerido')
.matches(/^[0-9]{10}$/, 'El teléfono debe ser un número de 10 dígitos'),
correoElectronico: Yup.string()
.required('El correo electrónico es requerido')
.email('Ingrese un correo electrónico válido'),
})
return (
<Card className="m-20 p-24">
<CardHeader title="Registro cliente" className="text-16 font-bold"/>
<Divider />
<Formik
initialValues = {{
nombreComercial: "",
razonSocial: "",
identificacion: "",
direccion:"",
telefono: "",
correoElectronico: "",
}}
validationSchema={validationSchema}
onSubmit={(values, { setSubmitting }) => {
console.log(values);
setSubmitting(false);
}}
>
{({ isSubmitting, touched, errors }) => (
<Form>
<CardContent className="flex">
<Grid className="grid grid-cols-1 sm:grid-cols-6 gap-10 w-full min-w-0">
<div className="mt-20 sm:col-span-3 lg:col-span-3">
<Field
as={TextField}
label="Nombre Comercial"
name="nombreComercial"
variant="outlined"
fullWidth
size="small"
error={touched.nombreComercial && !! errors.nombreComercial}
/>
<ErrorMessage
name="nombreComercial"
component ='div'
className="text-red-500 text-md ml-12 pt-4"
/>
</div>
<div className="mt-20 sm:col-span-3 lg:col-span-3">
<Field
as={TextField}
label="Razón Social"
name="razonSocial"
variant="outlined"
fullWidth
size="small"
error={touched.razonSocial && !! errors.razonSocial}
/>
<ErrorMessage
name="razonSocial"
component ='div'
className="text-red-500 text-md ml-12 pt-4"
/>
</div>
<div className="mt-10 sm:col-span-3 lg:col-span-3">
<Field
as={TextField}
label="Cédula o RUC"
name="identificacion"
variant="outlined"
fullWidth
size="small"
error={touched.identificacion && !!errors.identificacion}
/>
<ErrorMessage
name="identificacion"
component="div"
className="text-red-500 text-md ml-12 pt-4"
/>
</div>
<div className="mt-10 sm:col-span-3 lg:col-span-3">
<Field
as={TextField}
label="Dirección"
name="direccion"
variant="outlined"
fullWidth
size="small"
error={touched.direccion && !!errors.direccion}
/>
<ErrorMessage
name="direccion"
component="div"
className="text-red-500 text-md ml-12 pt-4"
/>
</div>
<div className="mt-10 sm:col-span-3 lg:col-span-3">
<Field
as={TextField}
label="Teléfono"
name="telefono"
variant="outlined"
fullWidth
size="small"
error={touched.telefono && !!errors.telefono}
/>
<ErrorMessage
name="telefono"
component="div"
className="text-red-500 text-md ml-12 pt-4"
/>
</div>
<div className="mt-10 sm:col-span-3 lg:col-span-3">
<Field
as={TextField}
label="Correo Electronico"
name="correoElectronico"
variant="outlined"
fullWidth
size="small"
error={touched.correoElectronico && !!errors.correoElectronico}
/>
<ErrorMessage
name="correoElectronico"
component="div"
className="text-red-500 text-md ml-12 pt-4"
/>
</div>
</Grid>
</CardContent>
<CardActions className="mt-15">
<Button
className="w-320"
variant="contained"
size="small"
color="secondary"
type="submit"
disabled={isSubmitting}
>
Guardar{" "}
</Button>
</CardActions>
</Form>
)}
</Formik>
</Card>
)
};
export default FormularioClienteRender;

View File

@ -1,15 +1,10 @@
import FusePageSimple from '@fuse/core/FusePageSimple';
import Tab from '@mui/material/Tab';
import Tabs from '@mui/material/Tabs';
import { useState } from 'react';
import Box from '@mui/material/Box';
import { styled } from '@mui/material/styles';
import * as React from 'react';
import FuseLoading from '@fuse/core/FuseLoading';
import ProjectDashboardAppHeader from './ProjectDashboardAppHeader';
import HomeTab from './tabs/home/HomeTab';
import TeamTab from './tabs/team/TeamTab';
import BudgetTab from './tabs/budget/BudgetTab';
import { useGetProjectDashboardWidgetsQuery } from './ProjectDashboardApi';
const Root = styled(FusePageSimple)(({ theme }) => ({

View File

@ -0,0 +1,37 @@
import DemoContent from '@fuse/core/DemoContent';
import FusePageSimple from '@fuse/core/FusePageSimple';
import { useTranslation } from 'react-i18next';
import { styled } from '@mui/material/styles';
import FirmaRender from './FirmaRender';
const Root = styled(FusePageSimple)(({ theme }) => ({
'& .FusePageSimple-header': {
backgroundColor: theme.palette.background.paper,
borderBottomWidth: 1,
borderStyle: 'solid',
borderColor: theme.palette.divider
},
'& .FusePageSimple-content': {},
'& .FusePageSimple-sidebarHeader': {},
'& .FusePageSimple-sidebarContent': {}
}));
function Firma() {
const { t } = useTranslation('signature');
return(
<Root
header={
<div className='p-24'>
<h4>{t('FIRMA ELECTRONICA')}</h4>
</div>
}
content={
<FirmaRender></FirmaRender>
}
/>
)
}
export default Firma;

View File

@ -0,0 +1,8 @@
import { FuseRouteConfigsType } from "@fuse/utils/FuseUtils";
import FormularioFirmaConfig from "./formularioFirma/FormularioFirmaConfig";
const firmaConfigs: FuseRouteConfigsType = [
FormularioFirmaConfig
]
export default firmaConfigs;

View File

@ -0,0 +1,11 @@
import FormularioFirma from "./formularioFirma/FormularioFirma";
const ClientRender = () => {
return (
<div className="w-full p-12 pt-16 sm:pt-24 lg:ltr:pr-0 lg:rtl:pl-0">
<FormularioFirma></FormularioFirma>
</div>
)
}
export default ClientRender;

View File

@ -0,0 +1,10 @@
import React from 'react'
import FormularioFirmaRender from './FormularioFirmaRender'
const FormularioFirma = () => {
return (
<FormularioFirmaRender/>
)
}
export default FormularioFirma

View File

@ -0,0 +1,19 @@
import {lazy} from 'react';
const FormularioFirma = lazy(() => import('./FormularioFirma'));
const FormularioClienteConfig = {
settings: {
layout: {
config: {}
}
},
routes: [
{
path: 'signature/form',
element: <FormularioFirma/>
}
]
};
export default FormularioClienteConfig;

View File

@ -0,0 +1,200 @@
import { useState } from "react";
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";
import { Card, CardActions, CardContent, CardHeader, Button, TextField } from "@mui/material";
const FormularioClienteRender = () => {
const [file, setFile] = useState(null);
const validationSchema = Yup.object().shape({
contrasena: Yup.string()
.required("La contraseña es requerida"),
/* .min(5, 'La contraseña debe tener como mínimo 8 caracteres')
.max(10, 'La contraseña debe tener como máximo 10 caracteres')
.matches(/[0-9]/, 'La contraseña requiere al menos un número')
.matches(/[a-z]/, 'La contraseña requiere al menos una letra minuscula')
.matches(/[A-Z]/, 'La contraseña requiere al menos una letra mayuscula'), */
validarContrasena: Yup.string()
.required("La verificación de la contraseña es requerida")
.oneOf([Yup.ref('contrasena'), null], 'La contraseña debe coincidir con la anterior'),
fechaCaducacion: Yup.date()
.required("La fecha de caducación es requerida")
.min(new Date(), "La fecha de caducación debe ser posterior a la fecha actual"),
archivo: Yup.mixed()
.required("El archivo es requerido")
.test("fileFormat", "Solo se permiten archivos PDF", (value) => {
if (value) {
return value.type === "application/pdf";
}
return true;
}),
});
const handleFileDrop = (event, setFieldValue) => {
event.preventDefault();
const droppedFile = event.dataTransfer.files[0];
setFile(droppedFile);
setFieldValue("archivo", droppedFile);
};
const handleFileChange = (event, setFieldValue) => {
const selectedFile = event.currentTarget.files[0];
setFile(selectedFile);
setFieldValue("archivo", selectedFile);
};
return (
<div className="p-52 ">
<h1 className="text-center">CONFIGURACIÓN DE LA FIRMA ELECTRÓNICA</h1>
<div className="flex justify-center items-center h-full">
<Card className="p-10 text-center w-2/3">
<CardHeader title="Subir Firma" className="text-16 font-bold" />
<h5>Para poder registrar su firma electrónica deberá</h5>
<h5>ingresar la siguiente información</h5>
<CardContent className="p-10">
<Formik
initialValues={{
contrasena: "",
validarContrasena:"",
fechaCaducacion: "",
archivo: "",
}}
validationSchema={validationSchema}
onSubmit={(values, { setSubmitting }) => {
console.log(values);
setSubmitting(false);
}}
>
{({ isSubmitting, touched, errors, setFieldValue }) => (
<Form>
<div className="pt-10">
<Field
as={TextField}
label="Contraseña"
name="contrasena"
variant="outlined"
fullWidth
size="small"
type="password"
error={touched.contrasena && !! errors.contrasena}
/>
<ErrorMessage
name="contrasena"
component="div"
className="text-red-500 text-md ml-12 pt-4 text-left"
/>
</div>
<div className="pt-14">
<Field
as={TextField}
label="Verificación Contraseña"
name="validarContrasena"
variant="outlined"
fullWidth
size="small"
type="password"
error={touched.validarContrasena && errors.validarContrasena}
/>
<ErrorMessage
name="validarContrasena"
component="div"
className="text-red-500 text-md ml-12 pt-4 text-left"
/>
</div>
<div className="pt-24">
<Field
as={TextField}
label="Fecha de caducidad"
name="fechaCaducacion"
variant="outlined"
fullWidth
size="small"
type="date"
error={touched.fechaCaducacion && !! errors.fechaCaducacion}
InputLabelProps={{
shrink: true,
}}
/>
<ErrorMessage
name="fechaCaducacion"
component="div"
className="text-red-500 text-md ml-12 pt-4 text-left"
/>
</div>
<div className="pt-20" onDrop={(event) => handleFileDrop(event, setFieldValue)} onDragOver={(event) => event.preventDefault()}>
<div className="w-full">
<label htmlFor="archivo" className="flex flex-col items-center justify-center w-full h-96 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-gray-800 dark:bg-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:hover:border-gray-500">
<input
id="archivo"
name="archivo"
type="file"
accept=".pdf"
className="hidden"
onChange={(event) => handleFileChange(event, setFieldValue)}
/>
<div className="flex flex-col items-center justify-center pt-5 pb-6 h-96">
<svg
className="w-20 h-15 mb-1 text-gray-500 dark:text-gray-400"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 20 16"
>
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5 5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0 0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2"
/>
</svg>
<p className="mb-2 text-sm text-gray-500 dark:text-gray-400 pt-8">
<span className="font-semibold">Haga clic para cargar o arrastre y suelte </span>
</p>
<p className="text-s text-gray-500 dark:text-gray-400" id="file-name-display">
{file ? file.name : "Solo se permiten archivos .pdf"}
</p>
</div>
</label>
</div>
<ErrorMessage
name="archivo"
component="div"
className="text-red-500 text-md ml-12 pt-4 text-left"
/>
</div>
<CardActions className="mt-15 justify-center p-8">
<Button
className="w-full sm:w-320"
variant="contained"
size="small"
color="secondary"
type="submit"
disabled={isSubmitting}
>
{isSubmitting ? "Subiendo..." : "Subir"}
</Button>
</CardActions>
</Form>
)}
</Formik>
</CardContent>
</Card>
</div>
</div>
);
};
export default FormularioClienteRender;

View File

@ -1,10 +1,7 @@
import React from 'react'
import GenerateInvoiceRender from './GenerateInvoiceRender'
import GenerateInvoiceRender from './GenerateInvoiceRender';
const GenerateInvoice = () => {
return (
<GenerateInvoiceRender/>
)
function GenerateInvoice() {
return <GenerateInvoiceRender />;
}
export default GenerateInvoice
export default GenerateInvoice;

View File

@ -1,90 +1,20 @@
import FusePageCarded from "@fuse/core/FusePageCarded";
import FusePageSimple from "@fuse/core/FusePageSimple";
import {
Autocomplete,
Card,
CardActions,
CardContent,
CardHeader,
Divider,
TextField,
styled,
Button,
Grid,
Box,
} from "@mui/material";
import { Controller, useFormContext } from "react-hook-form";
import DataInvoice from "./components/dataInvoice/DataInvoice";
import DataClient from "./components/dataClient/DataClient";
import { Card, CardActions, CardContent, CardHeader, Divider, Button } from '@mui/material';
import DataInvoice from './components/dataInvoice/DataInvoice';
import DataClient from './components/dataClient/DataClient';
import DataTransmitter from './components/dataTransmitter/DataTransmitter';
const Root = styled(FusePageSimple)(({ theme }) => ({
"& .FusePageSimple-header": {
backgroundColor: theme.palette.background.paper,
boxShadow: `inset 0 0 0 1px ${theme.palette.divider}`,
},
}));
const GenerateInvoiceRender = () => {
function GenerateInvoiceRender() {
return (
<Card className=" m-20 p-24">
<Card className=" m-20 p-12">
<CardHeader
title="Crear Factura de venta"
className="text-16 font-bold "
/>
<Divider />
<DataInvoice />
<DataClient />
<CardContent>
<Grid container spacing={2}>
<Grid item xs={12} md={6}>
<TextField
className="mt-8 mb-16"
required
label="Name"
autoFocus
id="name"
variant="outlined"
fullWidth
/* error={!!errors.name}
helperText={errors?.name?.message as string} */
/>
</Grid>
<Grid item xs={12} md={6}>
<TextField
className="mt-8 mb-16"
id="description"
label="Description"
type="text"
multiline
rows={5}
variant="outlined"
fullWidth
/>
</Grid>
<Grid item xs={12} md={6}>
<Autocomplete
className="mt-8 mb-16"
multiple
freeSolo
options={[]}
/* value={value }
onChange={(event, newValue) => {
onChange(newValue);
}} */
renderInput={(params) => (
<TextField
{...params}
placeholder="Select multiple categories"
label="Categories"
variant="outlined"
InputLabelProps={{
shrink: true,
}}
/>
)}
/>
</Grid>
</Grid>
<DataTransmitter />
<DataClient />
<DataInvoice />
</CardContent>
<CardActions>
<Button
@ -93,11 +23,11 @@ const GenerateInvoiceRender = () => {
size="small"
color="primary"
>
Guardar{" "}
Guardar{' '}
</Button>
</CardActions>
</Card>
);
};
}
export default GenerateInvoiceRender;

View File

@ -1,69 +1,42 @@
import {
Autocomplete,
Box,
Divider,
Grid,
TextField,
Typography,
} from "@mui/material";
import { useState } from 'react';
import DataClientRender from './DataClientRender';
import { Client } from './DataClientInterfaz';
const DataClient = () => {
const clientes = [
{
id: 1,
nombreComercial: 'Andres Alvarez',
razonSocial: 'Andres Alvarez',
identificacion: '1721529788',
direccion: 'Calle oe',
telefono: '0988545102',
correo: 'andres@test.com'
},
{
id: 2,
nombreComercial: 'Paul Ruales',
razonSocial: 'Paul Ruales',
identificacion: '1721524123',
direccion: 'Calle lomisima',
telefono: '0988544772',
correo: 'poul@test.com'
}
];
function DataClient() {
const [clients, setClients] = useState<Client[]>(clientes);
const [selectClient, setSelectClient] = useState<Client | null>(null);
const handleSelectClient = (value:Client) => {
setSelectClient(value)
if(!clients.includes(value)) setClients([...clients, value])
};
return (
<Box className="mt-10 p-20 shadow-2 rounded-8">
<Typography component="h3" className="mb-7">
Datos cliente
</Typography>
<Divider className="border-1 mb-10" />
<Grid container spacing={2}>
<Grid item xs={12} md={12}>
<Autocomplete
className="mt-8 mb-16"
multiple
freeSolo
options={[]}
/* value={value }
onChange={(event, newValue) => {
onChange(newValue);
}} */
renderInput={(params) => (
<TextField
{...params}
placeholder="Busque el cliente"
label="Cliente"
variant="outlined"
InputLabelProps={{
shrink: true,
}}
<DataClientRender
client={clients}
handleSelectClient={handleSelectClient}
selectClient={selectClient}
/>
)}
/>
</Grid>
<Grid item md={12} >
<Divider/>
</Grid>
<Grid item md={4} xs={12}>
<b>Razón Social:</b> Jonathan Andres Alvarez Flores
</Grid>
<Grid item md={4} xs={12}>
<b>Nombre Comercial:</b> Jonathan Andres Alvarez Flores
</Grid>
<Grid item md={4} xs={12}>
<b>Ruc:</b> 17215785512001
</Grid>
<Grid item md={4} xs={12}>
<b>Dirección:</b> Calle Oe11g y s32
</Grid>
<Grid item md={4} xs={12}>
<b>Teléfono:</b> 0988545211
</Grid>
<Grid item md={4} xs={12}>
<b>Correo:</b> admin@qsoftec.com
</Grid>
</Grid>
</Box>
);
};
}
export default DataClient;

View File

@ -0,0 +1,9 @@
export interface Client {
id: number;
nombreComercial: string;
razonSocial: string;
identificacion: string;
direccion: string;
telefono: string;
correo: string;
}

View File

@ -0,0 +1,163 @@
import { useState } from 'react';
import FuseSvgIcon from '@fuse/core/FuseSvgIcon';
import { Autocomplete, Box, Divider, Grid, TextField, Typography, Button } from '@mui/material';
import AddClient from './components/addClient/AddClient';
import { Client } from './DataClientInterfaz';
interface Props {
client: Client[];
handleSelectClient: (value: Client) => void;
selectClient: Client;
}
function DataClientRender({ client, handleSelectClient, selectClient }: Props) {
const [openDialog, setOpenDialog] = useState<boolean>(false);
return (
<Box className="mt-10 p-20 shadow-2 rounded-8">
<AddClient
open={openDialog}
setOpen={setOpenDialog}
handleSelectClient={handleSelectClient}
/>
<Grid
container
spacing={2}
>
<Grid
item
xs={12}
md={10}
>
<Typography
component="h3"
className="mb-7 text-16"
>
Datos cliente
</Typography>
</Grid>
<Grid
item
xs={12}
md={2}
className="flex justify-end"
>
<Button
color="primary"
variant="outlined"
className="rounded-6"
size="small"
onClick={() => setOpenDialog(true)}
startIcon={
<FuseSvgIcon
className="text-48"
size={24}
color="action"
>
heroicons-outline:plus
</FuseSvgIcon>
}
>
Agregar{' '}
</Button>
</Grid>
<Grid
item
xs={12}
>
<Divider className="border-1 mb-10" />
</Grid>
<Grid
item
xs={12}
md={12}
>
<Autocomplete
className="mt-8 mb-16"
options={client}
getOptionLabel={(option) => option.razonSocial}
noOptionsText="No se encontro un resultado"
size="small"
value={selectClient}
onChange={(event, newValue) => {
handleSelectClient(newValue);
}}
renderInput={(params) => (
<TextField
{...params}
placeholder="Busque el cliente"
label="Cliente"
variant="outlined"
InputLabelProps={{
shrink: true
}}
/>
)}
/>
</Grid>
<Grid
item
md={12}
>
<Divider />
</Grid>
{selectClient ? (
<>
<Grid
item
md={4}
xs={12}
>
<b>Razón Social:</b> {selectClient.razonSocial}
</Grid>
<Grid
item
md={4}
xs={12}
>
<b>Nombre Comercial:</b> {selectClient.nombreComercial}
</Grid>
<Grid
item
md={4}
xs={12}
>
<b>Ruc:</b> {selectClient.identificacion}
</Grid>
<Grid
item
md={4}
xs={12}
>
<b>Dirección:</b> {selectClient.direccion}
</Grid>
<Grid
item
md={4}
xs={12}
>
<b>Teléfono:</b> {selectClient.telefono}
</Grid>
<Grid
item
md={4}
xs={12}
>
<b>Correo:</b> {selectClient.correo}
</Grid>
</>
) : (
<Box className="flex justify-center w-full mt-20">
<Typography
variant="subtitle2"
gutterBottom
>
Seleccione o agrege el cliente
</Typography>
</Box>
)}
</Grid>
</Box>
);
}
export default DataClientRender;

View File

@ -0,0 +1,65 @@
import { useFormik } from 'formik';
import * as Yup from 'yup';
import { Client } from '../../DataClientInterfaz';
import AddClientRender from './AddClientRender';
interface Props {
open: boolean;
setOpen: (open: boolean) => void;
handleSelectClient: (value: Client) => void;
}
function AddClient({ open, setOpen, handleSelectClient }: Props) {
const formik = useFormik<Client>({
initialValues: {
id: Math.floor(Math.random() * 100),
nombreComercial: '',
razonSocial: '',
identificacion: '',
direccion: '',
telefono: '',
correo: ''
},
validationSchema: Yup.object({
nombreComercial: Yup.string().required('El nombre comercial es requerido'),
razonSocial: Yup.string().required('La razón social es requerida'),
identificacion: Yup.string()
.required('La cédula o RUC es requerida')
.min(10, 'La cédula debe tener 10 digitos')
.max(13, 'El RUC debe tener 13 digitos'),
direccion: Yup.string().required('La dirección es requerida'),
telefono: Yup.string()
.required('El teléfono es requerido')
.matches(/^[0-9]{10}$/, 'El teléfono debe ser un número de 10 dígitos'),
correo: Yup.string()
.required('El correo electrónico es requerido')
.email('Ingrese un correo electrónico válido'),
}),
onSubmit: (value) => {
handleSaveClient(value);
}
});
const handleOnChange = (event: { target: HTMLInputElement }) => {
const { name, value } = event.target;
formik.setFieldValue(name, value);
};
// TODO: Guardar cliente en la base de datos y seleccionar
const handleSaveClient = (value: Client) => {
handleSelectClient(value);
formik.resetForm();
setOpen(false);
};
return (
<AddClientRender
open={open}
setOpen={setOpen}
formik={formik}
handleOnChange={handleOnChange}
/>
);
}
export default AddClient;

View File

@ -0,0 +1,177 @@
import { DialogActions, DialogContent, DialogTitle, Button, Grid, TextField, Dialog } from '@mui/material';
import { FormikProps } from 'formik';
import { Client } from '../../DataClientInterfaz';
interface Props {
open: boolean;
setOpen: (open: boolean) => void;
formik: FormikProps<Client>;
handleOnChange: (event) => void;
}
function AddClientRender({ open, setOpen, formik, handleOnChange }: Props) {
return (
<Dialog
open={open}
scroll="body"
fullWidth
maxWidth="md"
>
<DialogTitle id="alert-dialog-title">Agregar Cliente</DialogTitle>
<DialogContent dividers>
<Grid
container
spacing={2}
>
<Grid
item
md={6}
xs={12}
>
<TextField
size="small"
label="Nombre Comercial"
variant="outlined"
fullWidth
name="nombreComercial"
value={formik.values.nombreComercial || ''}
onChange={handleOnChange}
onBlur={formik.handleBlur}
error={!!(formik.errors.nombreComercial && formik.touched.nombreComercial)}
helperText={
formik.errors.nombreComercial && formik.touched.nombreComercial
? formik.errors.nombreComercial
: false
}
/>
</Grid>
<Grid
item
md={6}
xs={12}
>
<TextField
size="small"
label="Razón Social"
variant="outlined"
fullWidth
name="razonSocial"
value={formik.values.razonSocial || ''}
onChange={handleOnChange}
onBlur={formik.handleBlur}
error={!!(formik.errors.razonSocial && formik.touched.razonSocial)}
helperText={
formik.errors.razonSocial && formik.touched.razonSocial
? formik.errors.razonSocial
: false
}
/>
</Grid>
<Grid
item
md={6}
xs={12}
>
<TextField
size="small"
label="Cédula o RUC"
variant="outlined"
fullWidth
name="identificacion"
value={formik.values.identificacion || ''}
onChange={handleOnChange}
onBlur={formik.handleBlur}
error={!!(formik.errors.identificacion && formik.touched.identificacion)}
helperText={
formik.errors.identificacion && formik.touched.identificacion
? formik.errors.identificacion
: false
}
/>
</Grid>
<Grid
item
md={6}
xs={12}
>
<TextField
size="small"
label="Dirección"
variant="outlined"
fullWidth
name="direccion"
value={formik.values.direccion || ''}
onChange={handleOnChange}
onBlur={formik.handleBlur}
error={!!(formik.errors.direccion && formik.touched.direccion)}
helperText={
formik.errors.direccion && formik.touched.direccion ? formik.errors.direccion : false
}
/>
</Grid>
<Grid
item
md={6}
xs={12}
>
<TextField
size="small"
label="Teléfono"
variant="outlined"
fullWidth
name="telefono"
value={formik.values.telefono || ''}
onChange={handleOnChange}
onBlur={formik.handleBlur}
error={!!(formik.errors.telefono && formik.touched.telefono)}
helperText={
formik.errors.telefono && formik.touched.telefono ? formik.errors.telefono : false
}
/>
</Grid>
<Grid
item
md={6}
xs={12}
>
<TextField
size="small"
label="Correo electrónico"
variant="outlined"
fullWidth
name="correo"
value={formik.values.correo || ''}
onChange={handleOnChange}
onBlur={formik.handleBlur}
error={!!(formik.errors.correo && formik.touched.correo)}
helperText={formik.errors.correo && formik.touched.correo ? formik.errors.correo : false}
/>
</Grid>
</Grid>
</DialogContent>
<DialogActions className="pr-24">
<Button
onClick={() => {
formik.resetForm();
setOpen(false);
}}
color="primary"
variant="contained"
size="small"
>
Cancelar
</Button>
<Button
onClick={formik.submitForm}
color="primary"
autoFocus
variant="contained"
size="small"
>
Aceptar
</Button>
</DialogActions>
</Dialog>
);
}
export default AddClientRender;

View File

@ -1,35 +1,28 @@
import { Box, Grid, Typography, Divider } from "@mui/material";
import { useState } from 'react';
import DataInvoiceRender from './DataInvoiceRender';
import { ItemInvoice } from './DataInvoiceInterface';
function DataInvoice() {
const [items, setItems] = useState<Array<ItemInvoice>>();
const handleAddItem = () => {
const newItems: ItemInvoice = {
cod: '',
description: '',
amount: 0,
iva: true,
unitValue: 1,
total: 0
};
setItems([...items, newItems]);
};
const DataInvoice = () => {
return (
<Box className="mt-10 p-20 shadow-2 rounded-8">
<Typography component="h3" className="mb-7">
Datos emisor
</Typography>
<Divider className="border-1 mb-10" />
<Grid container spacing={2}>
<Grid item md={6} xs={12}>
<b>Razón Social:</b> Jonathan Andres Alvarez Flores
</Grid>
<Grid item md={6} xs={12}>
<b>Nombre Comercial:</b> Jonathan Andres Alvarez Flores
</Grid>
<Grid item md={6} xs={12}>
<b>Ruc:</b> 17215785512001
</Grid>
<Grid item md={6} xs={12}>
<b>Dirección:</b> Calle Oe11g y s32
</Grid>
<Grid item md={6} xs={12}>
<b>Contribuyente Especial:</b> NO
</Grid>
<Grid item md={6} xs={12}>
<b>Obligado Contabilidad:</b> NO
</Grid>
</Grid>
</Box>
<DataInvoiceRender
handleAddItem={handleAddItem}
items={items}
/>
);
};
}
export default DataInvoice;

View File

@ -0,0 +1,14 @@
export interface Headers {
id: number;
name: string | Element;
style?: string | number;
}
export interface ItemInvoice {
cod: string;
description: string;
amount: number;
unitValue: number;
iva: boolean;
total: number;
}

View File

@ -0,0 +1,213 @@
import { Box, Grid, Typography, Divider, Button, TextField } from '@mui/material';
import FuseSvgIcon from '@fuse/core/FuseSvgIcon';
import TableInvoice from './components/tableInvoice/TableInvoice';
import { ItemInvoice } from './DataInvoiceInterface';
interface Props {
handleAddItem: () => void;
items: ItemInvoice[];
}
function DataInvoiceRender({ handleAddItem, items }: Props) {
return (
<Box className="mt-10 p-20 shadow-2 rounded-8">
<Grid
container
spacing={2}
>
<Grid
item
xs={12}
md={10}
>
<Typography
component="h3"
className="mb-7 text-16"
>
Datos factura
</Typography>
</Grid>
<Grid
item
xs={12}
md={2}
className="flex justify-end"
>
<Button
color="primary"
variant="outlined"
className="rounded-6"
size="small"
onClick={handleAddItem}
startIcon={
<FuseSvgIcon
className="text-48"
size={24}
color="action"
>
heroicons-outline:plus
</FuseSvgIcon>
}
>
Agregar{' '}
</Button>
</Grid>
<Grid
item
xs={12}
>
<Divider className="border-1" />
</Grid>
<Grid
item
xs={12}
md={2}
>
<TextField
label="Serial"
size="small"
variant="outlined"
fullWidth
InputLabelProps={{
shrink: true
}}
/>
</Grid>
<Grid
item
xs={12}
md={2}
>
<TextField
label="Fecha factura"
size="small"
variant="outlined"
type="date"
fullWidth
InputLabelProps={{
shrink: true
}}
/>
</Grid>
<Grid
item
xs={12}
md={2}
>
<TextField
label="Fecha creada"
size="small"
variant="outlined"
type="date"
fullWidth
InputLabelProps={{
shrink: true
}}
/>
</Grid>
</Grid>
<Grid
container
spacing={2}
>
<Grid
item
xs={12}
>
<TableInvoice />
</Grid>
<Grid
item
xs={7}
md={9}
/>
<Grid
item
xs={2}
md={2}
>
<Typography
variant="subtitle2"
gutterBottom
>
Sub Total:
</Typography>
</Grid>
<Grid
item
xs={3}
md={1}
className="item"
>
<Typography
variant="subtitle2"
gutterBottom
>
$10
</Typography>
</Grid>
<Grid
item
xs={7}
md={9}
/>
<Grid
item
xs={2}
md={2}
>
<Typography
variant="subtitle2"
gutterBottom
>
Iva 12%:
</Typography>
</Grid>
<Grid
item
xs={3}
md={1}
className="item"
>
<Typography
variant="subtitle2"
gutterBottom
>
$5
</Typography>
</Grid>
<Grid
item
xs={7}
md={9}
/>
<Grid
item
xs={2}
md={2}
>
<Typography
variant="subtitle2"
gutterBottom
>
Total:
</Typography>
</Grid>
<Grid
item
xs={3}
md={1}
className="item"
>
<Typography
variant="subtitle2"
gutterBottom
>
$15
</Typography>
</Grid>
</Grid>
</Box>
);
}
export default DataInvoiceRender;

View File

@ -0,0 +1,17 @@
import { Headers } from '../../DataInvoiceInterface';
import TableInvoiceRender from './TableInvoiceRender';
function TableInvoice() {
const headers: Array<Headers> = [
{ id: 1, name: 'Código', style: 100 },
{ id: 2, name: 'Descripción', style: 400 },
{ id: 3, name: 'Cantidad', style: 130 },
{ id: 4, name: 'Valor unitario', style: 130 },
{ id: 5, name: 'Iva', style: 10 },
{ id: 6, name: 'Total', style: 130 },
{ id: 7, name: 'Acción', style: 10 }
];
return <TableInvoiceRender headers={headers} />;
}
export default TableInvoice;

View File

@ -0,0 +1,141 @@
import {
Table,
TableCell,
TableHead,
Typography,
TableRow,
TableBody,
TextField,
Autocomplete,
Checkbox
} from '@mui/material';
import IconButton from '@mui/material/IconButton';
import FuseSvgIcon from '@fuse/core/FuseSvgIcon';
import { Headers } from '../../DataInvoiceInterface';
interface Props {
headers: Headers[];
}
function TableInvoiceRender({ headers }: Props) {
return (
<div className="table-responsive mt-24">
<Table
sx={{ minWidth: 650 }}
className="simple"
>
<TableHead>
<TableRow>
{headers.map((column) => (
<TableCell
key={column.id}
style={column.style ? { width: column.style } : {}}
>
<Typography
color="text.secondary"
className="font-semibold text-14 whitespace-nowrap"
>
{column.name}
</Typography>
</TableCell>
))}
{/* <TableCell style={{ width: 5 }}>
<IconButton>
<FuseSvgIcon>heroicons-outline:plus</FuseSvgIcon>
</IconButton>
</TableCell> */}
</TableRow>
</TableHead>
<TableBody>
<TableRow>
<TableCell
component="th"
scope="row"
>
<TextField
variant="outlined"
size="small"
fullWidth
/>
</TableCell>
<TableCell
component="th"
scope="row"
>
<Autocomplete
className=""
freeSolo
size="small"
options={[]}
/* value={value }
onChange={(event, newValue) => {
onChange(newValue);
}} */
renderInput={(params) => (
<TextField
{...params}
placeholder="Busque el producto"
variant="outlined"
fullWidth
InputLabelProps={{
shrink: true
}}
/>
)}
/>
</TableCell>
<TableCell
component="th"
scope="row"
>
<TextField
variant="outlined"
size="small"
fullWidth
type="number"
/>
</TableCell>
<TableCell
component="th"
scope="row"
>
<TextField
variant="outlined"
size="small"
fullWidth
type="number"
/>
</TableCell>
<TableCell
component="th"
scope="row"
className="px-0"
>
<Checkbox />
</TableCell>
<TableCell
component="th"
scope="row"
>
<TextField
variant="outlined"
size="small"
fullWidth
type="number"
/>
</TableCell>
<TableCell
component="th"
scope="row"
>
<IconButton>
<FuseSvgIcon>heroicons-outline:trash</FuseSvgIcon>
</IconButton>
</TableCell>
</TableRow>
</TableBody>
</Table>
</div>
);
}
export default TableInvoiceRender;

View File

@ -0,0 +1,65 @@
import { Box, Grid, Typography, Divider } from '@mui/material';
function DataTransmitter() {
return (
<Box className="mt-10 p-20 shadow-2 rounded-8">
<Typography
component="h3"
className="mb-7"
>
Datos emisor
</Typography>
<Divider className="border-1 mb-10" />
<Grid
container
spacing={2}
>
<Grid
item
md={6}
xs={12}
>
<b>Razón Social:</b> Jonathan Andres Alvarez Flores
</Grid>
<Grid
item
md={6}
xs={12}
>
<b>Nombre Comercial:</b> Jonathan Andres Alvarez Flores
</Grid>
<Grid
item
md={6}
xs={12}
>
<b>Ruc:</b> 17215785512001
</Grid>
<Grid
item
md={6}
xs={12}
>
<b>Dirección:</b> Calle Oe11g y s32
</Grid>
<Grid
item
md={6}
xs={12}
>
<b>Contribuyente Especial:</b> NO
</Grid>
<Grid
item
md={6}
xs={12}
>
<b>Obligado Contabilidad:</b> NO
</Grid>
</Grid>
</Box>
);
}
export default DataTransmitter;

View File

@ -1,130 +1,115 @@
import FusePageSimple from "@fuse/core/FusePageSimple";
import FuseSvgIcon from "@fuse/core/FuseSvgIcon";
import {
Button,
Paper,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Typography,
styled,
} from "@mui/material";
import clsx from "clsx";
import { format } from "date-fns";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import FusePageSimple from '@fuse/core/FusePageSimple';
import FuseSvgIcon from '@fuse/core/FuseSvgIcon';
import { Button, Paper, Table, TableBody, TableCell, TableHead, TableRow, Typography, styled } from '@mui/material';
import clsx from 'clsx';
import { format } from 'date-fns';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
const Root = styled(FusePageSimple)(({ theme }) => ({
"& .FusePageSimple-header": {
'& .FusePageSimple-header': {
backgroundColor: theme.palette.background.paper,
boxShadow: `inset 0 0 0 1px ${theme.palette.divider}`,
},
boxShadow: `inset 0 0 0 1px ${theme.palette.divider}`
}
}));
const ListInvoiceRender = () => {
const { t } = useTranslation("invoicePage");
const columns = [
"Número factura",
"Fecha",
"Cliente",
"Identificación",
"Valor",
"Estado",
"Acciones",
];
function ListInvoiceRender() {
const { t } = useTranslation('invoicePage');
const columns = ['Número factura', 'Fecha', 'Cliente', 'Identificación', 'Valor', 'Estado', 'Acciones'];
const rows = [
{
id: "528651571NT",
date: "2019-10-07T22:22:37.274Z",
name: "Morgan Page",
identification: "17215896114",
id: '528651571NT',
date: '2019-10-07T22:22:37.274Z',
name: 'Morgan Page',
identification: '17215896114',
amount: 1358.75,
status: "completed",
action: true,
status: 'completed',
action: true
},
{
id: "421436904YT",
date: "2019-12-18T14:51:24.461Z",
name: "Nita Hebert",
identification: "17215896114",
id: '421436904YT',
date: '2019-12-18T14:51:24.461Z',
name: 'Nita Hebert',
identification: '17215896114',
amount: -1042.82,
status: "completed",
action: true,
status: 'completed',
action: true
},
{
id: "685377421YT",
date: "2019-12-25T17:52:14.304Z",
name: "Marsha Chambers",
identification: "17215896114",
id: '685377421YT',
date: '2019-12-25T17:52:14.304Z',
name: 'Marsha Chambers',
identification: '17215896114',
amount: 1828.16,
status: "pending",
action: true,
status: 'pending',
action: true
},
{
id: "884960091RT",
date: "2019-11-29T06:32:16.111Z",
name: "Charmaine Jackson",
identification: "17215896114",
id: '884960091RT',
date: '2019-11-29T06:32:16.111Z',
name: 'Charmaine Jackson',
identification: '17215896114',
amount: 1647.55,
status: "completed",
action: true,
status: 'completed',
action: true
},
{
id: "361402213NT",
date: "2019-11-24T12:13:23.064Z",
name: "Maura Carey",
identification: "17215896114",
id: '361402213NT',
date: '2019-11-24T12:13:23.064Z',
name: 'Maura Carey',
identification: '17215896114',
amount: -927.43,
status: "completed",
action: true,
status: 'completed',
action: true
},
{
id: "361402213NT",
date: "2019-11-24T12:13:23.064Z",
name: "Maura Carey",
identification: "17215896114",
id: '361402213NT',
date: '2019-11-24T12:13:23.064Z',
name: 'Maura Carey',
identification: '17215896114',
amount: -927.43,
status: "completed",
action: true,
status: 'completed',
action: true
},
{
id: "361402213NT",
date: "2019-11-24T12:13:23.064Z",
name: "Maura Carey",
identification: "17215896114",
id: '361402213NT',
date: '2019-11-24T12:13:23.064Z',
name: 'Maura Carey',
identification: '17215896114',
amount: -927.43,
status: "completed",
action: true,
status: 'completed',
action: true
},
{
id: "361402213NT",
date: "2019-11-24T12:13:23.064Z",
name: "Maura Carey",
identification: "17215896114",
id: '361402213NT',
date: '2019-11-24T12:13:23.064Z',
name: 'Maura Carey',
identification: '17215896114',
amount: -927.43,
status: "completed",
action: true,
},
status: 'completed',
action: true
}
];
return (
<Root
header={
<div className="p-24">
<h4>{t("TITLE")}</h4>
<h4>{t('TITLE')}</h4>
</div>
}
content={
<Paper className="flex flex-col flex-auto p-24 shadow rounded-2 overflow-hidden mt-6">
<Paper className="flex flex-col flex-auto p-24 shadow rounded-2 overflow-hidden m-10">
<div className="flex md:flex-row justify-between flex-col">
<div>
<Typography className="mr-16 text-lg font-medium tracking-tight leading-6 truncate">
{t("TITLE_TABLE")}
{t('TITLE_TABLE')}
</Typography>
<Typography className="font-medium" color="text.secondary">
<Typography
className="font-medium"
color="text.secondary"
>
1 pendiente, 4 completadas
</Typography>
</div>
@ -134,7 +119,7 @@ const ListInvoiceRender = () => {
variant="contained"
color="primary"
component={Link}
to={'/invoice/edit'}
to="/invoice/edit"
startIcon={
<FuseSvgIcon
className="text-48 text-white"
@ -171,49 +156,64 @@ const ListInvoiceRender = () => {
<TableRow key={index}>
{Object.entries(row).map(([key, value]) => {
switch (key) {
case "id": {
case 'id': {
return (
<TableCell key={key} component="th" scope="row">
<Typography color="text.secondary">
{value}
</Typography>
<TableCell
key={key}
component="th"
scope="row"
>
<Typography color="text.secondary">{value}</Typography>
</TableCell>
);
}
case "date": {
case 'date': {
return (
<TableCell key={key} component="th" scope="row">
<TableCell
key={key}
component="th"
scope="row"
>
<Typography>
{
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
format(new Date(value), "dd/MM/yyyy")
format(new Date(value), 'dd/MM/yyyy')
}
</Typography>
</TableCell>
);
}
case "amount": {
case 'amount': {
return (
<TableCell key={key} component="th" scope="row">
<TableCell
key={key}
component="th"
scope="row"
>
<Typography>
{value.toLocaleString("en-US", {
style: "currency",
currency: "USD",
{value.toLocaleString('en-US', {
style: 'currency',
currency: 'USD'
})}
</Typography>
</TableCell>
);
}
case "status": {
case 'status': {
return (
<TableCell key={key} component="th" scope="row">
<TableCell
key={key}
component="th"
scope="row"
>
<Typography
className={clsx(
"inline-flex items-center font-bold text-10 px-10 py-2 rounded-full tracking-wide uppercase",
value === "pending" &&
"bg-red-100 text-red-800 dark:bg-red-600 dark:text-red-50",
value === "completed" &&
"bg-green-50 text-green-800 dark:bg-green-600 dark:text-green-50"
'inline-flex items-center font-bold text-10 px-10 py-2 rounded-full tracking-wide uppercase',
value === 'pending' &&
'bg-red-100 text-red-800 dark:bg-red-600 dark:text-red-50',
value === 'completed' &&
'bg-green-50 text-green-800 dark:bg-green-600 dark:text-green-50'
)}
>
{value}
@ -221,9 +221,13 @@ const ListInvoiceRender = () => {
</TableCell>
);
}
case "action": {
case 'action': {
return (
<TableCell key={key} component="th" scope="row">
<TableCell
key={key}
component="th"
scope="row"
>
<Button
size="small"
variant="contained"
@ -236,7 +240,11 @@ const ListInvoiceRender = () => {
}
default: {
return (
<TableCell key={key} component="th" scope="row">
<TableCell
key={key}
component="th"
scope="row"
>
<Typography>{value}</Typography>
</TableCell>
);
@ -252,6 +260,6 @@ const ListInvoiceRender = () => {
}
/>
);
};
}
export default ListInvoiceRender;

View File

@ -0,0 +1,37 @@
import DemoContent from '@fuse/core/DemoContent';
import FusePageSimple from '@fuse/core/FusePageSimple';
import { useTranslation } from 'react-i18next';
import { styled } from '@mui/material/styles';
import ListDetalleProductoRender from './detalleProducto/DetalleProductoRender';
import ProductoRender from './ProductoRender';
const Root = styled(FusePageSimple)(({ theme }) => ({
'& .FusePageSimple-header': {
backgroundColor: theme.palette.background.paper,
borderBottomWidth: 1,
borderStyle: 'solid',
borderColor: theme.palette.divider
},
'& .FusePageSimple-content': {},
'& .FusePageSimple-sidebarHeader': {},
'& .FusePageSimple-sidebarContent': {}
}));
function Producto() {
const { t } = useTranslation('products');
return(
<Root
header={
<div className='p-24'>
<h4>{t('PRODUCTOS')}</h4>
</div>
}
content={
<ProductoRender></ProductoRender>
}
/>
)
}
export default Producto;

View File

@ -0,0 +1,10 @@
import { FuseRouteConfigsType } from "@fuse/utils/FuseUtils";
import FormularioProductoConfig from "./formularioProducto/FormularioProductoConfig";
import DetalleProductoConfig from "./detalleProducto/DetalleProductoConfig";
const productoConfigs: FuseRouteConfigsType = [
DetalleProductoConfig,
FormularioProductoConfig
]
export default productoConfigs;

View File

@ -0,0 +1,14 @@
import BuscarProducto from "./widgets/BuscarProducto";
import ListDetalleProductoRender from "./detalleProducto/DetalleProductoRender";
import DetalleProducto from "./detalleProducto/DetalleProducto";
function ProductoRender() {
return(
<div className="w-full p-12 pt-16 sm:pt-24 lg:ltr:pr-0 lg:rtl:pl-0">
<BuscarProducto></BuscarProducto>
<DetalleProducto></DetalleProducto>
</div>
)
}
export default ProductoRender;

View File

@ -0,0 +1,9 @@
import ListDetalleProductoRender from "./DetalleProductoRender"
const DetalleProducto = () => {
return(
<ListDetalleProductoRender/>
)
}
export default DetalleProducto;

View File

@ -0,0 +1,21 @@
import i18next from "i18next";
import { lazy } from 'react';
import es from '../i18n/es';
i18next.addResourceBundle('es', 'detailProduct', es);
const Producto = lazy(() => import('../Producto'));
const DetalleProductoConfig = {
settings: {
layout: {}
},
routes: [
{
path: 'product/list',
element: <Producto/>
}
]
};
export default DetalleProductoConfig;

View File

@ -0,0 +1,216 @@
import * as React from "react";
import FuseSvgIcon from "@fuse/core/FuseSvgIcon";
import {
Button,
IconButton,
Paper,
Table,
TableBody,
TableCell,
TableHead,
TablePagination,
TableRow,
Typography,
} from "@mui/material";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import clsx from "clsx";
import ResponsiveDialog from "../widgets/DialogDelete";
function ListDetalleProductoRender() {
const [page, setPage] = React.useState(0);
const [rowsPerPage, setRowsPerPage] = React.useState(10);
const handleChangePage = (event: unknown, newPage: number) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (
event: React.ChangeEvent<HTMLInputElement>
) => {
setRowsPerPage(+event.target.value);
setPage(0);
};
const { t } = useTranslation("products");
const columns = [
"Código",
"Nombre",
"Descripción",
"Stock Mínimo",
"Estado",
"Acciones",
];
const rows = [
{
artCodigo: 1,
artNombre: "Servicios Profesionales",
artDescripcion: "Prestación de servicios",
artStockMinimo: 0,
artEstado: "activo",
action: true,
},
{
artCodigo: 2,
artNombre: "Servicios Varios",
artDescripcion: "",
artStockMinimo: 0,
artEstado: "inactivo",
action: true,
},
{
artCodigo: 3,
artNombre: "Servicios Profesionales",
artDescripcion: "Prestación de servicios",
artStockMinimo: 0,
artEstado: "activo",
action: true,
},
{
artCodigo: 4,
artNombre: "Servicios Varios",
artDescripcion: "",
artStockMinimo: 0,
artEstado: "inactivo",
action: true,
},
{
artCodigo: 5,
artNombre: "Servicios Profesionales",
artDescripcion: "Prestación de servicios",
artStockMinimo: 0,
artEstado: "activo",
action: true,
},
{
artCodigo: 6,
artNombre: "Servicios Varios",
artDescripcion: "",
artStockMinimo: 0,
artEstado: "inactivo",
action: true,
},
];
return (
<Paper className="flex flex-col flex-auto p-24 shadow rounded-2 overflow-hidden m-10">
<div className="flex md:flex-row justify-between flex-col">
<div>
<Typography className="mr-16 text-lg font-medium tracking-tight leading-6 truncate">
{t("Detalle de productos")}
</Typography>
</div>
<div>
<Button
size="small"
variant="contained"
color="secondary"
component={Link}
to={"/product/form"}
startIcon={
<FuseSvgIcon
className="text-48 text-white"
size={24}
color="action"
>
heroicons-outline:plus
</FuseSvgIcon>
}
>
Registrar Producto
</Button>
</div>
</div>
<div className="table-responsive mt-24">
<Table className="simple w-full min-w-full h-full">
<TableHead>
<TableRow>
{columns.map((column, index) => (
<TableCell key={index}>
<Typography
color="text.secondary"
className="font-semibold text-12 whitespace-nowrap"
>
{column}
</Typography>
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{rows
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row, index) => (
<TableRow key={index}>
{Object.entries(row).map(([key, value]) => {
switch (key) {
case "artEstado": {
return (
<TableCell key={key} component="th" scope="row" >
<Typography
className={clsx(
"inline-flex items-center font-bold text-10 px-10 py-2 rounded-full tracking-wide uppercase",
value === "inactivo" &&
"bg-red-100 text-red-800 dark:bg-red-600 dark:text-red-50",
value === "activo" &&
"bg-green-50 text-green-800 dark:bg-green-600 dark:text-green-50"
)}
>
{value}
</Typography>
</TableCell>
);
}
case "action": {
return (
<TableCell
key={key}
component="th"
scope="row"
className="text-center"
>
<div className="flex items-center justify-left">
<Typography>{value}</Typography>
<IconButton size="small" color="inherit">
<FuseSvgIcon>
heroicons-outline:pencil-alt
</FuseSvgIcon>
</IconButton>
<ResponsiveDialog></ResponsiveDialog>
</div>
</TableCell>
);
}
default: {
return (
<TableCell key={key} component="th" scope="row">
<Typography>{value}</Typography>
</TableCell>
);
}
}
})}
</TableRow>
))}
</TableBody>
</Table>
<TablePagination
rowsPerPageOptions={[5, 10, 25]}
component="div"
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
</div>
</Paper>
);
}
export default ListDetalleProductoRender;

View File

@ -0,0 +1,9 @@
import FormularioProductoRender from "./FormularioProductoRender"
const FormularioProducto = () => {
return(
<FormularioProductoRender/>
);
};
export default FormularioProducto;

View File

@ -0,0 +1,23 @@
import i18next from "i18next";
import es from "../i18n/es";
import { lazy } from "react";
i18next.addResourceBundle('es', 'formProducto', es);
const FormularioProducto = lazy(() => import('./FormularioProducto'));
const FormularioProductoConfig = {
settings: {
layout: {
config: {},
},
},
routes: [
{
path: 'product/form',
element: <FormularioProducto />,
},
],
};
export default FormularioProductoConfig;

View File

@ -0,0 +1,227 @@
import FusePageSimple from "@fuse/core/FusePageSimple";
import {
Autocomplete,
Card,
CardActions,
CardContent,
CardHeader,
Divider,
TextField,
styled,
Button,
Grid,
} from "@mui/material";
import { useState } from "react";
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";
const FormularioProductoRender = () => {
const [datosCargados, setDatosCargados] = useState(false);
const validationSchema = Yup.object().shape({
codigo: Yup.string().required("El código es requerido"),
tipo: Yup.string().required("El tipo es requerido"),
nombre: Yup.string().required("El nombre es requerido"),
estado: Yup.string().required("El estado es requerido"),
stockMinimo: Yup.string().required("El stock mínimo es requerido"),
margenGanancia: Yup.string().required("El margen de ganancia es requerido"),
descripcion: Yup.string().required("La descripción es requerida"),
});
return (
<Card className="m-20 p-24">
<CardHeader title="Registro producto" className="text-16 font-bold " />
<Divider />
<Formik
initialValues={{
codigo: "",
tipo: "",
nombre: "",
estado: "",
stockMinimo: "",
margenGanancia: "",
descripcion: "",
}}
validationSchema={validationSchema}
onSubmit={(values, { setSubmitting }) => {
console.log(values);
setSubmitting(false);
}}
>
{({ isSubmitting, touched, errors, setFieldTouched }) => (
<Form>
<CardContent className="flex">
<Grid className="grid grid-cols-1 sm:grid-cols-6 gap-10 w-full min-w-0">
<div className="mt-20 sm:col-span-3 lg:col-span-3">
<Field
as={TextField}
label="Código"
name="codigo"
variant="outlined"
fullWidth
size="small"
error={touched.codigo && !!errors.codigo}
/>
<ErrorMessage
name="codigo"
component="div"
className="text-red-500 text-md ml-12 pt-4"
/>
</div>
<div className="mt-20 sm:col-span-3 lg:col-span-3">
<Autocomplete
multiple
size="small"
freeSolo
options={["Hola", "Chao"]}
onChange={() => setFieldTouched("tipo", true)}
renderInput={(params) => (
<TextField
{...params}
label="Seleccione el tipo de producto"
name="tipo"
variant="outlined"
error={touched.tipo && !!errors.tipo}
/>
)}
/>
<ErrorMessage
name="tipo"
component="div"
className="text-red-500 text-md ml-12 pt-4"
/>
</div>
<div className="mt-10 sm:col-span-3 lg:col-span-3">
<Field
as={TextField}
label="Nombre"
name="nombre"
variant="outlined"
fullWidth
size="small"
error={touched.nombre && !!errors.nombre}
/>
<ErrorMessage
name="nombre"
component="div"
className="text-red-500 text-md ml-12 pt-4"
/>
</div>
<div className="mt-10 sm:col-span-3 lg:col-span-3">
<Autocomplete
multiple
size="small"
freeSolo
options={["Activo", "Inactivo"]}
onChange={() => setFieldTouched("estado", true)}
renderInput={(params) => (
<TextField
{...params}
label="Seleccione el estado"
name="estado"
variant="outlined"
error={touched.estado && !!errors.estado}
/>
)}
/>
<ErrorMessage
name="estado"
component="div"
className="text-red-500 text-md ml-12 pt-4"
/>
</div>
<div className="mt-10 sm:col-span-3 lg:col-span-3">
<Field
as={TextField}
label="Stock Mínimo"
name="stockMinimo"
variant="outlined"
fullWidth
size="small"
error={touched.stockMinimo && !!errors.stockMinimo}
/>
<ErrorMessage
name="stockMinimo"
component="div"
className="text-red-500 text-md ml-12 pt-4"
/>
</div>
<div className="mt-10 sm:col-span-3 md:col-span-3">
<Field
as={TextField}
label="Margen de Ganancia"
name="margenGanancia"
variant="outlined"
fullWidth
size="small"
error={touched.margenGanancia && !!errors.margenGanancia}
/>
<ErrorMessage
name="margenGanancia"
component="div"
className="text-red-500 text-md ml-12 pt-4"
/>
</div>
<div className="mt-10 sm:col-span-6 lg:col-span-6">
<Field
as={TextField}
id="descripcion"
label="Descripción"
name="descripcion"
type="text"
multiline
rows={5}
variant="outlined"
fullWidth
size="small"
error={touched.descripcion && !!errors.descripcion}
/>
<ErrorMessage
name="descripcion"
component="div"
className="text-red-500 text-md ml-12 pt-4"
/>
</div>
</Grid>
</CardContent>
<CardActions className="">
<Button
className="w-320"
variant="contained"
size="small"
color="secondary"
type="submit"
disabled={isSubmitting}
>
Guardar
</Button>
{datosCargados && (
<Button
className="w-320"
variant="contained"
size="small"
color="primary"
>
Actualizar
</Button>
)}
</CardActions>
</Form>
)}
</Formik>
</Card>
);
};
export default FormularioProductoRender;

View File

@ -0,0 +1,5 @@
const locale = {
TITLE: 'Producto'
};
export default locale;

View File

@ -0,0 +1,81 @@
import * as React from 'react';
import { styled, alpha } from '@mui/material/styles';
import Box from '@mui/material/Box';
import Toolbar from '@mui/material/Toolbar';
import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography';
import InputBase from '@mui/material/InputBase';
import FuseSvgIcon from '@fuse/core/FuseSvgIcon';
import FuseSearch from '@fuse/core/FuseSearch';
import { Card } from '@mui/material';
import { color } from '@mui/system';
const Search = styled('div')(({ theme }) => ({
position: 'relative',
borderRadius: theme.shape.borderRadius,
backgroundColor: alpha(theme.palette.common.black, 0.06),
'&:hover': {
/* backgroundColor: alpha(theme.palette.common.white, 0.25), */
},
width: '100%',
[theme.breakpoints.up('sm')]: {
/* marginLeft: theme.spacing(), */
width: 'auto',
},
}));
const SearchIconWrapper = styled('div')(({ theme }) => ({
padding: theme.spacing(0, 2),
height: '100%',
position: 'absolute',
pointerEvents: 'none',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}));
const StyledInputBase = styled(InputBase)(({ theme }) => ({
color: 'inherit',
width: '100%',
'& .MuiInputBase-input': {
padding: theme.spacing(1, 1, 1, 0),
// vertical padding + font size from searchIcon
paddingLeft: `calc(1em + ${theme.spacing(4)})`,
transition: theme.transitions.create('width'),
},
}));
function BuscarProducto() {
return (
<Box sx={{ flexGrow: 1 }}>
<div
className='bg-white'>
<Typography className="text-lg font-medium tracking-tight leading-6 truncate"
paddingTop={1}
paddingLeft={3}
>
{("Buscar producto")}
</Typography>
<Toolbar>
<Search>
<SearchIconWrapper>
<FuseSvgIcon
className="text-48 text-black"
size={24}
color="action"
>
heroicons-outline:search
</FuseSvgIcon>
</SearchIconWrapper>
<StyledInputBase
placeholder="Buscar Producto"
inputProps={{ 'aria-label': 'search' }}
/>
</Search>
</Toolbar>
</div>
</Box>
);
}
export default BuscarProducto;

View File

@ -0,0 +1,59 @@
import FuseSvgIcon from "@fuse/core/FuseSvgIcon";
import {
Button,
useMediaQuery,
useTheme,
Dialog,
DialogTitle,
DialogContent,
DialogContentText,
DialogActions,
IconButton,
} from "@mui/material";
import React from "react";
function ResponsiveDialog() {
const [open, setOpen] = React.useState(false);
const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down("sm"));
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
<IconButton size="small" color="default"
onClick={handleClickOpen}>
<FuseSvgIcon>heroicons-outline:trash</FuseSvgIcon>
</IconButton>
<Dialog
fullScreen={fullScreen}
open={open}
onClose={handleClose}
aria-labelledby="responsive-dialog-title"
>
<DialogTitle id="responsive-dialog-title">{"Confirmar"}</DialogTitle>
<DialogContent>
<DialogContentText>
Este elemento se eliminará permanentemente. ¿Deseas continuar?.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleClose} color="primary">
Cancelar
</Button>
<Button onClick={handleClose} color="primary" autoFocus>
Aceptar
</Button>
</DialogActions>
</Dialog>
</div>
);
}
export default ResponsiveDialog;

View File

@ -0,0 +1,14 @@
type DetalleProductoRow = {
artCodigo: number;
artNombre: string;
artDescripcion: string;
artStockMinimo: number;
artEstado: any;
};
type DetalleProductoType = {
columns: string[];
rows: DetalleProductoRow[];
};
export default DetalleProductoType;

View File

@ -1,24 +1,9 @@
import { Controller, useForm } from 'react-hook-form';
import Button from '@mui/material/Button';
import Checkbox from '@mui/material/Checkbox';
import FormControl from '@mui/material/FormControl';
import FormControlLabel from '@mui/material/FormControlLabel';
import TextField from '@mui/material/TextField';
import Typography from '@mui/material/Typography';
import { Link } from 'react-router-dom';
import _ from '@lodash';
import Paper from '@mui/material/Paper';
import FuseSvgIcon from '@fuse/core/FuseSvgIcon';
import AvatarGroup from '@mui/material/AvatarGroup';
import Avatar from '@mui/material/Avatar';
import Box from '@mui/material/Box';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
import Tabs from '@mui/material/Tabs';
import Tab from '@mui/material/Tab';
import { useState } from 'react';
import JwtLoginTab from './tabs/JwtSignInTab';
import FirebaseSignInTab from './tabs/FirebaseSignInTab';
/**
* Form Validation Schema
@ -39,14 +24,9 @@ const tabs = [
}
];
function SignInPage() {
const [selectedTabId, setSelectedTabId] = useState(tabs[0].id);
function handleSelectTab(id: string) {
setSelectedTabId("jwt");
}
return (
<div className="flex min-w-0 h-screen flex-auto flex-col justify-center items-center sm:justify-center md:p-32">
<Paper className="flex min-h-full w-full overflow-hidden justify-center items-center rounded-0 sm:min-h-auto sm:w-auto sm:rounded-2xl sm:shadow md:w-full md:max-w-6xl">
@ -93,16 +73,17 @@ function SignInPage() {
/>
))}
</Tabs> */}
{selectedTabId === 'jwt' && <JwtLoginTab />}
{/* {selectedTabId === 'jwt' && <JwtLoginTab />} */}
<JwtLoginTab />
{/* {selectedTabId === 'firebase' && <FirebaseSignInTab />} */}
</div>
</div>
<Box
className="relative hidden h-auto justify-center items-center flex-auto p-64 md:flex lg:px-112"
sx={{ backgroundColor: 'primary.main' }}>
sx={{ backgroundColor: 'primary.main' }}
>
<svg
className="pointer-events-none absolute inset-0"
viewBox="0 0 960 540"
@ -174,14 +155,20 @@ function SignInPage() {
</div>
<div className="flex overflow-hidden items-center mt-4 text-md leading-none text-gray-400">
<div>Soporte técnico: 0967722226</div>
<a aria-label="Chat WhatsApp" href="https://wa.me/967722226/?text=Hola!%20Necesito%20ayuda%20en%20el%20sistema%20de%20facturación">
<img className='w-52' alt="Chat WhatsApp" src="assets/images/logo/WhatsApp-Logo.wine.svg" />
<a
aria-label="Chat WhatsApp"
href="https://wa.me/967722226/?text=Hola!%20Necesito%20ayuda%20en%20el%20sistema%20de%20facturación"
>
<img
className="w-52"
alt="Chat WhatsApp"
src="assets/images/logo/WhatsApp-Logo.wine.svg"
/>
</a>
</div>
<div className="flex overflow-hidden mt-2 text-md leading-none text-gray-400">
<div>Correo eletrónico: info@qsoftec.com</div>
</div>
</div>
</Box>
</Paper>

View File

@ -3,7 +3,7 @@ import Button from '@mui/material/Button';
import { useEffect } from 'react';
import { Controller, useForm } from 'react-hook-form';
import _ from '@lodash';
import { AxiosError } from 'axios';
import axios, { AxiosError } from 'axios';
import FormControl from '@mui/material/FormControl';
import FormControlLabel from '@mui/material/FormControlLabel';
import Checkbox from '@mui/material/Checkbox';
@ -16,7 +16,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
* Form Validation Schema
*/
const schema = z.object({
email: z.string().email('Debe ingresar un correo válido').nonempty('Debe ingresar un correo'),
email: z.string().nonempty('Debe ingresar un correo'),
password: z
.string()
.min(4, 'La contraseña es muy corta, debe ingresar almenos 4 caracteres.')
@ -47,13 +47,57 @@ function jwtSignInTab() {
const { isValid, dirtyFields, errors } = formState;
useEffect(() => {
setValue('email', 'admin@qsoftec.com', { shouldDirty: true, shouldValidate: true });
setValue('password', 'admin', { shouldDirty: true, shouldValidate: true });
setValue('email', 'andres', { shouldDirty: true, shouldValidate: true });
setValue('password', '1234567Aa', { shouldDirty: true, shouldValidate: true });
}, [setValue]);
function onSubmit(formData: FormType) {
const { email, password } = formData;
/* const data = JSON.stringify({
headerIn: {
dispositivo: 'WeLaptop',
canal: null,
medio: null,
aplicacion: 'CardControlWeb',
tipoTransaccion: '0101001',
usuario: 'ADMIN',
uuid: '355b8668e50a06cf894e015c850d5bb9dc58a',
fechaHora: null,
idioma: null,
empresa: null,
geolocalizacion: '37.4215452, -122.0837541'
},
bodyIn: {
entidad: 'Usuario',
tipoConsulta: 1,
parametros: {
user: 'Nick',
password: '21365'
}
}
});
const config = {
method: 'post',
maxBodyLength: Infinity,
url: 'http://services.qsoftec.com:18080/inventario-rs-services-1.0-SNAPSHOT/servicios/autenticacion',
headers: {
'Content-Type': 'application/json'
},
data
};
axios
.request(config)
.then((response) => {
console.log(response.headers)
//console.log(JSON.stringify(response.data));
})
.catch((error) => {
console.log(error);
});
*/
jwtService
.signIn({
email,
@ -97,7 +141,7 @@ function jwtSignInTab() {
className="mb-24"
label="Correo electrónico"
autoFocus
type="email"
// type="email"
error={!!errors.email}
helperText={errors?.email?.message}
variant="outlined"

View File

@ -1,53 +1,25 @@
import Typography from '@mui/material/Typography';
import { Link } from 'react-router-dom';
import AvatarGroup from '@mui/material/AvatarGroup';
import Avatar from '@mui/material/Avatar';
import Box from '@mui/material/Box';
import Paper from '@mui/material/Paper';
import Tabs from '@mui/material/Tabs';
import Tab from '@mui/material/Tab';
import { useState } from 'react';
import _ from '../../../@lodash/@lodash';
import JwtSignUpTab from './tabs/JwSignUpTab';
import FirebaseSignUpTab from './tabs/FirebaseSignUpTab';
const tabs = [
{
id: 'jwt',
title: 'JWT',
logo: 'assets/images/logo/jwt.svg',
logoClass: 'h-40 p-4 bg-black rounded-12'
},
{
id: 'firebase',
title: 'Firebase',
logo: 'assets/images/logo/firebase.svg',
logoClass: 'h-40'
}
];
import FormRegister from './formRegister/FormRegister';
/**
* The sign up page.
*/
function SignUpPage() {
const [selectedTabId, setSelectedTabId] = useState(tabs[0].id);
function handleSelectTab(id: string) {
setSelectedTabId(id);
}
return (
<div className="flex min-w-0 h-screen flex-auto flex-col justify-center items-center sm:justify-center md:p-32">
<Paper className="flex min-h-full w-full overflow-hidden rounded-0 sm:min-h-auto sm:w-auto sm:rounded-2xl sm:shadow md:w-full md:max-w-6xl">
<div className="w-full px-16 py-32 ltr:border-r-1 rtl:border-l-1 sm:w-auto sm:p-48 md:p-64">
<div className="mx-auto w-full max-w-320 sm:mx-0 sm:w-320">
<div className="flex min-w-0 flex-1 flex-col items-center sm:flex-row sm:justify-center md:items-start md:justify-start">
<Paper className="h-full w-full px-16 py-8 ltr:border-r-1 rtl:border-l-1 sm:h-auto sm:w-auto sm:rounded-2xl sm:p-48 sm:shadow md:flex md:h-full md:w-1/2 md:items-center md:justify-end md:rounded-none md:p-64 md:shadow-none">
<div className="mx-auto w-full max-w-400 sm:mx-0 sm:w-400">
<img
className="w-48"
className="w-36"
src="assets/images/logo/logo1.svg"
alt="logo"
/>
<Typography className="mt-32 text-4xl font-extrabold leading-tight tracking-tight">
<Typography className="mt-32 text-3xl font-extrabold leading-tight tracking-tight">
Registrarse
</Typography>
<div className="mt-2 flex items-baseline font-medium">
@ -59,38 +31,15 @@ function SignUpPage() {
Inicia sesión
</Link>
</div>
{/*
<Tabs
value={_.findIndex(tabs, { id: selectedTabId })}
variant="fullWidth"
className="w-full mt-24 mb-32"
indicatorColor="secondary"
>
{tabs.map((item) => (
<Tab
onClick={() => handleSelectTab(item.id)}
key={item.id}
icon={
<img
className={item.logoClass}
src={item.logo}
alt={item.title}
/>
}
className="min-w-0"
label={item.title}
/>
))}
</Tabs>
*/}
<JwtSignUpTab />
{/* {selectedTabId === 'firebase' && <FirebaseSignUpTab />}
*/} </div>
<FormRegister />
{/* <JwtSignUpTab /> */}
</div>
</Paper>
<Box
className="relative hidden h-auto justify-center items-center flex-auto p-64 md:flex lg:px-112"
sx={{ backgroundColor: 'primary.main' }}>
className="relative hidden h-full flex-auto items-center justify-center overflow-hidden p-48 md:flex lg:px-112 w-3/5"
sx={{ backgroundColor: 'primary.main' }}
>
<svg
className="pointer-events-none absolute inset-0"
viewBox="0 0 960 540"
@ -162,17 +111,22 @@ function SignUpPage() {
</div>
<div className="flex overflow-hidden items-center mt-4 text-md leading-none text-gray-400">
<div>Soporte técnico: 0967722226</div>
<a aria-label="Chat WhatsApp" href="https://wa.me/967722226/?text=Hola!%20Necesito%20ayuda%20en%20el%20sistema%20de%20facturación">
<img className='w-52' alt="Chat WhatsApp" src="assets/images/logo/WhatsApp-Logo.wine.svg" />
<a
aria-label="Chat WhatsApp"
href="https://wa.me/967722226/?text=Hola!%20Necesito%20ayuda%20en%20el%20sistema%20de%20facturación"
>
<img
className="w-52"
alt="Chat WhatsApp"
src="assets/images/logo/WhatsApp-Logo.wine.svg"
/>
</a>
</div>
<div className="flex overflow-hidden mt-2 text-md leading-none text-gray-400">
<div>Correo eletrónico: info@qsoftec.com</div>
</div>
</div>
</Box>
</Paper>
</div>
);
}

View File

@ -0,0 +1,58 @@
import { useFormik } from 'formik';
import * as Yup from 'yup';
import { registerUser } from 'src/app/services/user.service';
import { SignUpPayload } from './interface';
import FormRegisterRender from './FormRegisterRender';
function FormRegister() {
const formik = useFormik<SignUpPayload>({
initialValues: {
detCodigo: 0,
empCodContribuyente: '',
empContacto: '',
empDescripcion: '',
empDireccion: '',
empIdentificacion: '',
empLlevaContabilidad: '',
empMail: '',
empNombreComercial: '',
empRazonSocial: '',
password: '',
passwordConfirm: '',
usuNombre: '',
usuUsuario: '',
rolCodigo: 2
},
validationSchema: Yup.object({
usuUsuario: Yup.string().required('Debe ingresar el usuario'),
usuNombre: Yup.string().required('Debe ingresar el nombre de usuario'),
detCodigo: Yup.number().test('len', 'Debe Seleccionar una opción', (val: number) => val !== 0),
empIdentificacion: Yup.string().required('Debe ingresar el número de identificación'),
empRazonSocial: Yup.string().required('Debe ingresar la razón social'),
empNombreComercial: Yup.string().required('Debe ingresar el nombre comercial'),
empContacto: Yup.string().required('Debe ingresar un contacto'),
empDireccion: Yup.string().required('Debe ingresar la dirección'),
empMail: Yup.string().email('Debe ingresar un correo válido').required('Debe ingresar un correo'),
empCodContribuyente: Yup.string().required('Debe ingresar el codigo'),
empDescripcion: Yup.string().required('Debe ingresar una descripción'),
empLlevaContabilidad: Yup.string().required('Debe seleccionar una opción'),
password: Yup.string()
.required('Por favor ingrese su contraseña.')
.min(4, 'La contraseña es muy corta, debe ingresar almenos 4 caracteres'),
passwordConfirm: Yup.string()
.oneOf([Yup.ref('password'), null], 'Las contraseñas no coinciden')
.required('Campo Obligatorio')
}),
onSubmit: (value) => {
console.log('first');
console.log(value);
registerUser(value).then((response) => {
console.log(response);
});
}
});
return <FormRegisterRender formik={formik} />;
}
export default FormRegister;

View File

@ -0,0 +1,298 @@
import { FormikProps } from 'formik';
import { Button, MenuItem, TextField } from '@mui/material';
import { SignUpPayload } from './interface';
interface Props {
formik: FormikProps<SignUpPayload>;
}
const tipoEmpresa = [
{ detCodigo: 1, detNombre: 'Publica' },
{ detCodigo: 3, detNombre: 'Privada' }
];
function FormRegisterRender({ formik }: Props) {
return (
<form
name="registerForm"
noValidate
className="mt-20 flex w-full flex-col justify-center"
onSubmit={formik.handleSubmit}
>
<TextField
className="mb-10"
size="small"
label="Usuario"
type="text"
name="usuUsuario"
onChange={formik.handleChange}
value={formik.values.usuUsuario}
onBlur={formik.handleBlur}
error={formik.errors.usuUsuario && formik.touched.usuUsuario}
helperText={formik.errors.usuUsuario && formik.touched.usuUsuario ? formik.errors.usuUsuario : false}
variant="outlined"
required
fullWidth
/>
<TextField
className="mb-10"
size="small"
label="Nombre usuario"
type="text"
name="usuNombre"
onChange={formik.handleChange}
value={formik.values.usuNombre}
onBlur={formik.handleBlur}
error={formik.errors.usuNombre && formik.touched.usuNombre}
helperText={formik.errors.usuNombre && formik.touched.usuNombre ? formik.errors.usuNombre : false}
variant="outlined"
required
fullWidth
/>
<TextField
className="mb-10"
size="small"
label="Razón social"
type="text"
name="empRazonSocial"
onChange={formik.handleChange}
value={formik.values.empRazonSocial}
onBlur={formik.handleBlur}
error={formik.errors.empRazonSocial && formik.touched.empRazonSocial}
helperText={
formik.errors.empRazonSocial && formik.touched.empRazonSocial ? formik.errors.empRazonSocial : false
}
variant="outlined"
required
fullWidth
/>
<TextField
className="mb-10"
size="small"
label="Nombre comercial"
type="text"
name="empNombreComercial"
onChange={formik.handleChange}
value={formik.values.empNombreComercial}
onBlur={formik.handleBlur}
error={formik.errors.empNombreComercial && formik.touched.empNombreComercial}
helperText={
formik.errors.empNombreComercial && formik.touched.empNombreComercial
? formik.errors.empNombreComercial
: false
}
variant="outlined"
required
fullWidth
/>
<TextField
className="mb-10"
size="small"
label="Identificacón"
type="text"
name="empIdentificacion"
onChange={formik.handleChange}
value={formik.values.empIdentificacion}
onBlur={formik.handleBlur}
error={formik.errors.empIdentificacion && formik.touched.empIdentificacion}
helperText={
formik.errors.empIdentificacion && formik.touched.empIdentificacion
? formik.errors.empIdentificacion
: false
}
variant="outlined"
required
fullWidth
/>
<TextField
className="mb-10"
size="small"
select
label="Tipo de empresa"
name="detCodigo"
onChange={formik.handleChange}
value={formik.values.detCodigo}
onBlur={formik.handleBlur}
error={formik.errors.detCodigo && formik.touched.detCodigo}
helperText={formik.errors.detCodigo && formik.touched.detCodigo ? formik.errors.detCodigo : false}
variant="outlined"
required
fullWidth
>
<MenuItem value={0}>Seleccione..</MenuItem>
{tipoEmpresa.map((option) => (
<MenuItem
key={option.detCodigo}
value={option.detCodigo}
>
{option.detNombre}
</MenuItem>
))}
</TextField>
<TextField
className="mb-10"
size="small"
label="Contacto"
type="text"
name="empContacto"
onChange={formik.handleChange}
value={formik.values.empContacto}
onBlur={formik.handleBlur}
error={formik.errors.empContacto && formik.touched.empContacto}
helperText={formik.errors.empContacto && formik.touched.empContacto ? formik.errors.empContacto : false}
variant="outlined"
required
fullWidth
/>
<TextField
className="mb-10"
size="small"
label="Dirección"
type="text"
name="empDireccion"
onChange={formik.handleChange}
value={formik.values.empDireccion}
onBlur={formik.handleBlur}
error={formik.errors.empDireccion && formik.touched.empDireccion}
helperText={
formik.errors.empDireccion && formik.touched.empDireccion ? formik.errors.empDireccion : false
}
variant="outlined"
required
fullWidth
/>
<TextField
className="mb-10"
size="small"
label="Correo electrónico"
type="email"
name="empMail"
onChange={formik.handleChange}
value={formik.values.empMail}
onBlur={formik.handleBlur}
error={formik.errors.empMail && formik.touched.empMail}
helperText={formik.errors.empMail && formik.touched.empMail ? formik.errors.empMail : false}
variant="outlined"
required
fullWidth
/>
<TextField
className="mb-10"
size="small"
label="Codigo de contribuyente"
type="text"
name="empCodContribuyente"
onChange={formik.handleChange}
value={formik.values.empCodContribuyente}
onBlur={formik.handleBlur}
error={formik.errors.empCodContribuyente && formik.touched.empCodContribuyente}
helperText={
formik.errors.empCodContribuyente && formik.touched.empCodContribuyente
? formik.errors.empCodContribuyente
: false
}
variant="outlined"
required
fullWidth
/>
<TextField
className="mb-10"
size="small"
label="Descripción"
type="text"
name="empDescripcion"
onChange={formik.handleChange}
value={formik.values.empDescripcion}
onBlur={formik.handleBlur}
error={formik.errors.empDescripcion && formik.touched.empDescripcion}
helperText={
formik.errors.empDescripcion && formik.touched.empDescripcion ? formik.errors.empDescripcion : false
}
variant="outlined"
required
fullWidth
/>
<TextField
className="mb-10"
size="small"
select
label="Lleva la contabilidad"
name="empLlevaContabilidad"
onChange={formik.handleChange}
value={formik.values.empLlevaContabilidad}
onBlur={formik.handleBlur}
error={formik.errors.empLlevaContabilidad && formik.touched.empLlevaContabilidad}
helperText={
formik.errors.empLlevaContabilidad && formik.touched.empLlevaContabilidad
? formik.errors.empLlevaContabilidad
: false
}
variant="outlined"
required
fullWidth
>
<MenuItem value="0">NO</MenuItem>
<MenuItem value="1">SI</MenuItem>
</TextField>
<TextField
className="mb-10"
size="small"
label="Contraseña"
type="password"
name="password"
onChange={formik.handleChange}
value={formik.values.password}
onBlur={formik.handleBlur}
error={formik.errors.password && formik.touched.password}
helperText={formik.errors.password && formik.touched.password ? formik.errors.password : false}
variant="outlined"
required
fullWidth
/>
<TextField
className="mb-10"
size="small"
label="Confirmar contraseña"
type="password"
name="passwordConfirm"
onChange={formik.handleChange}
value={formik.values.passwordConfirm}
onBlur={formik.handleBlur}
error={formik.errors.passwordConfirm && formik.touched.passwordConfirm}
helperText={
formik.errors.passwordConfirm && formik.touched.passwordConfirm
? formik.errors.passwordConfirm
: false
}
variant="outlined"
required
fullWidth
/>
<Button
variant="contained"
color="secondary"
className="mt-24 w-full col-span-2"
aria-label="Register"
// disabled={_.isEmpty(dirtyFields)}
type="submit"
size="large"
>
Registrarse
</Button>
</form>
);
}
export default FormRegisterRender;

View File

@ -0,0 +1,17 @@
export interface SignUpPayload {
usuUsuario: string;
usuNombre: string;
detCodigo: number;
empIdentificacion: string;
empRazonSocial: string;
empNombreComercial: string;
empContacto: string;
empDireccion: string;
empMail: string;
empCodContribuyente: string | number;
empDescripcion: string;
empLlevaContabilidad: string | number;
password: string;
passwordConfirm: string;
rolCodigo: number;
}

View File

@ -1,28 +1,40 @@
import { Controller, useForm } from 'react-hook-form';
import TextField from '@mui/material/TextField';
import FormControl from '@mui/material/FormControl';
import FormControlLabel from '@mui/material/FormControlLabel';
import Checkbox from '@mui/material/Checkbox';
import FormHelperText from '@mui/material/FormHelperText';
import Button from '@mui/material/Button';
import _ from '@lodash';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { SignUpPayload, useAuth } from '../../../auth/AuthRouteProvider';
import { MenuItem } from '@mui/material';
import { SignUpPayload } from '../../../auth/AuthRouteProvider';
/**
* Form Validation Schema
*/
const tipoEmpresa = [
{ detCodigo: 1, detNombre: 'Publica' },
{ detCodigo: 2, detNombre: 'Privada' }
];
const schema = z
.object({
displayName: z.string().nonempty('Debe ingresar un nombre de usuario'),
email: z.string().email('Debe ingresar un correo válido').nonempty('Debe ingresar un correo'),
usuUsuario: z.string().nonempty('Debe ingresar el usuario'),
usuNombre: z.string().nonempty('Debe ingresar el nombre de usuario'),
detCodigo: z.number().refine((val) => val !== 0, 'Debe Seleccionar una opción'),
empIdentificacion: z.string().nonempty('Debe ingresar el número de identificación'),
empRazonSocial: z.string().nonempty('Debe ingresar la razón social'),
empNombreComercial: z.string().nonempty('Debe ingresar el nombre comercial'),
empContacto: z.string().nonempty('Debe ingresar un contacto'),
empDireccion: z.string().nonempty('Debe ingresar la dirección'),
empMail: z.string().email('Debe ingresar un correo válido').nonempty('Debe ingresar un correo'),
empCodContribuyente: z.string().nonempty('Debe ingresar el codigo'),
empDescripcion: z.string().nonempty('Debe ingresar una descripción'),
empLlevaContabilidad: z.string().nonempty('Debe seleccionar una opción'),
password: z
.string()
.nonempty('Por favor ingrese su contraseña.')
.min(4, 'La contraseña es muy corta, debe ingresar almenos 4 caracteres'),
passwordConfirm: z.string().nonempty('Debe ingresar su contraseña'),
acceptTermsConditions: z.boolean().refine((val) => val === true, 'Debe aceptar los términos y condiciones')
passwordConfirm: z.string().nonempty('Debe ingresar su contraseña')
// acceptTermsConditions: z.boolean().refine((val) => val === true, 'Debe aceptar los términos y condiciones')
})
.refine((data) => data.password === data.passwordConfirm, {
message: 'Contraseñas no coinciden',
@ -30,16 +42,24 @@ const schema = z
});
const defaultValues = {
displayName: '',
email: '',
usuUsuario: '',
usuNombre: '',
detCodigo: 0,
empIdentificacion: '',
empRazonSocial: '',
empNombreComercial: '',
empContacto: '',
empDireccion: '',
empMail: '',
empCodContribuyente: '',
empDescripcion: '',
empLlevaContabilidad: '',
password: '',
passwordConfirm: '',
acceptTermsConditions: false
passwordConfirm: ''
// acceptTermsConditions: false
};
function JwtSignUpTab() {
const { jwtService } = useAuth();
const { control, formState, handleSubmit, setError } = useForm({
mode: 'onChange',
defaultValues,
@ -49,7 +69,8 @@ function JwtSignUpTab() {
const { isValid, dirtyFields, errors } = formState;
function onSubmit(formData: SignUpPayload) {
const { displayName, email, password } = formData;
console.log(formData);
/* const { displayName, email, password } = formData;
jwtService
.signUp({
displayName,
@ -63,28 +84,28 @@ function JwtSignUpTab() {
_errors.forEach(({ message, type }) => {
setError(type, { type: 'manual', message });
});
});
}); */
}
return (
<form
name="registerForm"
noValidate
className="mt-32 flex w-full flex-col justify-center"
className="mt-20 flex w-full flex-col justify-center"
onSubmit={handleSubmit(onSubmit)}
>
<Controller
name="displayName"
name="usuUsuario"
control={control}
render={({ field }) => (
<TextField
{...field}
className="mb-24"
label="Nombre de usuario"
autoFocus
type="name"
error={!!errors.displayName}
helperText={errors?.displayName?.message}
className="mb-10"
size="small"
label="Usuario"
type="text"
error={!!errors.usuUsuario}
helperText={errors?.usuUsuario?.message}
variant="outlined"
required
fullWidth
@ -93,16 +114,17 @@ function JwtSignUpTab() {
/>
<Controller
name="email"
name="usuNombre"
control={control}
render={({ field }) => (
<TextField
{...field}
className="mb-24"
label="Correo electrónico"
type="email"
error={!!errors.email}
helperText={errors?.email?.message}
className="mb-10"
size="small"
label="Nombre usuario"
type="text"
error={!!errors.usuNombre}
helperText={errors?.usuNombre?.message}
variant="outlined"
required
fullWidth
@ -110,13 +132,199 @@ function JwtSignUpTab() {
)}
/>
<Controller
name="empRazonSocial"
control={control}
render={({ field }) => (
<TextField
{...field}
className="mb-10"
size="small"
label="Razón social"
autoFocus
type="text"
error={!!errors.empRazonSocial}
helperText={errors?.empRazonSocial?.message}
variant="outlined"
required
fullWidth
/>
)}
/>
<Controller
name="empNombreComercial"
control={control}
render={({ field }) => (
<TextField
{...field}
className="mb-10"
size="small"
label="Nombre comercial"
type="text"
error={!!errors.empNombreComercial}
helperText={errors?.empNombreComercial?.message}
variant="outlined"
required
fullWidth
/>
)}
/>
<Controller
name="detCodigo"
control={control}
render={({ field }) => (
<TextField
{...field}
className="mb-10"
size="small"
select
label="Tipo de empresa"
error={!!errors.detCodigo}
helperText={errors?.detCodigo?.message}
variant="outlined"
required
fullWidth
>
<MenuItem value={0}>Seleccione..</MenuItem>
{tipoEmpresa.map((option) => (
<MenuItem
key={option.detCodigo}
value={option.detCodigo}
>
{option.detNombre}
</MenuItem>
))}
</TextField>
)}
/>
<Controller
name="empContacto"
control={control}
render={({ field }) => (
<TextField
{...field}
className="mb-10"
size="small"
label="Contacto"
type="text"
error={!!errors.empContacto}
helperText={errors?.empContacto?.message}
variant="outlined"
required
fullWidth
/>
)}
/>
<Controller
name="empDireccion"
control={control}
render={({ field }) => (
<TextField
{...field}
className="mb-10"
size="small"
label="Dirección"
type="text"
error={!!errors.empDireccion}
helperText={errors?.empDireccion?.message}
variant="outlined"
required
fullWidth
/>
)}
/>
<Controller
name="empMail"
control={control}
render={({ field }) => (
<TextField
{...field}
className="mb-10"
size="small"
label="Correo electrónico"
type="email"
error={!!errors.empMail}
helperText={errors?.empMail?.message}
variant="outlined"
required
fullWidth
/>
)}
/>
<Controller
name="empCodContribuyente"
control={control}
render={({ field }) => (
<TextField
{...field}
className="mb-10"
size="small"
label="Codigo de contribuyente"
type="text"
error={!!errors.empCodContribuyente}
helperText={errors?.empCodContribuyente?.message}
variant="outlined"
required
fullWidth
/>
)}
/>
<Controller
name="empDescripcion"
control={control}
render={({ field }) => (
<TextField
{...field}
className="mb-10"
size="small"
label="Descripción"
type="text"
error={!!errors.empDescripcion}
helperText={errors?.empDescripcion?.message}
variant="outlined"
required
fullWidth
/>
)}
/>
<Controller
name="empLlevaContabilidad"
control={control}
render={({ field }) => (
<TextField
{...field}
className="mb-10"
size="small"
select
label="Lleva la contabilidad"
error={!!errors.empLlevaContabilidad}
helperText={errors?.empLlevaContabilidad?.message}
variant="outlined"
required
fullWidth
>
<MenuItem value="0">NO</MenuItem>
<MenuItem value="1">SI</MenuItem>
</TextField>
)}
/>
<Controller
name="password"
control={control}
render={({ field }) => (
<TextField
{...field}
className="mb-24"
className="mb-10"
size="small"
label="Contraseña"
type="password"
error={!!errors.password}
@ -134,7 +342,8 @@ function JwtSignUpTab() {
render={({ field }) => (
<TextField
{...field}
className="mb-24"
className="mb-10"
size="small"
label="Confirmar contraseña"
type="password"
error={!!errors.passwordConfirm}
@ -146,12 +355,12 @@ function JwtSignUpTab() {
)}
/>
<Controller
{/* <Controller
name="acceptTermsConditions"
control={control}
render={({ field }) => (
<FormControl
className="items-center"
className="items-center col-span-2"
error={!!errors.acceptTermsConditions}
>
<FormControlLabel
@ -166,14 +375,14 @@ function JwtSignUpTab() {
<FormHelperText>{errors?.acceptTermsConditions?.message}</FormHelperText>
</FormControl>
)}
/>
/> */}
<Button
variant="contained"
color="secondary"
className="mt-24 w-full"
className="mt-24 w-full col-span-2"
aria-label="Register"
disabled={_.isEmpty(dirtyFields) || !isValid}
disabled={_.isEmpty(dirtyFields)}
type="submit"
size="large"
>

View File

@ -0,0 +1,177 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
// import { dispatch } from 'app/store/store';
import config from 'app/configs/enviroment';
// import { displayError } from '../notifications/displayMessage';
// import { displayMessage } from 'notifications/displayMessage';
// import { logout } from 'store/reducers/auth/auth';
// eslint-disable-next-line consistent-return
const post = (endpoint, body, transId) => {
const trama = {
headerIn: headerIn(transId),
bodyIn: body
};
const requestOptions = {
method: 'POST',
headers: header(),
body: JSON.stringify(trama),
redirect: 'follow'
};
const apiEndpoints = config.api + endpoint;
try {
return fetch(apiEndpoints, requestOptions).then(handleResponse);
} catch (error) {
console.log(error);
}
};
const postMultipart = (endpoint, body, transId, files) => {
const trama = {
headerIn: headerIn(transId),
bodyIn: body
};
const formData = new FormData();
formData.append('0', JSON.stringify(trama));
if (files.length > 0) {
files.map((file, index) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
formData.append(index + 1, file);
return 0;
});
}
const requestOptions = {
method: 'POST',
headers: headerM(),
body: formData
};
const apiEndpoints = config.api + endpoint;
return fetch(apiEndpoints, requestOptions).then(handleResponse);
};
const header = () => {
const token = localStorage.getItem('token');
const myHeaders = new Headers();
// eslint-disable-next-line no-unused-expressions
token && myHeaders.append('Authorization', `${token}`);
myHeaders.append('Content-Type', 'application/json');
return myHeaders;
};
const headerM = () => {
const token = localStorage.getItem('token');
const myHeaders = new Headers();
// eslint-disable-next-line no-unused-expressions
token && myHeaders.append('Authorization', `${token}`);
// myHeaders.append("Content-Type", "application/json");
return myHeaders;
};
const headerIn = (transId) => {
const user = JSON.parse(localStorage.getItem('user'));
const { headerIn } = config;
headerIn.tipoTransaccion = transId;
if (user) {
// headerIn.usuario = user.usuario;
headerIn.usuario = user.usercode;
} else {
headerIn.usuario = 'user-no';
}
return headerIn;
};
const handleResponse = (response) => {
response.headers.forEach((item,i) => {
console.log(item,i)
})
if (response.status === 401) {
localStorage.removeItem('user');
localStorage.removeItem('token');
// return dispatch(logout());
}
if (!response.ok) {
messageStatusCode(response.clone());
throw response;
} else {
let isBlod = false;
let isHtml = false;
response.headers.forEach((val, key) => {
if (key === 'content-disposition') isBlod = true;
if (val.indexOf('text/html') !== -1) isHtml = true;
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
if (key === 'authorization') localStorage.setItem('accessToken', val);
});
if (isBlod || isHtml) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return response;
}
messageStatusCode(response.clone());
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return response.json().then((data) => data);
}
};
function messageStatusCode(response) {
const code = response.status;
let error = false;
// eslint-disable-next-line default-case
switch (code) {
case 200:
response.json().then((data) => {
if (data.error && data.error.codigo !== '0') {
if (data.error.mensaje) {
// "ERROR NO CONTROLADO:
if (data.error.mensaje.includes('com.stripe.exception.CardException')) {
if (data.error.mensaje.includes('Your card has insufficient funds.; code: card_declined')) {
// displayError('La transacción no se pudo realizar, fondos insuficientes');
} else if (data.error.mensaje.includes('This transaction requires authentication.')) {
// displayError('La transacción no se pudo realizar, su tarjeta requiere autentificación');
} else {
// displayMessage('error', `Error ${data.error.mensaje}`);
// displayError(`Error ${data.error.mensaje}`);
}
} else if (data.error.codigo !== '1002') {
// displayMessage('error', data.error.mensaje);
// displayError(data.error.mensaje);
}
}
}
});
break;
case 502:
error = true;
// displayMessage('error', 'Conexión rechazada');
// displayError('Connection refused');
break;
case 500:
error = true;
// displayError('Internal Error Server');
response.json().then((data) => {
console.error('sdfsdfsdfds', data);
});
break;
case 405:
error = true;
// displayError('Failed to load resource');
break;
case 404:
error = true;
// displayError('Page not Found');
break;
case 401:
error = true;
// displayError('Cierre sesión, su sesión a caducado');
}
return error;
}
export { post, postMultipart };

View File

@ -0,0 +1,43 @@
import { post } from './dataServices';
export interface RegisterUser {
usuUsuario: string;
usuNombre: string;
usuDescripcion?: string;
usuUrlImagen?: string;
detCodigo: number;
rolCodigo?: number;
empIdentificacion: string;
empRazonSocial: string;
empNombreComercial: string;
empContacto: string;
empDireccion: string;
empMail: string;
empCodContribuyente: string | number;
empDescripcion: string;
empLlevaContabilidad: number | string;
password: string;
}
export const loginIn = (username: string, password: string) => {
const body = {
tipoConsulta: 1,
entidad: 'Usuario',
parametros: {
user: username,
password
}
};
const endpoint = '/autenticacion';
return post(endpoint, body, '0101001');
};
export const registerUser = (data: RegisterUser) => {
const body = {
tipoAccion: 1,
entidad: 'Usuario',
entidades: [data]
};
const endpoint = '/accion';
return post(endpoint, body, '0101012');
};

View File

@ -27,7 +27,9 @@ export const addAppMiddleware = dynamicInstance.addMiddleware.withTypes<Config>(
const middlewares: Middleware[] = [apiService.middleware, dynamicMiddleware];
if (process.env.NODE_ENV === 'development') {
const logger = createLogger({ collapsed: (getState, action, logEntry) => (logEntry ? !logEntry.error : true) });
const logger = createLogger({
collapsed: (getState, action, logEntry) => (logEntry ? !logEntry.error : true)
});
middlewares.push(logger);
}
@ -61,6 +63,7 @@ export function configureAppStore(initialState?: RootState) {
const store = configureStore({
reducer: rootReducer,
preloadedState: initialState,
devTools: true,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(middlewares)
}) as Store<RootState>;
@ -108,4 +111,6 @@ export const withAppMiddleware = dynamicInstance.withMiddleware.withTypes<Config
const store = configureAppStore();
export default store;
const { dispatch } = store;
export {store, dispatch};

View File

@ -11,7 +11,7 @@ import { useMemo } from 'react';
import { Provider } from 'react-redux';
import ErrorBoundary from '@fuse/utils/ErrorBoundary';
import AppContext from './AppContext';
import store from './store/store';
import { store } from './store/store';
type ComponentProps = {
name?: string;

View File

@ -0,0 +1,81 @@
import * as React from 'react';
import { styled, alpha } from '@mui/material/styles';
import Box from '@mui/material/Box';
import Toolbar from '@mui/material/Toolbar';
import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography';
import InputBase from '@mui/material/InputBase';
import FuseSvgIcon from '@fuse/core/FuseSvgIcon';
import FuseSearch from '@fuse/core/FuseSearch';
import { Card } from '@mui/material';
import { color } from '@mui/system';
const Search = styled('div')(({ theme }) => ({
position: 'relative',
borderRadius: theme.shape.borderRadius,
backgroundColor: alpha(theme.palette.common.black, 0.06),
'&:hover': {
/* backgroundColor: alpha(theme.palette.common.white, 0.25), */
},
width: '100%',
[theme.breakpoints.up('sm')]: {
/* marginLeft: theme.spacing(), */
width: 'auto',
},
}));
const SearchIconWrapper = styled('div')(({ theme }) => ({
padding: theme.spacing(0, 2),
height: '100%',
position: 'absolute',
pointerEvents: 'none',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}));
const StyledInputBase = styled(InputBase)(({ theme }) => ({
color: 'inherit',
width: '100%',
'& .MuiInputBase-input': {
padding: theme.spacing(1, 1, 1, 0),
// vertical padding + font size from searchIcon
paddingLeft: `calc(1em + ${theme.spacing(4)})`,
transition: theme.transitions.create('width'),
},
}));
function BuscarProducto() {
return (
<Box sx={{ flexGrow: 1 }}>
<div
className='bg-white'>
<Typography className="text-lg font-medium tracking-tight leading-6 truncate"
paddingTop={1}
paddingLeft={3}
>
{("Buscar producto")}
</Typography>
<Toolbar>
<Search>
<SearchIconWrapper>
<FuseSvgIcon
className="text-48 text-black"
size={24}
color="action"
>
heroicons-outline:search
</FuseSvgIcon>
</SearchIconWrapper>
<StyledInputBase
placeholder="Buscar Producto"
inputProps={{ 'aria-label': 'search' }}
/>
</Search>
</Toolbar>
</div>
</Box>
);
}
export default BuscarProducto;

View File

@ -0,0 +1,59 @@
import FuseSvgIcon from "@fuse/core/FuseSvgIcon";
import {
Button,
useMediaQuery,
useTheme,
Dialog,
DialogTitle,
DialogContent,
DialogContentText,
DialogActions,
IconButton,
} from "@mui/material";
import React from "react";
function ResponsiveDialog() {
const [open, setOpen] = React.useState(false);
const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down("sm"));
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
<IconButton size="small" color="default"
onClick={handleClickOpen}>
<FuseSvgIcon>heroicons-outline:trash</FuseSvgIcon>
</IconButton>
<Dialog
fullScreen={fullScreen}
open={open}
onClose={handleClose}
aria-labelledby="responsive-dialog-title"
>
<DialogTitle id="responsive-dialog-title">{"Confirmar"}</DialogTitle>
<DialogContent>
<DialogContentText>
Este elemento se eliminará permanentemente. ¿Deseas continuar?.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleClose} color="primary">
Cancelar
</Button>
<Button onClick={handleClose} color="primary" autoFocus>
Aceptar
</Button>
</DialogActions>
</Dialog>
</div>
);
}
export default ResponsiveDialog;

View File

@ -0,0 +1,14 @@
type DetalleProductoRow = {
artCodigo: number;
artNombre: string;
artDescripcion: string;
artStockMinimo: number;
artEstado: any;
};
type DetalleProductoType = {
columns: string[];
rows: DetalleProductoRow[];
};
export default DetalleProductoType;

View File

@ -17,31 +17,31 @@ table.simple {
}
table.simple thead tr th {
padding: 16px 8px;
/* padding: 16px 16px; */
font-weight: 500;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
white-space: nowrap;
}
table.simple thead tr th:first-child {
padding-left: 24px;
padding-left: 15px;
}
table.simple thead tr th:last-child {
padding-right: 24px;
padding-right: 0px;
}
table.simple tbody tr td {
padding: 12px 8px;
padding: 12px 12px;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
}
table.simple tbody tr td:first-child {
padding-left: 24px;
padding-left: 0px;
}
table.simple tbody tr td:last-child {
padding-right: 24px;
padding-right: 0px;
}
table.simple tbody tr:last-child td {

View File

@ -48,5 +48,5 @@
"tailwind.config.js",
"vite.config.mts",
"vite-env.d.ts"
],
, "src/app/main/producto/.tsx" ],
}

View File

@ -2825,7 +2825,7 @@
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64"
integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==
"@types/hoist-non-react-statics@*", "@types/hoist-non-react-statics@^3.3.0":
"@types/hoist-non-react-statics@*", "@types/hoist-non-react-statics@^3.3.0", "@types/hoist-non-react-statics@^3.3.1":
version "3.3.5"
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494"
integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==
@ -4243,6 +4243,11 @@ deep-is@^0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
deepmerge@^2.1.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==
define-data-property@^1.0.1, define-data-property@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3"
@ -5238,6 +5243,20 @@ form-data@^4.0.0:
combined-stream "^1.0.8"
mime-types "^2.1.12"
formik@^2.4.5:
version "2.4.5"
resolved "https://registry.yarnpkg.com/formik/-/formik-2.4.5.tgz#f899b5b7a6f103a8fabb679823e8fafc7e0ee1b4"
integrity sha512-Gxlht0TD3vVdzMDHwkiNZqJ7Mvg77xQNfmBRrNtvzcHZs72TJppSTDKHpImCMJZwcWPBJ8jSQQ95GJzXFf1nAQ==
dependencies:
"@types/hoist-non-react-statics" "^3.3.1"
deepmerge "^2.1.1"
hoist-non-react-statics "^3.3.0"
lodash "^4.17.21"
lodash-es "^4.17.21"
react-fast-compare "^2.0.1"
tiny-warning "^1.0.2"
tslib "^2.0.0"
fraction.js@^4.3.6:
version "4.3.7"
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7"
@ -6382,6 +6401,11 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
lodash-es@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
lodash.camelcase@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
@ -7296,6 +7320,11 @@ prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
object-assign "^4.1.1"
react-is "^16.13.1"
property-expr@^2.0.5:
version "2.0.6"
resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.6.tgz#f77bc00d5928a6c748414ad12882e83f24aec1e8"
integrity sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==
protobufjs@^7.2.4:
version "7.2.6"
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.2.6.tgz#4a0ccd79eb292717aacf07530a07e0ed20278215"
@ -7383,6 +7412,11 @@ react-draft-wysiwyg@1.15.0:
linkify-it "^2.2.0"
prop-types "^15.7.2"
react-fast-compare@^2.0.1:
version "2.0.4"
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
react-fast-compare@^3.0.1:
version "3.2.2"
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49"
@ -8458,6 +8492,11 @@ throat@^4.1.0:
resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a"
integrity sha512-wCVxLDcFxw7ujDxaeJC6nfl2XfHJNYs8yUYJnvMgtPEFlttP9tHSfRUv2vBe6C4hkVFPWoP1P6ZccbYjmSEkKA==
tiny-case@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/tiny-case/-/tiny-case-1.0.3.tgz#d980d66bc72b5d5a9ca86fb7c9ffdb9c898ddd03"
integrity sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==
tiny-warning@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
@ -8500,6 +8539,11 @@ to-regex@^3.0.1, to-regex@^3.0.2:
regex-not "^1.0.2"
safe-regex "^1.1.0"
toposort@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330"
integrity sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
@ -8600,7 +8644,7 @@ tslib@^1.8.1:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.6.2:
tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
@ -8629,6 +8673,11 @@ type-fest@^0.20.2:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
type-fest@^2.19.0:
version "2.19.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b"
integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==
typed-array-buffer@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60"
@ -8823,6 +8872,11 @@ util@0.12.5:
is-typed-array "^1.1.3"
which-typed-array "^1.1.2"
uuid@^9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
v8-compile-cache-lib@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
@ -9126,6 +9180,16 @@ yocto-queue@^0.1.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
yup@^1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/yup/-/yup-1.3.3.tgz#d2f6020ad1679754c5f8178a29243d5447dead04"
integrity sha512-v8QwZSsHH2K3/G9WSkp6mZKO+hugKT1EmnMqLNUcfu51HU9MDyhlETT/JgtzprnrnQHPWsjc6MUDMBp/l9fNnw==
dependencies:
property-expr "^2.0.5"
tiny-case "^1.0.3"
toposort "^2.0.2"
type-fest "^2.19.0"
zod@3.22.4:
version "3.22.4"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff"