3v324v23 commited on
Commit
19f9ce5
·
1 Parent(s): 93052d8

despliegue

Browse files
.eslintrc.cjs ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ module.exports = {
2
+ root: true,
3
+ env: { browser: true, es2020: true },
4
+ extends: [
5
+ 'eslint:recommended',
6
+ 'plugin:react/recommended',
7
+ 'plugin:react/jsx-runtime',
8
+ 'plugin:react-hooks/recommended',
9
+ ],
10
+ ignorePatterns: ['dist', '.eslintrc.cjs'],
11
+ parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
12
+ settings: { react: { version: '18.2' } },
13
+ plugins: ['react-refresh'],
14
+ rules: {
15
+ 'react-refresh/only-export-components': [
16
+ 'warn',
17
+ { allowConstantExport: true },
18
+ ],
19
+ },
20
+ }
.gitignore ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ node_modules
11
+ dist
12
+ dist-ssr
13
+ *.local
14
+
15
+ # Editor directories and files
16
+ .vscode/*
17
+ !.vscode/extensions.json
18
+ .idea
19
+ .DS_Store
20
+ *.suo
21
+ *.ntvs*
22
+ *.njsproj
23
+ *.sln
24
+ *.sw?
25
+ .env
Dockerfile ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Usa la imagen de Node con la etiqueta 20.10-alpine3.18 como base
2
+ FROM node:20.10-alpine3.18
3
+
4
+ # Establece el directorio de trabajo en la aplicación
5
+ WORKDIR /usr/src/app
6
+
7
+ # Copia los archivos de configuración y el package.json a la imagen
8
+ COPY package*.json ./
9
+
10
+ # Instala las dependencias
11
+ RUN npm install
12
+
13
+ # Copia el resto de la aplicación a la imagen
14
+ COPY . .
15
+
16
+ # Otorga permisos de lectura, escritura y ejecución a todos los archivos y directorios
17
+ RUN chmod -R 777 /usr/src/app
18
+
19
+ # Expone el puerto 7860 (ajusta según sea necesario)
20
+ EXPOSE 7860
21
+
22
+ # Comando para ejecutar la aplicación
23
+ CMD ["npm", "run", "dev"]
index.html ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/logo.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Arte Visual</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.jsx"></script>
12
+ </body>
13
+ </html>
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "intento5",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite --port 7860 --host 0.0.0.0",
8
+ "build": "vite build",
9
+ "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10
+ "preview": "vite preview"
11
+ },
12
+ "dependencies": {
13
+ "react": "^18.2.0",
14
+ "react-dom": "^18.2.0",
15
+ "react-router": "^6.20.1",
16
+ "react-router-dom": "6.20.1"
17
+ },
18
+ "devDependencies": {
19
+ "@types/react": "^18.2.37",
20
+ "@types/react-dom": "^18.2.15",
21
+ "@vitejs/plugin-react": "^4.2.0",
22
+ "autoprefixer": "^10.4.16",
23
+ "eslint": "^8.53.0",
24
+ "eslint-plugin-react": "^7.33.2",
25
+ "eslint-plugin-react-hooks": "^4.6.0",
26
+ "eslint-plugin-react-refresh": "^0.4.4",
27
+ "postcss": "^8.4.32",
28
+ "standard": "^17.1.0",
29
+ "tailwindcss": "^3.3.5",
30
+ "vite": "^5.0.0"
31
+ }
32
+ }
postcss.config.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ export default {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ }
public/logo.svg ADDED
public/vite.svg ADDED
src/App.jsx ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Route, Routes } from "react-router-dom";
2
+ import { NavBar } from "./NavBar";
3
+ import { Prescripcion } from "./Prescripcion";
4
+ import { Catalogo } from "./Catalogo";
5
+ import { useEffect, useState } from "react";
6
+ import { Carrito } from "./Carrito";
7
+ import { obtenerGet } from "./lib/conexionApi";
8
+
9
+ const Home = () => {
10
+ return <div>Home</div>;
11
+ };
12
+
13
+ const Pedidos = () => {
14
+ return <div>Pedidos</div>;
15
+ };
16
+
17
+ const fecha = new Date();
18
+ let dia = fecha.getDate();
19
+ let mes = fecha.getMonth() + 1; // Los meses en JavaScript empiezan desde 0
20
+ let año = fecha.getFullYear();
21
+
22
+ // Asegurándose de que el día y el mes sean de dos dígitos
23
+ if (dia < 10) dia = '0' + dia;
24
+ if (mes < 10) mes = '0' + mes;
25
+
26
+ const fechaFormateada = dia + '/' + mes + '/' + año;
27
+
28
+ export default function App() {
29
+ const [carrito, setCarrito] = useState([]);
30
+ const [monturas, setMonturas] = useState([]);
31
+ const [errorConsultaMonturas, setsetErrorConsultaMonturas] = useState(false);
32
+ const [precioTotal, setPrecioTotal] = useState(0);
33
+ const [prescripcion, setPrescripcion] = useState([]);
34
+ const [fecha, setFecha] = useState('')
35
+
36
+ useEffect(() => {
37
+ obtenerGet("monturas", setMonturas, setsetErrorConsultaMonturas);
38
+ }, []);
39
+
40
+ useEffect(() => {
41
+ setFecha(fechaFormateada)
42
+ }, []);
43
+
44
+ return (
45
+ <div className="flex flex-col items-center h-screen">
46
+ <NavBar />
47
+ <Routes>
48
+ <Route path="/" element={<Home />} />
49
+ <Route path="/pedidos" element={<Pedidos />} />
50
+ <Route path="/prescripcion" element={<Prescripcion />} />
51
+ <Route
52
+ path="/catalogo"
53
+ element={
54
+ <Catalogo
55
+ setCarrito={setCarrito}
56
+ carrito={carrito}
57
+ monturas={monturas}
58
+ setMonturas={setMonturas}
59
+ setPrecioTotal={setPrecioTotal}
60
+ setPrescripcion={setPrescripcion}
61
+ prescripcion={prescripcion}
62
+ />
63
+ }
64
+ />
65
+ <Route
66
+ path="/carrito"
67
+ element={
68
+ <Carrito
69
+ setCarrito={setCarrito}
70
+ carrito={carrito}
71
+ precioTotal={precioTotal}
72
+ setPrecioTotal={setPrecioTotal}
73
+ fecha={fecha}
74
+ prescripcion={prescripcion}
75
+ setPrescripcion={setPrescripcion}
76
+ />
77
+ }
78
+ />
79
+ </Routes>
80
+ </div>
81
+ );
82
+ }
src/Carrito.jsx ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useEffect, useState } from 'react'
2
+ import { CarritoItem } from './components/CarritoItem'
3
+ import { actualizarPut, cargarApi, obtenerEspecifico, registrarPost, obtenerGet } from "./lib/conexionApi";
4
+ import ModalBoleta from './components/ModalBoleta';
5
+ import {CarritoItemLunas} from './components/CarritoItemLunas';
6
+
7
+ export const Carrito = ({setCarrito, carrito, precioTotal, setPrecioTotal, fecha, prescripcion, setPrescripcion}) => {
8
+
9
+ const [modalBoleta, setModalBoleta] = useState(false)
10
+ const [adelanto, setAdelanto] = useState('')
11
+ const [precioLunas, setPrecioLunas] = useState('')
12
+ const [error, setError] = useState('')
13
+ const [errorPrecioLuna, setErrorPrecioLuna] = useState('')
14
+
15
+ const [precioTotalMostrado, setPrecioTotalMostrado] = useState(0)
16
+
17
+ useEffect(() => {
18
+ setPrecioTotal(carrito.reduce((total, carritoItem) => total + (carritoItem["cantidad"]*carritoItem["monturaInventario"]["precio_unit"]), 0))
19
+ }, [])
20
+
21
+ const handleContinuarClick = () => {
22
+ if (adelanto === '' || precioLunas === '') {
23
+ setError('todos los campos son obligatorios')
24
+ } else if (isNaN(parseFloat(adelanto)) || isNaN(parseFloat(precioLunas))) {
25
+ setError('Ingresar campos válidos')
26
+ } else if (adelanto < (precioTotal+parseFloat(precioLunas))*0.3) {
27
+ setError('El adelanto debe ser mayor al 30% del total')
28
+ } else {
29
+ setModalBoleta(true)
30
+ setError('')
31
+ setErrorPrecioLuna('')
32
+ }
33
+ }
34
+
35
+ return (
36
+ <div className='w-3/5'>
37
+ <div className='flex justify-between'>
38
+ <h1 className="my-10 text-4xl font-semibold text-center">Carrito de compras</h1>
39
+
40
+ <button className='bg-slate-700 text-white my-8 rounded-lg hover:bg-slate-800 uppercase text-xl font-semibold px-5' onClick={handleContinuarClick}>
41
+ Continuar
42
+ </button>
43
+ </div>
44
+ <div className='border-t border-black w-full'>
45
+ {(carrito.length > 0) && carrito.map((item) => {
46
+ return <CarritoItem key={item["monturaInventario"]["codigo"]} carritoItem={item} setCarrito={setCarrito} precioTotal={precioTotal} setPrecioTotal={setPrecioTotal} carrito={carrito} />
47
+ })}
48
+ { prescripcion.length !== 0 && <CarritoItemLunas prescripcion={prescripcion} precioLunas={precioLunas} setPrecioLunas={setPrecioLunas} setPrecioTotal={setPrecioTotal} precioTotal={precioTotal} />}
49
+ </div>
50
+ <div className='flex justify-end mt-10 items-center'>
51
+ <label htmlFor="input-buscar-cliente" className='mr-2 font-semibold'>Adelanto: S/.</label>
52
+ <input
53
+ className="border w-[6rem] py-2 px-3 rounded-md text-right"
54
+ id="input-buscar-cliente "
55
+ type="text"
56
+ placeholder="00.00"
57
+ onChange={(e) => {
58
+ setAdelanto(e.target.value)
59
+ }}
60
+ value={adelanto}
61
+ />
62
+ </div>
63
+ {(modalBoleta && precioTotal !== 0) && (
64
+ <ModalBoleta
65
+ fecha={fecha}
66
+ setModalBoleta={setModalBoleta}
67
+ precioTotal={precioTotal+parseFloat(precioLunas)}
68
+ carrito={carrito}
69
+ prescripcion={prescripcion}
70
+ adelanto={adelanto}
71
+ precioLunas={precioLunas}
72
+ setPrescripcion={setPrescripcion}
73
+ setPrecioTotal={setPrecioTotal}
74
+ setCarrito={setCarrito}
75
+ />
76
+ )}
77
+ {errorPrecioLuna && <p className="text-red-500 text-xl">{errorPrecioLuna}</p>}
78
+ {error && <p className="text-red-500 text-xl">{error}</p>}
79
+ </div>
80
+ )
81
+ }
src/Catalogo.jsx ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useEffect, useState } from "react";
2
+ import CatalogoMonturaCard from "./components/CatalogoMonturaCard";
3
+ import {
4
+ actualizarPut,
5
+ cargarApi,
6
+ obtenerEspecifico,
7
+ registrarPost,
8
+ obtenerGet,
9
+ prescripcionPdff,
10
+ } from "./lib/conexionApi";
11
+ import ModalVerCarrito from "./components/ModalVerCarrito";
12
+ import { Navigate } from "react-router";
13
+
14
+ export const Catalogo = ({ setCarrito, carrito, monturas, setMonturas, setPrecioTotal, setPrescripcion, prescripcion }) => {
15
+ // Se obtienen primero los detalles de cada montura
16
+
17
+ // se obtienen los datos de precio, stock y código
18
+ const [monturasInventario, setmonturasInventario] = useState([]);
19
+
20
+ const [montura, setMontura] = useState({})
21
+ const [monturaInventario, setMonturaInventario] = useState({})
22
+
23
+ const [errorConsultaMonturasInv, setsetErrorConsultaMonturasInv] =
24
+ useState(false);
25
+
26
+ const [errorUltimaPrescripcion, setsetErrorUltimaPrescripcion] =
27
+ useState(false);
28
+
29
+ const [btnBuscarMonturaCodigo, setbtnBuscarMonturaCodigo] = useState(false);
30
+
31
+ const [modalVerCarrito, setModalVerCarrito] = useState(false)
32
+
33
+ const [irAlCarrito, setIrAlCarrito] = useState(false)
34
+
35
+ useEffect(() => {
36
+
37
+ }, []);
38
+
39
+ // obtener catalogo de monturas
40
+ useEffect(() => {
41
+ obtenerGet(
42
+ "monturas_inventario",
43
+ setmonturasInventario,
44
+ setsetErrorConsultaMonturasInv
45
+ );
46
+ }, []);
47
+
48
+ useEffect(() => {
49
+ const pdfPrescripcion = async () => {
50
+ await obtenerGet("prescripciones/ultimoregistro", setPrescripcion, setsetErrorUltimaPrescripcion);
51
+ await prescripcionPdff(prescripcion[0]["id_prescripcion"])
52
+ }
53
+ pdfPrescripcion()
54
+ }, []);
55
+
56
+
57
+
58
+ return (
59
+ <div className="mx-20 mt-5">
60
+
61
+ <div className="flex flex-col justify-center items-center">
62
+ <h1 className="my-10 text-5xl font-semibold text-center">Catálogo de monturas</h1>
63
+ {/* <div className="flex flex-row gap-2 mb-2 w-1/3">
64
+ <input
65
+ className="border w-3/4 py-2 px-3 rounded-md"
66
+ id="input-buscar-cliente "
67
+ type="text"
68
+ placeholder="Código de montura"
69
+ />
70
+ <button className="rounded-md w-1/4 bg-slate-700 hover:bg-slate-800 text-white font-semibold px-2 text-center">
71
+ Buscar
72
+ </button>
73
+ </div> */}
74
+ </div>
75
+ <div className="mt-5 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2">
76
+ {monturasInventario.map((monturaInventario) => {
77
+ const montura = monturas.find(
78
+ (montura) =>
79
+ montura["id_montura"] === monturaInventario["id_montura"]
80
+ );
81
+ return (
82
+ <CatalogoMonturaCard
83
+ key={montura["id_montura"]}
84
+ montura={montura}
85
+ monturaInventario={monturaInventario}
86
+ setModalVerCarrito={setModalVerCarrito}
87
+ setMontura={setMontura}
88
+ setMonturaInventario={setMonturaInventario}
89
+ setCarrito={setCarrito}
90
+ carrito={carrito}
91
+ setPrecioTotal={setPrecioTotal}
92
+ />
93
+ );
94
+ })}
95
+ </div>
96
+ {modalVerCarrito && (
97
+ <ModalVerCarrito montura={montura} monturaInventario={monturaInventario} setModalVerCarrito={setModalVerCarrito} carrito={carrito} setMontura={setMontura} setMonturaInventario={setMonturaInventario} setIrAlCarrito={setIrAlCarrito} />
98
+ )}
99
+
100
+ {irAlCarrito && (
101
+ <Navigate to="/carrito" replace={true}/>
102
+ )}
103
+
104
+ </div>
105
+ );
106
+ };
src/NavBar.jsx ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from "react";
2
+ import { NavLink } from "react-router-dom";
3
+
4
+ export const NavBar = () => {
5
+ return (
6
+ <header className="w-full mb-10">
7
+ <div className="bg-slate-500 text-white text-center pt-20 pb-10">
8
+ <h1 className="text-4xl font-semibold ">Óptica Arte Visual</h1>
9
+ <p className="mt-5">La mejor calidad en monturas con modelos exclusivos y resinas de alta calidad</p>
10
+ </div>
11
+ <nav className="bg-slate-700">
12
+ <ul id="nav-ul" className="flex flex-row justify-center text-xl">
13
+ <NavLink className="w-1/5 text-center" to="/">
14
+ <li className="hover:bg-slate-800 text-white py-2 hover:font-semibold">
15
+ Home
16
+ </li>
17
+ </NavLink>
18
+ <NavLink className="w-1/5 text-center" to="/pedidos">
19
+ <li className="hover:bg-slate-800 text-white py-2 hover:font-semibold">
20
+ Pedidos
21
+ </li>
22
+ </NavLink>
23
+ <NavLink className="w-1/5 text-center" to="/prescripcion">
24
+ <li className="hover:bg-slate-800 text-white py-2 hover:font-semibold">
25
+ Emitir Prescripción
26
+ </li>
27
+ </NavLink>
28
+ <NavLink className="w-1/5 text-center" to="/catalogo">
29
+ <li className="hover:bg-slate-800 text-white py-2 hover:font-semibold">
30
+ Catálogo
31
+ </li>
32
+ </NavLink>
33
+ <NavLink className="w-1/5 text-center" to="/carrito">
34
+ <li className="hover:bg-slate-800 text-white py-2 hover:font-semibold">
35
+ Carrito de compras
36
+ </li>
37
+ </NavLink>
38
+ </ul>
39
+ </nav>
40
+ </header>
41
+ );
42
+ };
src/Prescripcion.jsx ADDED
@@ -0,0 +1,392 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useEffect, useState } from "react";
2
+ import { actualizarPut, cargarApi, obtenerEspecifico, registrarPost } from "./lib/conexionApi";
3
+ import Modal from "./components/Modal";
4
+ import ModalPrescripcion from "./components/ModalPrescripcion";
5
+ import TablaMedidas from "./components/TablaMedidas";
6
+ import BarraBusquedaCliente from "./components/BarraBusquedaCliente";
7
+ import DatosCliente from "./components/DatosCliente";
8
+ import { Navigate } from "react-router-dom";
9
+ import ErrorMessage from "./components/ErrorMessage";
10
+
11
+ const fecha = new Date();
12
+ let dia = fecha.getDate();
13
+ let mes = fecha.getMonth() + 1; // Los meses en JavaScript empiezan desde 0
14
+ let año = fecha.getFullYear();
15
+
16
+ // Asegurándose de que el día y el mes sean de dos dígitos
17
+ if (dia < 10) dia = '0' + dia;
18
+ if (mes < 10) mes = '0' + mes;
19
+
20
+ const fechaFormateada = dia + '/' + mes + '/' + año;
21
+
22
+
23
+ export const Prescripcion = () => {
24
+ // States para la búsqueda de clientes
25
+ const [clienteBusqueda, setClienteBusqueda] = useState("");
26
+ const [btnBuscarClick, setBtnBuscarClick] = useState(false);
27
+ const [clientes, setClientes] = useState([]);
28
+ const [cliente, setCliente] = useState({});
29
+ const [openModal, setOpenModal] = useState(false);
30
+ const [openModalPrescripcion, setOpenModalPrescripcion] = useState(false)
31
+
32
+ // States para medidas
33
+ const [medidas, setMedidas] = useState([]);
34
+ const [medidasEncontradas, setMedidasEncontradas] = useState(false);
35
+ const [asignacionNuevasMedidas, setAsignacionNuevasMedidas] = useState(false)
36
+ const [actualizacionMedidas, setActualizacionMedidas] = useState(false)
37
+
38
+ // lejos
39
+ const [esferaODlejos, setEsferaODLejos] = useState(0);
40
+ const [cilindroODlejos, setCilindroODlejos] = useState(0);
41
+ const [ejeODlejos, setEjeODlejos] = useState(0);
42
+ const [agudezavisualODlejos, setAgudezavisualODlejos] = useState(0);
43
+ const [esferaOIlejos, setEsferaOIlejos] = useState(0);
44
+ const [cilindroOIlejos, setCilindroOIlejos] = useState(0);
45
+ const [ejeOIlejos, setEjeOIlejos] = useState(0);
46
+ const [agudezavisualOIlejos, setAgudezavisualOIlejos] = useState(0);
47
+
48
+ // cerca
49
+ const [esferaODcerca, setEsferaODcerca] = useState(0);
50
+ const [cilindroODcerca, setCilindroODcerca] = useState(0);
51
+ const [ejeODcerca, setEjeODcerca] = useState(0);
52
+ const [agudezavisualODcerca, setAgudezavisualODcerca] = useState(0);
53
+ const [esferaOIcerca, setEsferaOIcerca] = useState(0);
54
+ const [cilindroOIcerca, setCilindroOIcerca] = useState(0);
55
+ const [ejeOIcerca, setEjeOIcerca] = useState(0);
56
+ const [agudezavisualOIcerca, setAgudezavisualOIcerca] = useState(0);
57
+
58
+ // States para crear nuevo cliente
59
+ const [nombresYApellidos, setNombresYApellidos] = useState("");
60
+ const [edad, setEdad] = useState("");
61
+ const [telefono, setTelefono] = useState("");
62
+ const [direccion, setDireccion] = useState("");
63
+
64
+ // States para crear nueva prescripción
65
+ const [notaAdicional, setNotaAdicional] = useState("");
66
+ const [prescripcionLista, setPrescripcionLista] = useState(false)
67
+ const [prescripcionRegistrada, setPrescripcionRegistrada] = useState(false)
68
+
69
+ // errores
70
+ const [error, setError] = useState(false)
71
+
72
+
73
+ useEffect(() => {
74
+ cargarApi()
75
+ }, []);
76
+
77
+ useEffect(() => {
78
+ if (btnBuscarClick) {
79
+ obtenerEspecifico(
80
+ "clientes/busqueda",
81
+ { nombres_y_apellidos: clienteBusqueda },
82
+ setClientes, setError
83
+ );
84
+
85
+ } else return;
86
+ }, [btnBuscarClick]);
87
+
88
+ useEffect(() => {
89
+ if (Object.keys(cliente).length !== 0 || asignacionNuevasMedidas) {
90
+ obtenerEspecifico(
91
+ "medidas/busqueda",
92
+ { "id_cliente": parseInt(cliente["id_cliente"]) },
93
+ setMedidas, setError
94
+ );
95
+ console.log('medidas encontradas');
96
+ } else return;
97
+ }, [cliente, asignacionNuevasMedidas]);
98
+
99
+ useEffect(() => {
100
+ if (Object.keys(cliente).length !== 0 || actualizacionMedidas) {
101
+ obtenerEspecifico(
102
+ "medidas/busqueda",
103
+ { "id_cliente": parseInt(cliente["id_cliente"]) },
104
+ setMedidas, setError
105
+ );
106
+ console.log('medidas encontradas');
107
+ } else return
108
+ }, [actualizacionMedidas])
109
+
110
+ useEffect(() => {
111
+ if (medidas.length !== 0) {
112
+ setMedidasEncontradas(true);
113
+ setEsferaODLejos(medidas[0]["Esfera_OD_lejos"]);
114
+ setCilindroODlejos(medidas[0]["Cilindro_OD_lejos"]);
115
+ setEjeODlejos(medidas[0]["Eje_OD_lejos"]);
116
+ setAgudezavisualODlejos(medidas[0]["Agudeza_visual_OD_lejos"]);
117
+ setEsferaOIlejos(medidas[0]["Esfera_OI_lejos"]);
118
+ setCilindroOIlejos(medidas[0]["Cilindro_OI_lejos"]);
119
+ setEjeOIlejos(medidas[0]["Eje_OI_lejos"]);
120
+ setAgudezavisualOIlejos(medidas[0]["Agudeza_visual_OI_lejos"]);
121
+
122
+ setEsferaODcerca(medidas[0]["Esfera_OD_cerca"]);
123
+ setCilindroODcerca(medidas[0]["Cilindro_OD_cerca"]);
124
+ setEjeODcerca(medidas[0]["Eje_OD_cerca"]);
125
+ setAgudezavisualODcerca(medidas[0]["Agudeza_visual_OD_cerca"]);
126
+ setEsferaOIcerca(medidas[0]["Esfera_OI_cerca"]);
127
+ setCilindroOIcerca(medidas[0]["Cilindro_OI_cerca"]);
128
+ setEjeOIcerca(medidas[0]["Eje_OI_cerca"]);
129
+ setAgudezavisualOIcerca(medidas[0]["Agudeza_visual_OI_cerca"]);
130
+ } else {
131
+ setMedidasEncontradas(false);
132
+ setEsferaODLejos(0);
133
+ setCilindroODlejos(0);
134
+ setEjeODlejos(0);
135
+ setAgudezavisualODlejos(0);
136
+ setEsferaOIlejos(0);
137
+ setCilindroOIlejos(0);
138
+ setEjeOIlejos(0);
139
+ setAgudezavisualOIlejos(0);
140
+
141
+ setEsferaODcerca(0);
142
+ setCilindroODcerca(0);
143
+ setEjeODcerca(0);
144
+ setAgudezavisualODcerca(0);
145
+ setEsferaOIcerca(0);
146
+ setCilindroOIcerca(0);
147
+ setEjeOIcerca(0);
148
+ setAgudezavisualOIcerca(0);
149
+ }
150
+ }, [medidas]);
151
+
152
+
153
+ const handleButtonBuscarCliente = (e) => {
154
+ e.preventDefault();
155
+ if (clienteBusqueda !== "") {
156
+ setBtnBuscarClick(true);
157
+ } else setBtnBuscarClick(false);
158
+ //console.log(clientes);
159
+ };
160
+
161
+ const handleSubmitEmitirPrescripcion = (e) => {
162
+ e.preventDefault()
163
+ if (Object.keys(cliente).length !== 0) {
164
+ const nuevoMonturas = {
165
+ "Esfera_OD_lejos": parseFloat(esferaODlejos),
166
+ "Cilindro_OD_lejos": parseFloat(cilindroODlejos),
167
+ "Eje_OD_lejos": parseFloat(ejeODlejos),
168
+ "Agudeza_visual_OD_lejos": parseFloat(agudezavisualODlejos),
169
+ "Esfera_OI_lejos": parseFloat(esferaOIlejos),
170
+ "Cilindro_OI_lejos": parseFloat(cilindroOIlejos),
171
+ "Eje_OI_lejos": parseFloat(ejeOIlejos),
172
+ "Agudeza_visual_OI_lejos": parseFloat(agudezavisualOIlejos),
173
+ "Esfera_OD_cerca": parseFloat(esferaODcerca),
174
+ "Cilindro_OD_cerca": parseFloat(cilindroODcerca),
175
+ "Eje_OD_cerca": parseFloat(ejeODcerca),
176
+ "Agudeza_visual_OD_cerca": parseFloat(agudezavisualODcerca),
177
+ "Esfera_OI_cerca": parseFloat(esferaOIcerca),
178
+ "Cilindro_OI_cerca": parseFloat(cilindroOIcerca),
179
+ "Eje_OI_cerca": parseFloat(ejeOIcerca),
180
+ "Agudeza_visual_OI_cerca": parseFloat(agudezavisualOIcerca),
181
+ "id_cliente": cliente["id_cliente"],
182
+ };
183
+
184
+ if (!medidasEncontradas ) {
185
+ registrarPost('medidas', nuevoMonturas)
186
+ console.log('medidas registradas');
187
+ setAsignacionNuevasMedidas(true)
188
+ console.log('medidas encontradas');
189
+ } else {
190
+ if (medidas.length !== 0) {
191
+ const actualizarMonturas = {
192
+ "id_medidas": medidas[0]["id_medidas"],
193
+ "Esfera_OD_lejos": parseFloat(esferaODlejos),
194
+ "Cilindro_OD_lejos": parseFloat(cilindroODlejos),
195
+ "Eje_OD_lejos": parseFloat(ejeODlejos),
196
+ "Agudeza_visual_OD_lejos": parseFloat(agudezavisualODlejos),
197
+ "Esfera_OI_lejos": parseFloat(esferaOIlejos),
198
+ "Cilindro_OI_lejos": parseFloat(cilindroOIlejos),
199
+ "Eje_OI_lejos": parseFloat(ejeOIlejos),
200
+ "Agudeza_visual_OI_lejos": parseFloat(agudezavisualOIlejos),
201
+ "Esfera_OD_cerca": parseFloat(esferaODcerca),
202
+ "Cilindro_OD_cerca": parseFloat(cilindroODcerca),
203
+ "Eje_OD_cerca": parseFloat(ejeODcerca),
204
+ "Agudeza_visual_OD_cerca": parseFloat(agudezavisualODcerca),
205
+ "Esfera_OI_cerca": parseFloat(esferaOIcerca),
206
+ "Cilindro_OI_cerca": parseFloat(cilindroOIcerca),
207
+ "Eje_OI_cerca": parseFloat(ejeOIcerca),
208
+ "Agudeza_visual_OI_cerca": parseFloat(agudezavisualOIcerca),
209
+ "id_cliente": cliente["id_cliente"],
210
+ };
211
+ actualizarPut("medidas",actualizarMonturas)
212
+ setActualizacionMedidas(true)
213
+ }
214
+ }
215
+ }
216
+
217
+ setOpenModalPrescripcion(true)
218
+
219
+ console.log('submit');
220
+ }
221
+
222
+ return (
223
+ <div className=" w-3/5 flex flex-row my-5 p-5 h-auto">
224
+ <div className="w-2/5">
225
+ {(btnBuscarClick && error) && <ErrorMessage mensaje={'No se encontraron resultados'}/>}
226
+ <div className=" flex flex-row">
227
+ <div className="w-full pr-5">
228
+ {/* BARRA DE BÚSQUEDA DE CLIENTE */}
229
+ <BarraBusquedaCliente
230
+ clienteBusqueda = {clienteBusqueda}
231
+ setClienteBusqueda = {setClienteBusqueda}
232
+ setBtnBuscarClick = {setBtnBuscarClick}
233
+ setAsignacionNuevasMedidas = {setAsignacionNuevasMedidas}
234
+ handleButtonBuscarCliente = {handleButtonBuscarCliente}
235
+ setError = {setError}
236
+ />
237
+
238
+ {/* DROPDOWN DE CLIENTES ENCONTRADOS */}
239
+
240
+
241
+
242
+ {clientes && clienteBusqueda !== "" && btnBuscarClick !== false
243
+ ? <ul>
244
+ {clientes.map((cli) => {
245
+ return (
246
+ <li
247
+ key={cli["id_cliente"]}
248
+ onClick={() => {
249
+ setBtnBuscarClick(false);
250
+ setClienteBusqueda("");
251
+ setCliente(cli);
252
+ setNotaAdicional("");
253
+ }}
254
+ className="cursor-pointer hover:font-semibold hover:bg-slate-50 p-2 "
255
+ >
256
+ <p>{cli["nombres_y_apellidos"]}</p>
257
+ <p className="text-sm text-slate-400">{cli["edad"]}</p>
258
+ <p className="text-sm text-slate-400">{cli["telefono"]}</p>
259
+ <p className="text-sm text-slate-400">{cli["direccion"]}</p>
260
+ </li>
261
+ );
262
+ })}
263
+ </ul>
264
+ : ""}
265
+
266
+ </div>
267
+
268
+ <svg
269
+ xmlns="http://www.w3.org/2000/svg"
270
+ className="icon icon-tabler icon-tabler-user-plus cursor-pointer hover:stroke-slate-800 mr-5"
271
+ width="40"
272
+ height="40"
273
+ viewBox="0 0 24 24"
274
+ strokeWidth="2.5"
275
+ stroke="#334155"
276
+ fill="none"
277
+ strokeLinecap="round"
278
+ strokeLinejoin="round"
279
+ /* ABRIR MODAL PARA AGREGAR CLIENTE */
280
+ onClick={() => {
281
+ console.log("agregar");
282
+ setOpenModal(true);
283
+ }}
284
+ >
285
+ <path stroke="none" d="M0 0h24v24H0z" fill="none" />
286
+ <path d="M8 7a4 4 0 1 0 8 0a4 4 0 0 0 -8 0" />
287
+ <path d="M16 19h6" />
288
+ <path d="M19 16v6" />
289
+ <path d="M6 21v-2a4 4 0 0 1 4 -4h4" />
290
+ </svg>
291
+
292
+ </div>
293
+ </div>
294
+
295
+ {/* MOSTRAR CLIENTE SELECCIONADO */}
296
+ {Object.keys(cliente).length !== 0 ? (
297
+ <form onSubmit={handleSubmitEmitirPrescripcion} className="px-5 flex flex-col w-3/5 h-full pb-5 border-l-2">
298
+ <DatosCliente
299
+ cliente={cliente}
300
+ notaAdicional={notaAdicional}
301
+ setNotaAdicional={setNotaAdicional}
302
+ />
303
+
304
+ {!medidasEncontradas && (
305
+ <p className="mt-5 text-center text-xl text-gray-500">
306
+ Llena las medidas del nuevo cliente
307
+ </p>
308
+ )}
309
+
310
+ <TablaMedidas
311
+ esferaODlejos={esferaODlejos}
312
+ setEsferaODLejos={setEsferaODLejos}
313
+ cilindroODlejos={cilindroODlejos}
314
+ setCilindroODlejos={setCilindroODlejos}
315
+ ejeODlejos={ejeODlejos}
316
+ setEjeODlejos={setEjeODlejos}
317
+ agudezavisualODlejos={agudezavisualODlejos}
318
+ setAgudezavisualODlejos={setAgudezavisualODlejos}
319
+ esferaOIlejos={esferaOIlejos}
320
+ setEsferaOIlejos={setEsferaOIlejos}
321
+ cilindroOIlejos={cilindroOIlejos}
322
+ setCilindroOIlejos={setCilindroOIlejos}
323
+ ejeOIlejos={ejeOIlejos}
324
+ setEjeOIlejos={setEjeOIlejos}
325
+ agudezavisualOIlejos={agudezavisualOIlejos}
326
+ setAgudezavisualOIlejos={setAgudezavisualOIlejos}
327
+ esferaODcerca={esferaODcerca}
328
+ setEsferaODcerca={setEsferaODcerca}
329
+ cilindroODcerca={cilindroODcerca}
330
+ setCilindroODcerca={setCilindroODcerca}
331
+ ejeODcerca={ejeODcerca}
332
+ setEjeODcerca={setEjeODcerca}
333
+ agudezavisualODcerca={agudezavisualODcerca}
334
+ setAgudezavisualODcerca={setAgudezavisualODcerca}
335
+ esferaOIcerca={esferaOIcerca}
336
+ setEsferaOIcerca={setEsferaOIcerca}
337
+ cilindroOIcerca={cilindroOIcerca}
338
+ setCilindroOIcerca={setCilindroOIcerca}
339
+ ejeOIcerca={ejeOIcerca}
340
+ setEjeOIcerca={setEjeOIcerca}
341
+ agudezavisualOIcerca={agudezavisualOIcerca}
342
+ setAgudezavisualOIcerca={setAgudezavisualOIcerca}
343
+ />
344
+
345
+ <button type="submit" className="rounded-md w-full py-3 mt-10 bg-slate-700 hover:bg-slate-800 text-white font-semibold uppercase ">
346
+ Emitir Prescripción
347
+ </button>
348
+ </form>
349
+ ) : (
350
+ <div className="px-5 flex flex-col w-3/5 h-full pb-20 border-l-2">
351
+ <p className="text-gray-400 text-2xl font-semibold">
352
+ No se ha seleccionado ningún cliente
353
+ </p>
354
+ </div>
355
+ )}
356
+
357
+ {openModal && (
358
+ <Modal
359
+ openModal={openModal}
360
+ setOpenModal={setOpenModal}
361
+ nombresYApellidos={nombresYApellidos}
362
+ setNombresYApellidos={setNombresYApellidos}
363
+ edad={edad}
364
+ setEdad={setEdad}
365
+ telefono={telefono}
366
+ setTelefono={setTelefono}
367
+ direccion={direccion}
368
+ setDireccion={setDireccion}
369
+ setError={setError}
370
+ setClienteBusqueda={setClienteBusqueda}
371
+ />
372
+ )}
373
+ {openModalPrescripcion && (
374
+ <ModalPrescripcion
375
+ setOpenModalPrescripcion={setOpenModalPrescripcion}
376
+ setPrescripcionLista={setPrescripcionLista}
377
+ cliente={cliente}
378
+ medidas={medidas}
379
+ fechaFormateada={fechaFormateada}
380
+ notaAdicional={notaAdicional}
381
+ setActualizacionMedidas={setActualizacionMedidas}
382
+ setAsignacionNuevasMedidas={setAsignacionNuevasMedidas}
383
+ setPrescripcionRegistrada={setPrescripcionRegistrada}
384
+ />
385
+ )}
386
+ {prescripcionRegistrada && (
387
+ <Navigate to="/catalogo" replace={true} />
388
+ )}
389
+
390
+ </div>
391
+ );
392
+ }
src/assets/lentes-prueba.png ADDED
src/assets/lunas.jpg ADDED
src/assets/lunas.webp ADDED
src/assets/react.svg ADDED
src/components/BarraBusquedaCliente.jsx ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from "react";
2
+
3
+ const BarraBusquedaCliente = ({
4
+ clienteBusqueda,
5
+ setClienteBusqueda,
6
+ setBtnBuscarClick,
7
+ setAsignacionNuevasMedidas,
8
+ handleButtonBuscarCliente,
9
+ setError
10
+ }) => {
11
+ return (
12
+ <li className="flex flex-row gap-2 mb-2">
13
+ <input
14
+ className="border w-3/4 py-2 px-3 rounded-md"
15
+ id="input-buscar-cliente "
16
+ type="text"
17
+ placeholder="Nombre del cliente"
18
+ onChange={(e) => {
19
+ setClienteBusqueda(e.target.value);
20
+ setBtnBuscarClick(false);
21
+ setAsignacionNuevasMedidas(false);
22
+ setError(false)
23
+ }}
24
+ value={clienteBusqueda}
25
+ />
26
+ <button
27
+ className="rounded-md w-1/4 bg-slate-700 hover:bg-slate-800 text-white font-semibold px-2 text-center"
28
+ onClick={handleButtonBuscarCliente}
29
+ >
30
+ Buscar
31
+ </button>
32
+ </li>
33
+ );
34
+ };
35
+
36
+ export default BarraBusquedaCliente;
src/components/CarritoItem.jsx ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState } from "react";
2
+ import { formatearStringUrlImagen } from "../lib/funciones";
3
+
4
+ export const CarritoItem = ({
5
+ carritoItem,
6
+ setCarrito,
7
+ precioTotal,
8
+ setPrecioTotal,
9
+ carrito,
10
+ }) => {
11
+ const imagen = carritoItem["montura"]["imagen"];
12
+
13
+ const { src, alt, border } = formatearStringUrlImagen(imagen);
14
+
15
+ const [cantidad, setCantidad] = useState(carritoItem["cantidad"]);
16
+
17
+ return (
18
+ <div className="w-full p-10 flex justify-between items-center border-b border-black">
19
+ <div className="">
20
+ <img width={200} src={src} alt={alt} border={border} />
21
+ </div>
22
+ <div className="block w-[350px]">
23
+ <p className="truncate uppercase text-xl font-semibold">
24
+ {carritoItem["montura"]["nombre_montura"]}
25
+ </p>
26
+ <p className="mb-3 text-gray-500">{carritoItem["montura"]["marca"]}</p>
27
+ <p>
28
+ Material:{" "}
29
+ <span className="uppercase">
30
+ {carritoItem["montura"]["material"]}
31
+ </span>
32
+ </p>
33
+ <p>
34
+ Color:{" "}
35
+ <span className="uppercase">{carritoItem["montura"]["color"]}</span>
36
+ </p>
37
+ </div>
38
+ <div className="w-[80px] text-center">
39
+ S/.{carritoItem["monturaInventario"]["precio_unit"]} c/u
40
+ </div>
41
+ <div className="flex">
42
+ <button
43
+ className="bg-gray-200 h-8 w-8 text-lg hover:bg-gray-300 disabled:cursor-default disabled:text-gray-400 disabled:hover:bg-gray-200"
44
+ onClick={() => {
45
+ // falta eliminar
46
+ carritoItem["cantidad"] = cantidad - 1;
47
+ setCantidad(cantidad - 1);
48
+ console.log(carritoItem);
49
+ setPrecioTotal(
50
+ carrito.reduce(
51
+ (total, carritoItem) =>
52
+ total +
53
+ carritoItem["cantidad"] *
54
+ carritoItem["monturaInventario"]["precio_unit"],
55
+ 0
56
+ )
57
+ );
58
+ }}
59
+ disabled={cantidad === 1}
60
+ >
61
+ -
62
+ </button>
63
+ <p className="mx-2 h-8 w-10 text-center">{cantidad}</p>
64
+ <button
65
+ className="bg-gray-200 h-8 w-8 text-lg hover:bg-gray-300 disabled:cursor-default disabled:text-gray-400 disabled:hover:bg-gray-200"
66
+ onClick={() => {
67
+ // falta eliminar
68
+ carritoItem["cantidad"] = cantidad + 1;
69
+ setCantidad(cantidad + 1);
70
+ console.log(carritoItem);
71
+ setPrecioTotal(
72
+ carrito.reduce(
73
+ (total, carritoItem) =>
74
+ total +
75
+ carritoItem["cantidad"] *
76
+ carritoItem["monturaInventario"]["precio_unit"],
77
+ 0
78
+ )
79
+ );
80
+ }}
81
+ disabled={cantidad === carritoItem["monturaInventario"]["stock"]}
82
+ >
83
+ +
84
+ </button>
85
+ </div>
86
+ <div className="w-[80px] text-center text-sm font-semibold">
87
+ S/.{" "}
88
+ <span className="text-2xl">
89
+ {cantidad * carritoItem["monturaInventario"]["precio_unit"]}
90
+ </span>
91
+ </div>
92
+ <div>
93
+ <svg
94
+ xmlns="http://www.w3.org/2000/svg"
95
+ className="icon icon-tabler icon-tabler-trash cursor-pointer"
96
+ width="30"
97
+ height="30"
98
+ viewBox="0 0 24 24"
99
+ strokeWidth="1"
100
+ stroke="#ff0000"
101
+ fill="none"
102
+ strokeLinecap="round"
103
+ strokeLinejoin="round"
104
+ onClick={() => {
105
+ setCarrito(carrito.filter((carritoItemFiltrado) => carritoItemFiltrado["monturaInventario"]["codigo"] !== carritoItem["monturaInventario"]["codigo"]));
106
+ setPrecioTotal(precioTotal - (carritoItem["cantidad"]*carritoItem["monturaInventario"]["precio_unit"]))
107
+ }}
108
+ >
109
+ <path stroke="none" d="M0 0h24v24H0z" fill="none" />
110
+ <path d="M4 7l16 0" />
111
+ <path d="M10 11l0 6" />
112
+ <path d="M14 11l0 6" />
113
+ <path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12" />
114
+ <path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3" />
115
+ </svg>
116
+ </div>
117
+ </div>
118
+ );
119
+ };
src/components/CarritoItemLunas.jsx ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react'
2
+ // cantidad 1 prescripcion[0]["detalle_lunas"] input precio precio*cantidad
3
+ export const CarritoItemLunas = ({ prescripcion, precioLunas, setPrecioLunas, setPrecioTotal, precioTotal }) => {
4
+ return (
5
+ <div className="w-full p-10 flex justify-between items-center border-b border-black">
6
+ <div className="">
7
+ <img width={200} src="../src/assets/lunas.jpg" alt="lunas" border="0" />
8
+ </div>
9
+ <div className="block w-[350px]">
10
+ <p className="truncate text-xl ">
11
+ Detalle de lunas: <span className='font-semibold uppercase'>{ prescripcion.length !== 0 && prescripcion[0]["detalle_lunas"] }</span>
12
+ </p>
13
+ </div>
14
+
15
+ <div className="flex">
16
+ <button
17
+ className="bg-gray-200 h-8 w-8 text-lg hover:bg-gray-300 disabled:cursor-default disabled:text-gray-400 disabled:hover:bg-gray-200"
18
+ onClick={() => {
19
+
20
+ }}
21
+ disabled={true}
22
+ >
23
+ -
24
+ </button>
25
+ <p className="mx-2 h-8 w-10 text-center">{1}</p>
26
+ <button
27
+ className="bg-gray-200 h-8 w-8 text-lg hover:bg-gray-300 disabled:cursor-default disabled:text-gray-400 disabled:hover:bg-gray-200"
28
+ onClick={() => {
29
+
30
+ }}
31
+ disabled={true}
32
+ >
33
+ +
34
+ </button>
35
+ </div>
36
+ <div className="w-[80px] flex items-end">
37
+ <p className='text-sm'>S/.</p>
38
+ <input className='text-2xl border w-20 text-center ml-2' type="text" placeholder='00.00' value={precioLunas} onChange={e => {
39
+ setPrecioLunas(e.target.value)
40
+ //setPrecioTotal(pre)
41
+ }} />
42
+
43
+
44
+ </div>
45
+
46
+ </div>
47
+ )
48
+ }
src/components/CatalogoMonturaCard.jsx ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { formatearStringUrlImagen } from "../lib/funciones";
2
+
3
+ const CatalogoMonturaCard = ({
4
+ montura = {
5
+ "id_montura": 10,
6
+ "nombre_montura": "Nombre monturaaaaaaaaaaaaaaaaaaa",
7
+ "imagen":
8
+ '<img src="https://i.ibb.co/jHWcz8B/EBJ8-NK3-Ko81-301-C5-Frontal.png" alt="EBJ8-NK3-Ko81-301-C5-Frontal" border="0">',
9
+ "marca": "Marca montura",
10
+ "color": "Color montura",
11
+ "material": "Material Montura",
12
+ },
13
+ monturaInventario = {
14
+ "id_montura_inventario": 10,
15
+ "id_montura": 8,
16
+ "precio_unit": 90,
17
+ "stock": 20,
18
+ "codigo": "montura8",
19
+ },
20
+ setModalVerCarrito,
21
+ setMontura,
22
+ setMonturaInventario,
23
+ setCarrito,
24
+ carrito,
25
+ setPrecioTotal
26
+ }) => {
27
+ const { src, alt, border } = formatearStringUrlImagen(montura["imagen"]);
28
+
29
+ return (
30
+ <div className="p-5 hover:bg-slate-50 w-[360px]">
31
+ <div className="w-30 h-[150px] p-5">
32
+ <img src={src} alt={alt} border={border} className=" mb-5" />
33
+ </div>
34
+ <div className="flex flex-col items-center">
35
+ <h3 className="text-center px-6 w-full uppercase text-xl font-semibold truncate ">{montura["nombre_montura"]}</h3>
36
+ <div className="flex flex-row gap-2 items-center">
37
+ <p className="text-sm ">S/.
38
+ <span className="text-2xl">
39
+ {monturaInventario["precio_unit"]}
40
+ </span>
41
+ </p>
42
+ <div className="hover:bg-slate-300 rounded-full p-2">
43
+ <svg
44
+ xmlns="http://www.w3.org/2000/svg"
45
+ className="icon icon-tabler icon-tabler-shopping-cart-plus"
46
+ width="30"
47
+ height="30"
48
+ viewBox="0 0 24 24"
49
+ strokeWidth="1.5"
50
+ stroke="#000000"
51
+ fill="none"
52
+ strokeLinecap="round"
53
+ strokeLinejoin="round"
54
+ onClick={(e) => {
55
+ e.preventDefault();
56
+ setMontura(montura);
57
+ setMonturaInventario(monturaInventario);
58
+ setModalVerCarrito(true)
59
+ setCarrito([...carrito, {
60
+ "montura":montura,
61
+ "monturaInventario":monturaInventario,
62
+ "cantidad":1,
63
+ }])
64
+
65
+ }}
66
+ >
67
+ <path stroke="none" d="M0 0h24v24H0z" fill="none" />
68
+ <path d="M4 19a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" />
69
+ <path d="M12.5 17h-6.5v-14h-2" />
70
+ <path d="M6 5l14 1l-.86 6.017m-2.64 .983h-10.5" />
71
+ <path d="M16 19h6" />
72
+ <path d="M19 16v6" />
73
+ </svg>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ );
79
+ };
80
+
81
+ export default CatalogoMonturaCard;
src/components/DatosCliente.jsx ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from "react";
2
+
3
+ const DatosCliente = ({
4
+ cliente,
5
+ setNotaAdicional,
6
+ notaAdicional,
7
+ }) => {
8
+ return (
9
+ <div className="w-full">
10
+ <p className=" font-semibold text-4xl mb-8">
11
+ {cliente["nombres_y_apellidos"]}
12
+ </p>
13
+ <div className="flex flex-row">
14
+ <p className="w-1/2 mb-2">Edad: {cliente["edad"]}</p>
15
+ <p className="w-1/2 mb-2">Teléfono: {cliente["telefono"]}</p>
16
+ </div>
17
+ <p className="mb-2"> Dirección: {cliente["direccion"]}</p>
18
+ <label htmlFor="notaAdicional">Nota adicional: {""}</label>
19
+ <textarea
20
+ className="mt-2 border w-full py-2 px-3 rounded-md"
21
+ id="notaAdicional"
22
+ type="text"
23
+ placeholder="Detalle de lunas"
24
+ onChange={(e) => setNotaAdicional(e.target.value)}
25
+ value={notaAdicional}
26
+ ></textarea>
27
+ </div>
28
+ );
29
+ };
30
+
31
+ export default DatosCliente;
src/components/DetalleClienteModalPrescripcion.jsx ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from "react";
2
+
3
+ const DetalleClienteModalPrescripcion = ({ cliente, notaAdicional }) => {
4
+ return (
5
+ (cliente && Object.keys(cliente).length !== 0) > 0 && (
6
+ <div className="w-full h-full mr-5 flex flex-row justify-center">
7
+ <div className="flex flex-col w-full">
8
+ <p className=" mb-2">Edad: {cliente["edad"]}</p>
9
+ <p className=" mb-2">Teléfono: {cliente["telefono"]}</p>
10
+ </div>
11
+ <div className="flex flex-col w-full">
12
+ <p className=" mb-2"> Dirección: {cliente["direccion"]}</p>
13
+ <p className=" mb-2"> Nota adicional: {notaAdicional}</p>
14
+ </div>
15
+ </div>
16
+ )
17
+ );
18
+ };
19
+
20
+ export default DetalleClienteModalPrescripcion;
src/components/DetalleTablaMedidas.jsx ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from "react";
2
+
3
+ const DetalleTablaMedidas = ({ medidas }) => {
4
+ return (
5
+ (medidas && medidas.length > 0) && (
6
+ <div className=" flex flex-col gap-5 w-[462.3px] items-center justify-center px-5 mt-5">
7
+ <div className=" flex flex-col items-center justify-center">
8
+ <h3 className="font-semibold text-gray-500 mb-2 uppercase ">Lejos</h3>
9
+ <div className="w-full flex items-end mb-8">
10
+ <div className="">
11
+ <p className="h-[34.81px] pr-2 font-semibold text-gray-500 text-center">
12
+ OD
13
+ </p>
14
+ <p className="h-[34.81px] pr-2 font-semibold text-gray-500 text-center">
15
+ OI
16
+ </p>
17
+ </div>
18
+ <table className="rounded-lg text-center border-2 table-fixed w-full border-separate lg:border-collapse h-[6.5rem]">
19
+ <thead className="border-2">
20
+ <tr>
21
+ <th className="border">Esfera</th>
22
+ <th className="border">Cilindro</th>
23
+ <th className="border">Eje</th>
24
+ <th className="border">A/Y</th>
25
+ </tr>
26
+ </thead>
27
+ <tbody>
28
+ <tr>
29
+ <td className="border">{medidas[0]["Esfera_OD_lejos"]}</td>
30
+ <td className="border">{medidas[0]["Cilindro_OD_lejos"]}</td>
31
+ <td className="border">{medidas[0]["Eje_OD_lejos"]}</td>
32
+ <td className="border">
33
+ {medidas[0]["Agudeza_visual_OD_lejos"]}
34
+ </td>
35
+ </tr>
36
+ <tr>
37
+ <td className="border">{medidas[0]["Esfera_OI_lejos"]}</td>
38
+ <td className="border">{medidas[0]["Cilindro_OI_lejos"]}</td>
39
+ <td className="border">{medidas[0]["Eje_OI_lejos"]}</td>
40
+ <td className="border">
41
+ {medidas[0]["Agudeza_visual_OI_lejos"]}
42
+ </td>
43
+ </tr>
44
+ </tbody>
45
+ </table>
46
+ </div>
47
+ </div>
48
+ <div className=" flex flex-col items-center justify-center">
49
+ <h3 className="font-semibold text-gray-500 mb-2 uppercase ">Cerca</h3>
50
+ <div className="w-full flex items-end mb-8">
51
+ <div className="">
52
+ <p className="h-[34.81px] pr-2 font-semibold text-gray-500 text-center">
53
+ OD
54
+ </p>
55
+ <p className="h-[34.81px] pr-2 font-semibold text-gray-500 text-center">
56
+ OI
57
+ </p>
58
+ </div>
59
+ <table className="w-full rounded-lg bg-white text-center border-2 table-fixed border-separate lg:border-collapse h-[6.5rem]">
60
+ <thead className="border-2">
61
+ <tr>
62
+ <th className="border">Esfera</th>
63
+ <th className="border">Cilindro</th>
64
+ <th className="border">Eje</th>
65
+ <th className="border">A/Y</th>
66
+ </tr>
67
+ </thead>
68
+ <tbody>
69
+ <tr>
70
+ <td className="border">{medidas[0]["Esfera_OD_cerca"]}</td>
71
+ <td className="border">{medidas[0]["Cilindro_OD_cerca"]}</td>
72
+ <td className="border">{medidas[0]["Eje_OD_cerca"]}</td>
73
+ <td className="border">
74
+ {medidas[0]["Agudeza_visual_OD_cerca"]}
75
+ </td>
76
+ </tr>
77
+ <tr>
78
+ <td className="border">{medidas[0]["Esfera_OI_cerca"]}</td>
79
+ <td className="border">{medidas[0]["Cilindro_OI_cerca"]}</td>
80
+ <td className="border">{medidas[0]["Eje_OI_cerca"]}</td>
81
+ <td className="border">
82
+ {medidas[0]["Agudeza_visual_OI_cerca"]}
83
+ </td>
84
+ </tr>
85
+ </tbody>
86
+ </table>
87
+ </div>
88
+ </div>
89
+ </div>
90
+ )
91
+ );
92
+ };
93
+
94
+ export default DetalleTablaMedidas;
src/components/DropdownClientesItem.jsx ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from "react";
2
+
3
+ const DropdownClientesItem = ({ cli, setBtnBuscarClick, setCliente, setClienteBusqueda, setNotaAdicional }) => {
4
+ return (
5
+ <li
6
+ key={cli["id_cliente"]}
7
+ onClick={() => {
8
+ setBtnBuscarClick(false);
9
+ setClienteBusqueda("");
10
+ setCliente(cli);
11
+ setNotaAdicional("");
12
+ }}
13
+ className="cursor-pointer hover:font-semibold hover:bg-slate-50 p-2 "
14
+ >
15
+ <p>{cli["nombres_y_apellidos"]}</p>
16
+ <p className="text-sm text-slate-400">{cli["edad"]}</p>
17
+ <p className="text-sm text-slate-400">{cli["telefono"]}</p>
18
+ <p className="text-sm text-slate-400">{cli["direccion"]}</p>
19
+ </li>
20
+ );
21
+ };
22
+
23
+ export default DropdownClientesItem;
src/components/ErrorMessage.jsx ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ const ErrorMessage = ({ mensaje }) => {
3
+ return (
4
+ <div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative my-2 mr-5" role="alert ">
5
+ <strong className="font-bold">Error: </strong>
6
+ <span className="block sm:inline">{mensaje}</span>
7
+ </div>
8
+ );
9
+ };
10
+
11
+ export default ErrorMessage;
src/components/Modal.jsx ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from "react";
2
+ import { registrarPost } from "../lib/conexionApi";
3
+
4
+ const Modal = ({
5
+ openModal,
6
+ setOpenModal,
7
+ nombresYApellidos,
8
+ setNombresYApellidos,
9
+ edad,
10
+ setEdad,
11
+ telefono,
12
+ setTelefono,
13
+ direccion,
14
+ setDireccion,
15
+ setError,
16
+ setClienteBusqueda
17
+ }) => {
18
+ return (
19
+ <div className="fixed inset-0 bg-black bg-opacity-25 backdrop-blur-sm flex justify-center items-center">
20
+ <div className="bg-white p-5 w-1/3 shadow-md rounded-lg">
21
+ <form>
22
+ <h3 className="text-center text-3xl text-bold mb-8">
23
+ Agregar nuevo cliente
24
+ </h3>
25
+ <div className="">
26
+ <label htmlFor="nombresYApellidos" className="block">
27
+ Nombres y apellidos
28
+ </label>
29
+ <input
30
+ type="text"
31
+ id="nombresYApellidos"
32
+ className="block w-full border p-2 rounded-md"
33
+ placeholder="Ej. Juan Perez"
34
+ onChange={(e) => setNombresYApellidos(e.target.value)}
35
+ value={nombresYApellidos}
36
+ />
37
+ </div>
38
+ <div className="flex flex-row gap-3 my-3">
39
+ <div className="w-1/2">
40
+ <label htmlFor="edad" className="block">
41
+ Edad
42
+ </label>
43
+ <input
44
+ type="number"
45
+ id="edad"
46
+ className="block w-full border p-2 rounded-md"
47
+ placeholder="Ej. 40"
48
+ onChange={(e) => setEdad(e.target.value)}
49
+ value={edad}
50
+ />
51
+ </div>
52
+ <div className="w-1/2">
53
+ <label htmlFor="telefono" className="">
54
+ Telefono
55
+ </label>
56
+ <input
57
+ type="text"
58
+ id="telefono"
59
+ className="block w-full border p-2 rounded-md"
60
+ placeholder="Ej. 999888777"
61
+ onChange={(e) => setTelefono(e.target.value)}
62
+ value={telefono}
63
+ />
64
+ </div>
65
+ </div>
66
+ <div>
67
+ <label htmlFor="direccion" className="">
68
+ Direccion
69
+ </label>
70
+ <input
71
+ type="text"
72
+ id="direccion"
73
+ className="block w-full border p-2 rounded-md"
74
+ placeholder="Ej. Calle 123"
75
+ onChange={(e) => setDireccion(e.target.value)}
76
+ value={direccion}
77
+ />
78
+ </div>
79
+ <div>
80
+ <div>
81
+ <button
82
+ type="submit"
83
+ className="cursor-pointer py-2 w-full bg-slate-700 hover:bg-slate-800 text-white font-semibold text-center rounded-md mt-5"
84
+ onClick={(e) => {
85
+ e.preventDefault();
86
+ const nuevoCliente = {
87
+ nombres_y_apellidos: nombresYApellidos,
88
+ edad: edad,
89
+ telefono: telefono,
90
+ direccion: direccion,
91
+ };
92
+ registrarPost("clientes", nuevoCliente);
93
+ setOpenModal(false);
94
+ setNombresYApellidos("");
95
+ setEdad("");
96
+ setTelefono("");
97
+ setDireccion("");
98
+ setError(false);
99
+ setClienteBusqueda("");
100
+ }}
101
+ >
102
+ Agregar cliente
103
+ </button>
104
+ </div>
105
+ <div>
106
+ <button
107
+ className="cursor-pointer py-2 w-full bg-slate-300 hover:bg-slate-400 text-gray-700 font-semibold text-center rounded-md mt-2"
108
+ onClick={(e) => {
109
+ e.preventDefault();
110
+ setOpenModal(false);
111
+ setNombresYApellidos("");
112
+ setEdad("");
113
+ setTelefono("");
114
+ setDireccion("");
115
+ setClienteBusqueda("");
116
+ }}
117
+ >
118
+ Cancelar
119
+ </button>
120
+ </div>
121
+ </div>
122
+ </form>
123
+ </div>
124
+ </div>
125
+ );
126
+ };
127
+
128
+ export default Modal;
src/components/ModalBoleta.jsx ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useEffect, useState } from "react";
2
+ import { obtenerEspecifico, obtenerGet, procesarBoleta, registrarPost } from "../lib/conexionApi";
3
+ import { Navigate } from "react-router";
4
+
5
+ const ModalBoleta = ({
6
+ fecha,
7
+ setModalBoleta,
8
+ precioTotal,
9
+ carrito,
10
+ prescripcion,
11
+ adelanto,
12
+ precioLunas,
13
+ setPrescripcion,
14
+ setPrecioTotal,
15
+ setCarrito
16
+ }) => {
17
+
18
+ const [clienteBoleta, setClienteBoleta] = useState({})
19
+ const [errorClienteBoleta, setErrorClienteBoleta] = useState(false)
20
+ const [boleta, setBoleta] = useState({})
21
+ const [errorBoleta, setErrorBoleta] = useState(false)
22
+
23
+ const [boletaEmitida, setBoletaEmitida] = useState(false)
24
+
25
+ useEffect(() => {
26
+ obtenerEspecifico("clientes/prescripcion", { "id_prescripcion": prescripcion[0]["id_prescripcion"] }, setClienteBoleta, setErrorClienteBoleta)
27
+ }, [])
28
+
29
+ return (
30
+ <div className="fixed inset-0 bg-black bg-opacity-25 backdrop-blur-sm flex justify-center items-center">
31
+ <div className="bg-white p-10 shadow-md rounded-lg w-[50rem]">
32
+ <div className=" flex flex-col w-full h-full ">
33
+ <div className="flex flex-col w-full h-full p-5 px-10 mb-5 border-2 pt-5 rounded-lg">
34
+ <p className="text-4xl font-bold mb-2">Arte Visual</p>
35
+ <div className="flex">
36
+ <div className="w-2/3">
37
+ <p>Av. Los Héroes 632 S. J. M</p>
38
+ <div className="flex gap-1 mb-5">
39
+ <svg
40
+ xmlns="http://www.w3.org/2000/svg"
41
+ className="icon icon-tabler icon-tabler-brand-whatsapp"
42
+ width="25"
43
+ height="25"
44
+ viewBox="0 0 24 24"
45
+ strokeWidth="1"
46
+ stroke="#000000"
47
+ fill="none"
48
+ strokeLinecap="round"
49
+ strokeLinejoin="round"
50
+ >
51
+ <path stroke="none" d="M0 0h24v24H0z" fill="none" />
52
+ <path d="M3 21l1.65 -3.8a9 9 0 1 1 3.4 2.9l-5.05 .9" />
53
+ <path d="M9 10a.5 .5 0 0 0 1 0v-1a.5 .5 0 0 0 -1 0v1a5 5 0 0 0 5 5h1a.5 .5 0 0 0 0 -1h-1a.5 .5 0 0 0 0 1" />
54
+ </svg>
55
+ <p>902 501 054/968 600 415</p>
56
+ </div>
57
+ <p>Señor(a): {clienteBoleta["nombres_y_apellidos"]}</p>
58
+ <p>Dirección: {clienteBoleta["direccion"]}</p>
59
+ <p>Teléfono: {clienteBoleta["telefono"]}</p>
60
+ </div>
61
+ <div className="w-1/3 text-right">
62
+ <p className="uppercase">NOTA DE PEDIDO</p>
63
+ <p>00000{prescripcion[0]["id_prescripcion"]}</p>
64
+ <p>Fecha: {fecha}</p>
65
+ </div>
66
+ </div>
67
+ <p className="mt-8">Tabla de productos</p>
68
+ <div className="flex justify-center items-center w-full my-5">
69
+ <table className="rounded-lg bg-white text-center border-2 table-fixed w-full border-separate lg:border-collapse">
70
+ <thead className="border-2">
71
+ <tr>
72
+ <th className="border w-[7rem] font-semibold">Cantidad</th>
73
+ <th className="border font-semibold">Descripción</th>
74
+ <th className="border w-[8rem] font-semibold">Precio Unit.</th>
75
+ <th className="border w-[8rem] font-semibold">Importe</th>
76
+ </tr>
77
+ </thead>
78
+ <tbody>
79
+ {carrito.map(item => {
80
+ return (
81
+ <tr key={item["monturaInventario"]["codigo"]}>
82
+ <td className="border">{item["cantidad"]}</td>
83
+ <td className="border">{item["montura"]["nombre_montura"]}</td>
84
+ <td className="border">S/.{item["monturaInventario"]["precio_unit"]}</td>
85
+ <td className="border">S/.{item["monturaInventario"]["precio_unit"]*item["cantidad"]}</td>
86
+ </tr>
87
+ )
88
+ })}
89
+ <tr>
90
+ <td className="border">{1}</td>
91
+ <td className="border">detalle de lunas: {prescripcion[0]["detalle_lunas"]}</td>
92
+ <td className="border">S/.{parseFloat(precioLunas)}</td>
93
+ <td className="border">S/.{1*parseFloat(precioLunas)}</td>
94
+ </tr>
95
+ </tbody>
96
+ </table>
97
+ </div>
98
+ <div className="flex justify-between">
99
+ <p>Adelanto: S/.{adelanto}</p>
100
+ <p>Saldo: S/.{precioTotal-adelanto}</p>
101
+ <p>Total: S/.{precioTotal}</p>
102
+ </div>
103
+ </div>
104
+ <h3 className="text-center text-2xl text-bold mb-8">
105
+ ¿Emitir boleta?
106
+ </h3>
107
+ <div className="flex gap-5">
108
+ <button
109
+ className="cursor-pointer py-2 w-1/2 bg-slate-300 hover:bg-slate-400 text-gray-700 font-semibold text-center rounded-md mt-2"
110
+ onClick={(e) => {
111
+ setModalBoleta(false)
112
+ }}
113
+ >
114
+ No
115
+ </button>
116
+
117
+ <button
118
+ type="submit"
119
+ className="w-1/2 mt-2 cursor-pointer py-2 bg-slate-700 hover:bg-slate-800 text-white font-semibold text-center rounded-md "
120
+ onClick={async (e) => {
121
+ const finalizarEmisionBoleta = async () => {
122
+ await procesarBoleta(precioTotal, carrito, adelanto, prescripcion[0]["id_prescripcion"], prescripcion[0]["detalle_lunas"], precioLunas );
123
+ // reiniciar precioTotal carrito, prescripcion,
124
+ await setPrecioTotal(0)
125
+ await setCarrito([])
126
+ await setPrescripcion([])
127
+
128
+ }
129
+ finalizarEmisionBoleta()
130
+ setBoletaEmitida(true)
131
+ }}
132
+ >
133
+
134
+ </button>
135
+ </div>
136
+ </div>
137
+ </div>
138
+ {boletaEmitida && (
139
+ <Navigate to="/" replace={true}/>
140
+ )}
141
+ </div>
142
+ );
143
+ };
144
+
145
+ export default ModalBoleta;
src/components/ModalPrescripcion.jsx ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from "react";
2
+ import DetalleTablaMedidas from "./DetalleTablaMedidas";
3
+ import DetalleClienteModalPrescripcion from "./DetalleClienteModalPrescripcion";
4
+ import { registrarPost } from "../lib/conexionApi";
5
+
6
+ const ModalPrescripcion = ({
7
+ setOpenModalPrescripcion,
8
+ setPrescripcionLista,
9
+ cliente,
10
+ medidas,
11
+ fechaFormateada,
12
+ notaAdicional,
13
+ setActualizacionMedidas,
14
+ setAsignacionNuevasMedidas,
15
+ setPrescripcionRegistrada
16
+ }) => {
17
+ return (
18
+ <div className="fixed inset-0 bg-black bg-opacity-25 backdrop-blur-sm flex justify-center items-center">
19
+ <div className="bg-white p-10 shadow-md rounded-lg">
20
+ <form className=" flex flex-col w-full h-full ">
21
+ <div className="flex flex-col w-full h-full p-5 px-10 mb-5 border-2 pt-5 rounded-lg ">
22
+ <div className="flex flex-row justify-between text-gray-500">
23
+ <p className="italic text-left w-ful mb-5">Prescripción</p>
24
+ <p className="text-right w-ful mb-5">{fechaFormateada}</p>
25
+ </div>
26
+ <p className="font-semibold text-2xl mb-8 text-center">
27
+ {cliente["nombres_y_apellidos"]}
28
+ </p>
29
+ <div className="flex flex-col">
30
+ <div className="">
31
+ <DetalleClienteModalPrescripcion
32
+ cliente={cliente}
33
+ notaAdicional={notaAdicional}
34
+ />
35
+ </div>
36
+ <div>
37
+ <DetalleTablaMedidas medidas={medidas} />
38
+ </div>
39
+ </div>
40
+ </div>
41
+ <h3 className="text-center text-2xl text-bold mb-8">
42
+ ¿Estás seguro que deseas continuar?
43
+ </h3>
44
+ <div className="flex gap-5">
45
+ <button
46
+ className="cursor-pointer py-2 w-1/2 bg-slate-300 hover:bg-slate-400 text-gray-700 font-semibold text-center rounded-md mt-2"
47
+ onClick={(e) => {
48
+ e.preventDefault();
49
+ console.log("No");
50
+ setOpenModalPrescripcion(false);
51
+ setActualizacionMedidas(false)
52
+ setAsignacionNuevasMedidas(false)
53
+ setPrescripcionLista(false);
54
+ }}
55
+ >
56
+ No
57
+ </button>
58
+
59
+ <button
60
+ type="submit"
61
+ className="w-1/2 mt-2 cursor-pointer py-2 bg-slate-700 hover:bg-slate-800 text-white font-semibold text-center rounded-md "
62
+ onClick={(e) => {
63
+ e.preventDefault();
64
+ const nuevaPrescripcion = {
65
+ "id_medidas": medidas[0]["id_medidas"],
66
+ "detalle_lunas": notaAdicional,
67
+ "fecha": fechaFormateada,
68
+ };
69
+
70
+ registrarPost('prescripciones', nuevaPrescripcion)
71
+ setPrescripcionRegistrada(true)
72
+ }}
73
+ >
74
+
75
+ </button>
76
+ </div>
77
+ </form>
78
+ </div>
79
+ </div>
80
+ );
81
+ };
82
+
83
+ export default ModalPrescripcion;
src/components/ModalVerCarrito.jsx ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from "react";
2
+ import { formatearStringUrlImagen } from "../lib/funciones";
3
+
4
+ const ModalVerCarrito = ({
5
+ montura = {
6
+ "id_montura": 10,
7
+ "nombre_montura": "Nombre monturaaaaaaaaaaaaaaaaaaa",
8
+ "imagen":
9
+ '<img src="https://i.ibb.co/jHWcz8B/EBJ8-NK3-Ko81-301-C5-Frontal.png" alt="EBJ8-NK3-Ko81-301-C5-Frontal" border="0">',
10
+ "marca": "Marca montura",
11
+ "color": "Color montura",
12
+ "material": "Material Montura",
13
+ },
14
+ monturaInventario = {
15
+ "id_montura_inventario": 10,
16
+ "id_montura": 8,
17
+ "precio_unit": 90,
18
+ "stock": 20,
19
+ "codigo": "montura8",
20
+ },
21
+ setModalVerCarrito,
22
+ carrito,
23
+ setMontura,
24
+ setMonturaInventario,
25
+ setIrAlCarrito
26
+ }) => {
27
+ const { src, alt, border } = formatearStringUrlImagen(montura["imagen"]);
28
+
29
+ return (
30
+ <div className="fixed inset-0 bg-black bg-opacity-25 backdrop-blur-sm flex justify-center items-center">
31
+ <div className="bg-white px-10 py-5 w-[790.5px] shadow-md rounded-lg ">
32
+ <div className=" flex items-center justify-between border-b-2 border-black pb-2">
33
+ <p className="uppercase">Recién añadido a tu carrito de compra</p>
34
+ <svg
35
+ xmlns="http://www.w3.org/2000/svg"
36
+ className="icon icon-tabler icon-tabler-x w-10 h-10 rounded-full cursor-pointer p-1"
37
+ width="25"
38
+ height="25"
39
+ viewBox="0 0 24 24"
40
+ strokeWidth="2"
41
+ stroke="#000000"
42
+ fill="none"
43
+ strokeLinecap="round"
44
+ strokeLinejoin="round"
45
+ onClick={() => {
46
+ setMonturaInventario({})
47
+ setMontura({})
48
+ setModalVerCarrito(false)
49
+ }}
50
+ >
51
+ <path stroke="none" d="M0 0h24v24H0z" fill="none" />
52
+ <path d="M18 6l-12 12" />
53
+ <path d="M6 6l12 12" />
54
+ </svg>
55
+ </div>
56
+ <div className="flex w-full my-10 ">
57
+ <div className="w-1/2">
58
+ <img src={src} alt={alt} border={border} />
59
+ </div>
60
+ <div className="w-1/2">
61
+ <div className="flex flex-col gap-2">
62
+ <p className="w-full text-xl truncate uppercase">
63
+ {montura["nombre_montura"]}
64
+ </p>
65
+ <p className="text-xl uppercase">{montura["marca"]}</p>
66
+ <p className="mt-3">
67
+ Color: <span className="uppercase">{montura["color"]}</span>
68
+ </p>
69
+ <p className="">
70
+ Material:{" "}
71
+ <span className="uppercase">{montura["material"]}</span>
72
+ </p>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ <button className="w-full text-xl py-4 uppercase border-2 cursor-pointer border-black hover:bg-gray-300" onClick={() => setIrAlCarrito(true)} >
77
+ Ver Carrito ({carrito.length})
78
+ </button>
79
+ </div>
80
+ </div>
81
+ );
82
+ };
83
+
84
+ export default ModalVerCarrito;
src/components/TablaMedidas.jsx ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react'
2
+
3
+ const TablaMedidas = ({
4
+ esferaODlejos, setEsferaODLejos,
5
+ cilindroODlejos, setCilindroODlejos,
6
+ ejeODlejos, setEjeODlejos,
7
+ agudezavisualODlejos, setAgudezavisualODlejos,
8
+ esferaOIlejos, setEsferaOIlejos,
9
+ cilindroOIlejos, setCilindroOIlejos,
10
+ ejeOIlejos, setEjeOIlejos,
11
+ agudezavisualOIlejos, setAgudezavisualOIlejos,
12
+ esferaODcerca, setEsferaODcerca,
13
+ cilindroODcerca, setCilindroODcerca,
14
+ ejeODcerca, setEjeODcerca,
15
+ agudezavisualODcerca, setAgudezavisualODcerca,
16
+ esferaOIcerca, setEsferaOIcerca,
17
+ cilindroOIcerca, setCilindroOIcerca,
18
+ ejeOIcerca, setEjeOIcerca,
19
+ agudezavisualOIcerca, setAgudezavisualOIcerca
20
+ }) => {
21
+ return (
22
+ <div className=" flex flex-col items-center justify-center mt-5 px-5">
23
+ <h3 className="font-semibold text-gray-500 mb-2 uppercase ">
24
+ Lejos
25
+ </h3>
26
+ <div className="w-full flex items-end mb-8">
27
+ <div className="">
28
+ <p className="h-[34.81px] pr-2 font-semibold text-gray-500 text-center">
29
+ OD
30
+ </p>
31
+ <p className="h-[34.81px] pr-2 font-semibold text-gray-500 text-center">
32
+ OI
33
+ </p>
34
+ </div>
35
+ <table className="rounded-lg bg-white text-center border-2 table-fixed border-separate lg:border-collapse h-[6.5rem]">
36
+ <thead className="border-2">
37
+ <tr>
38
+ <th className="border">Esfera</th>
39
+ <th className="border">Cilindro</th>
40
+ <th className="border">Eje</th>
41
+ <th className="border">A/Y</th>
42
+ </tr>
43
+ </thead>
44
+ <tbody>
45
+ <tr>
46
+ <td className="border">
47
+ <input
48
+ type="text"
49
+ className="text-center w-full h-full"
50
+ onChange={(e) =>
51
+ setEsferaODLejos(e.target.value)
52
+ }
53
+ value={esferaODlejos}
54
+ />
55
+ </td>
56
+ <td className="border">
57
+ <input
58
+ type="text"
59
+ className="text-center w-full h-full"
60
+ onChange={(e) =>
61
+ setCilindroODlejos(e.target.value)
62
+ }
63
+ value={cilindroODlejos}
64
+ />
65
+ </td>
66
+ <td className="border">
67
+ <input
68
+ type="text"
69
+ className="text-center w-full h-full"
70
+ onChange={(e) =>
71
+ setEjeODlejos(e.target.value)
72
+ }
73
+ value={ejeODlejos}
74
+ />
75
+ </td>
76
+ <td className="border">
77
+ <input
78
+ type="text"
79
+ className="text-center w-full h-full"
80
+ onChange={(e) =>
81
+ setAgudezavisualODlejos(e.target.value)
82
+ }
83
+ value={agudezavisualODlejos}
84
+ />
85
+ </td>
86
+ </tr>
87
+ <tr>
88
+ <td className="border">
89
+ <input
90
+ type="text"
91
+ className="text-center w-full h-full"
92
+ onChange={(e) =>
93
+ setEsferaOIlejos(e.target.value)
94
+ }
95
+ value={esferaOIlejos}
96
+ />
97
+ </td>
98
+ <td className="border">
99
+ <input
100
+ type="text"
101
+ className="text-center w-full h-full"
102
+ onChange={(e) =>
103
+ setCilindroOIlejos(e.target.value)
104
+ }
105
+ value={cilindroOIlejos}
106
+ />
107
+ </td>
108
+ <td className="border">
109
+ <input
110
+ type="text"
111
+ className="text-center w-full h-full"
112
+ onChange={(e) =>
113
+ setEjeOIlejos(e.target.value)
114
+ }
115
+ value={ejeOIlejos}
116
+ />
117
+ </td>
118
+ <td className="border">
119
+ <input
120
+ type="text"
121
+ className="text-center w-full h-full"
122
+ onChange={(e) =>
123
+ setAgudezavisualOIlejos(e.target.value)
124
+ }
125
+ value={agudezavisualOIlejos}
126
+ />
127
+ </td>
128
+ </tr>
129
+ </tbody>
130
+ </table>
131
+ </div>
132
+ <div className=" flex flex-col items-center justify-center mt-5">
133
+ <h3 className="font-semibold text-gray-500 mb-2 uppercase ">
134
+ Cerca
135
+ </h3>
136
+ <div className="w-full flex items-end mb-8">
137
+ <div className="">
138
+ <p className="h-[34.81px] pr-2 font-semibold text-gray-500 text-center">
139
+ OD
140
+ </p>
141
+ <p className="h-[34.81px] pr-2 font-semibold text-gray-500 text-center">
142
+ OI
143
+ </p>
144
+ </div>
145
+ <table className="rounded-lg bg-white text-center border-2 table-fixed border-separate lg:border-collapse h-[6.5rem]">
146
+ <thead className="border-2">
147
+ <tr>
148
+ <th className="border">Esfera</th>
149
+ <th className="border">Cilindro</th>
150
+ <th className="border">Eje</th>
151
+ <th className="border">A/Y</th>
152
+ </tr>
153
+ </thead>
154
+ <tbody>
155
+ <tr>
156
+ <td className="border">
157
+ <input
158
+ type="text"
159
+ className="text-center w-full h-full"
160
+ onChange={(e) =>
161
+ setEsferaODcerca(e.target.value)
162
+ }
163
+ value={esferaODcerca}
164
+ />
165
+ </td>
166
+ <td className="border">
167
+ <input
168
+ type="text"
169
+ className="text-center w-full h-full"
170
+ onChange={(e) =>
171
+ setCilindroODcerca(e.target.value)
172
+ }
173
+ value={cilindroODcerca}
174
+ />
175
+ </td>
176
+ <td className="border">
177
+ <input
178
+ type="text"
179
+ className="text-center w-full h-full"
180
+ onChange={(e) =>
181
+ setEjeODcerca(e.target.value)
182
+ }
183
+ value={ejeODcerca}
184
+ />
185
+ </td>
186
+ <td className="border">
187
+ <input
188
+ type="text"
189
+ className="text-center w-full h-full"
190
+ onChange={(e) =>
191
+ setAgudezavisualODcerca(e.target.value)
192
+ }
193
+ value={agudezavisualODcerca}
194
+ />
195
+ </td>
196
+ </tr>
197
+ <tr>
198
+ <td className="border">
199
+ <input
200
+ type="text"
201
+ className="text-center w-full h-full"
202
+ onChange={(e) =>
203
+ setEsferaOIcerca(e.target.value)
204
+ }
205
+ value={esferaOIcerca}
206
+ />
207
+ </td>
208
+ <td className="border">
209
+ <input
210
+ type="text"
211
+ className="text-center w-full h-full"
212
+ onChange={(e) =>
213
+ setCilindroOIcerca(e.target.value)
214
+ }
215
+ value={cilindroOIcerca}
216
+ />
217
+ </td>
218
+ <td className="border">
219
+ <input
220
+ type="text"
221
+ className="text-center w-full h-full"
222
+ onChange={(e) =>
223
+ setEjeOIcerca(e.target.value)
224
+ }
225
+ value={ejeOIcerca}
226
+ />
227
+ </td>
228
+ <td className="border">
229
+ <input
230
+ type="text"
231
+ className="text-center w-full h-full"
232
+ onChange={(e) =>
233
+ setAgudezavisualOIcerca(e.target.value)
234
+ }
235
+ value={agudezavisualOIcerca}
236
+ />
237
+ </td>
238
+ </tr>
239
+ </tbody>
240
+ </table>
241
+ </div>
242
+ </div>
243
+ </div>
244
+ )
245
+ }
246
+
247
+ export default TablaMedidas
src/index.css ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ #nav-ul a.active li{
6
+ background-color: rgb(255 255 255);
7
+ color: black;
8
+ font-weight: 600;
9
+ }
src/lib/conexionApi.js ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const url = import.meta.env.VITE_API_URL
2
+
3
+ export const cargarApi = async () => {
4
+ const response = await fetch(url)
5
+ if (response.ok) {
6
+ const data = await response.json()
7
+ console.log(data);
8
+ }
9
+ }
10
+
11
+ export const obtenerGet = async (endpoint, funcionSet, funcionSetError) => {
12
+ const response = await fetch(`${url}/${endpoint}`)
13
+ const data = await response.json()
14
+ if (data.error || data.length === 0 || Object.keys(data).length === 0) {
15
+ funcionSetError(true)
16
+ }
17
+ funcionSet(data)
18
+ }
19
+
20
+ export const registrarPost = async (endpoint, data) => {
21
+ const response = await fetch(`${url}/${endpoint}`, {
22
+ method: 'POST',
23
+ body: JSON.stringify(data),
24
+ headers: {
25
+ 'Content-type': 'application/json'
26
+ }
27
+ })
28
+ const res = await response.json()
29
+ return res
30
+ }
31
+
32
+ export const obtenerEspecifico = async (endpoint, dato, funcionSet, funcionSetError) => {
33
+ const response = await fetch(`${url}/${endpoint}`, {
34
+ method: 'POST',
35
+ headers: {
36
+ 'Content-type': 'application/json'
37
+ },
38
+ body: JSON.stringify(dato),
39
+ })
40
+ const data = await response.json()
41
+ funcionSet(data)
42
+ if (data.error || data.length === 0) {
43
+ funcionSetError(true)
44
+ }
45
+ }
46
+
47
+ export const actualizarPut = async (endpoint, data) => {
48
+ const response = await fetch(`${url}/${endpoint}`, {
49
+ method: 'PUT',
50
+ body: JSON.stringify(data),
51
+ headers: {
52
+ 'Content-type': 'application/json'
53
+ }
54
+ })
55
+ const res = await response.json()
56
+ return res
57
+ }
58
+
59
+ export const getEspecifico = async (endpoint, dato) => {
60
+ const response = await fetch(`${url}/${endpoint}`)
61
+ const data = await response.json()
62
+ return data
63
+ }
64
+
65
+ export const emitirPdfPrescripcion = (idPrescripcion) => {
66
+ const urlPdf = `${url}/prescripcion/pdf/${idPrescripcion}`
67
+ return urlPdf
68
+ }
69
+
70
+ export const procesarBoleta = async (precioTotal, carrito, adelanto, idprescripcion, descripcion, precioLunas) => {
71
+ const responseRegistrarBoleta = await fetch(`${url}/boletas`, {
72
+ method: 'POST',
73
+ body: JSON.stringify({
74
+ "precio_total": precioTotal,
75
+ "estado_recojo": "pendiente"
76
+ }),
77
+ headers: {
78
+ 'Content-type': 'application/json'
79
+ }
80
+ })
81
+ const resRegistrarBoleta = await responseRegistrarBoleta.json()
82
+ console.log(resRegistrarBoleta)
83
+
84
+
85
+ const boleta = await fetch(`${url}/boleta/ultima`)
86
+ const responseBoleta = await boleta.json()
87
+ console.log(responseBoleta)
88
+
89
+
90
+ carrito.forEach(async item => {
91
+ console.log(item);
92
+ const responseMonturasPedidos = await fetch(`${url}/monturas_pedidos`, {
93
+ method: 'POST',
94
+ body: JSON.stringify({
95
+ "id_montura_inventario": item["monturaInventario"]["id_montura_inventario"],
96
+ "cantidad": item["cantidad"],
97
+ "precio": item["monturaInventario"]["precio_unit"]*item["cantidad"],
98
+ "id_boleta": responseBoleta["id_boleta"]
99
+ }),
100
+ headers: {
101
+ 'Content-type': 'application/json'
102
+ }
103
+ })
104
+ const resMonturasPedidos = await responseMonturasPedidos.json()
105
+ console.log(resMonturasPedidos)
106
+ })
107
+
108
+
109
+
110
+ const lunasPedido = await fetch(`${url}/lunas_pedido`, {
111
+ method: 'POST',
112
+ body: JSON.stringify({
113
+ "id_prescripcion": idprescripcion,
114
+ "precio": parseFloat(precioLunas),
115
+ "id_boleta": responseBoleta["id_boleta"] ,
116
+ "descripcion": descripcion,
117
+ "cantidad": 1
118
+ }),
119
+ headers: {
120
+ 'Content-type': 'application/json'
121
+ }
122
+ })
123
+ const resLunasPedido = await lunasPedido.json()
124
+ console.log(resLunasPedido);
125
+
126
+ const responsePDF = await fetch(`${url}/boleta/descargarPDF/${responseBoleta["id_boleta"]}/${adelanto}`)
127
+
128
+ window.open(responsePDF.url, "_blank");
129
+ }
130
+
131
+ export const prescripcionPdff = async (idPrescripcion) => {
132
+ const responsePDF = await fetch(`${url}/prescripcion/pdf/${idPrescripcion}`)
133
+ window.open(responsePDF.url, "_blank");
134
+ }
src/lib/funciones.js ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { obtenerGet, registrarPost } from "./conexionApi";
2
+
3
+ export function formatearStringUrlImagen(stringImagen='<img src="https://i.ibb.co/TTmWTjV/df-BBqw-TYjvfrontal-tom-silver-optimania.png" alt="df-BBqw-TYjvfrontal-tom-silver-optimania" border="0">') {
4
+ // Crear un elemento temporal para parsear el string HTML
5
+ const tempElement = document.createElement('div');
6
+ tempElement.innerHTML = stringImagen;
7
+
8
+ // Obtener los atributos necesarios del elemento
9
+ const src = tempElement.querySelector('img').getAttribute('src');
10
+ const alt = tempElement.querySelector('img').getAttribute('alt');
11
+ const border = tempElement.querySelector('img').getAttribute('border');
12
+
13
+ // Crear un objeto con los valores obtenidos
14
+ const imagenObj = {
15
+ src: src,
16
+ alt: alt,
17
+ border: border
18
+ };
19
+
20
+ return imagenObj;
21
+ }
22
+
src/main.jsx ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react'
2
+ import ReactDOM from "react-dom/client";
3
+ import App from "./App.jsx";
4
+ import "./index.css";
5
+ import { BrowserRouter } from "react-router-dom";
6
+
7
+ ReactDOM.createRoot(document.getElementById("root")).render(
8
+ <React.StrictMode>
9
+ <BrowserRouter>
10
+ <App />
11
+ </BrowserRouter>
12
+ </React.StrictMode>
13
+ );
tailwind.config.js ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ /** @type {import('tailwindcss').Config} */
2
+ export default {
3
+ content: ["index.html", "./src/**/*.jsx"],
4
+ theme: {
5
+ extend: {},
6
+ },
7
+ plugins: []
8
+ }
9
+
vite.config.js ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+
4
+ // https://vitejs.dev/config/
5
+ export default defineConfig({
6
+ plugins: [react()],
7
+ })