Pull a lino

This commit is contained in:
jls846
2025-11-01 19:58:23 -06:00
8 changed files with 247 additions and 116 deletions
+12 -17
View File
@@ -1,8 +1,8 @@
"use client";
import { useState } from "react";
import "../styles/layout/login.scss"; // puedes usar los mismos estilos
import "../styles/base/globales.scss";
import "../../styles/layout/login.scss";
import "../../styles/base/globales.scss";
import Link from "next/link";
import axios from "axios";
@@ -29,23 +29,21 @@ function CrearCuenta() {
setLoading(true);
try {
const response = await axios.post(
`${process.env.NEXT_PUBLIC_API_URL}/auth/register`,
{
nombre,
correo,
contraseña: password,
}
);
// const response = await axios.post(
// `${process.env.NEXT_PUBLIC_API_URL}/auth/register`,
// {
// nombre,
// correo,
// contraseña: password,
// }
// );
toast.success("Cuenta creada correctamente");
router.push("/"); // redirige al login
} catch (err: unknown) {
if (axios.isAxiosError(err)) {
if (err.response) {
toast.error(
err.response.data?.message || "Error al crear la cuenta"
);
toast.error(err.response.data?.message || "Error al crear la cuenta");
} else if (err.request) {
toast.error("No se pudo conectar con el servidor");
} else {
@@ -57,7 +55,6 @@ function CrearCuenta() {
} finally {
setLoading(false);
}
};
return (
@@ -116,9 +113,7 @@ function CrearCuenta() {
<Link href="/">¿Ya tienes cuenta? Inicia sesión</Link>
</form>
</div>
</section>
);
}
export default CrearCuenta;
export default CrearCuenta;
+27 -11
View File
@@ -4,7 +4,8 @@ import { useState } from "react";
import axios from "axios";
import { useRouter } from "next/navigation";
import toast from "react-hot-toast";
import "../styles/base/globales.scss"; // ajusta ruta si tu proyecto la tiene en otro sitio
import "../../styles/base/globales.scss"; // ajusta ruta si tu proyecto la tiene en otro sitio
import Link from "next/link";
export default function ForgotPasswordPage() {
const [email, setEmail] = useState("");
@@ -26,27 +27,38 @@ export default function ForgotPasswordPage() {
setLoading(true);
const res = await axios.post("/api/auth/forgot-password", { email });
if (res.status === 200) {
toast.success("Si existe esa cuenta, recibirás un correo con instrucciones.");
toast.success(
"Si existe esa cuenta, recibirás un correo con instrucciones."
);
// Opcional: redirigir al login después de enviar
setTimeout(() => router.push("/"), 1200);
} else {
toast.error("Ocurrió un error. Intenta de nuevo.");
}
} catch (error:any ) {
} catch (error) {
// manejar mensajes de error desde el servidor si vienen
const msg = error?.response?.data?.message ?? "Error al enviar el correo.";
toast.error(msg);
console.log(error);
} finally {
setLoading(false);
}
};
return (
<main className="forgot-password-page" style={{ maxWidth: 520, margin: "4rem auto", padding: "1.5rem" }}>
<main
className="forgot-password-page"
style={{ maxWidth: 520, margin: "4rem auto", padding: "1.5rem" }}
>
<h1>¿Olvidaste tu contraseña?</h1>
<p>Escribe el correo asociado a tu cuenta y te enviaremos instrucciones para restablecerla.</p>
<p>
Escribe el correo asociado a tu cuenta y te enviaremos instrucciones
para restablecerla.
</p>
<form onSubmit={handleSubmit} className="forgot-form" style={{ display: "grid", gap: 12 }}>
<form
onSubmit={handleSubmit}
className="forgot-form"
style={{ display: "grid", gap: 12 }}
>
<label htmlFor="email">Correo electrónico</label>
<input
id="email"
@@ -55,7 +67,11 @@ export default function ForgotPasswordPage() {
onChange={(e) => setEmail(e.target.value)}
placeholder="tu@correo.com"
required
style={{ padding: "0.6rem", borderRadius: 8, border: "1px solid #ccc" }}
style={{
padding: "0.6rem",
borderRadius: 8,
border: "1px solid #ccc",
}}
/>
<button
@@ -65,14 +81,14 @@ export default function ForgotPasswordPage() {
padding: "0.7rem 1rem",
borderRadius: 8,
border: "none",
cursor: loading ? "not-allowed" : "pointer"
cursor: loading ? "not-allowed" : "pointer",
}}
>
{loading ? "Enviando..." : "Enviar instrucciones"}
</button>
<div style={{ marginTop: 8 }}>
<a href="/">Recordé mi contraseña Iniciar sesión</a>
<Link href="/">Recordé mi contraseña Iniciar sesión</Link>
</div>
</form>
</main>
+93 -28
View File
@@ -22,13 +22,52 @@ export default function Page() {
responsable: "",
});
const [tiposUso, setTiposUso] = useState<string[]>([]);
const [marcas, setMarcas] = useState<string[]>([]);
const [estados, setEstados] = useState<string[]>([]);
const [adscripciones, setAdscripciones] = useState<string[]>([]);
const [tiposEquipo, setTiposEquipo] = useState<string[]>([]);
const [sistemasOperativos, setSistemasOperativos] = useState<string[]>([]);
const [procesadores, setProcesadores] = useState<string[]>([]);
// Tipos de datos
interface TipoUso {
id_tipo_uso: number;
tipo_uso: string;
}
interface Estado {
id_estado: number;
estado: string;
}
interface TipoEquipo {
id_tipo_de_equipo: number;
tipo_equipo: string;
}
interface SistemaOperativo {
id_sistema_operativo: number;
sistema_operativo: string;
}
interface Procesador {
id_procesador: number;
procesador: string;
}
interface Marca {
id_marca: number;
tipo_marca: string;
}
interface Adscripcion {
id_adscripcion: number;
adscripcion: string;
}
// Estados
const [tiposUso, setTiposUso] = useState<TipoUso[]>([]);
const [marcas, setMarcas] = useState<Marca[]>([]);
const [estados, setEstados] = useState<Estado[]>([]);
const [adscripciones, setAdscripciones] = useState<Adscripcion[]>([]);
const [tiposEquipo, setTiposEquipo] = useState<TipoEquipo[]>([]);
const [sistemasOperativos, setSistemasOperativos] = useState<
SistemaOperativo[]
>([]);
const [procesadores, setProcesadores] = useState<Procesador[]>([]);
const [suggestions, setSuggestions] = useState({
adscripcion: [] as string[],
@@ -68,6 +107,7 @@ export default function Page() {
setTiposEquipo(tiposEquipoRes.data);
setSistemasOperativos(sistemasOperativosRes.data);
setProcesadores(procesadoresRes.data);
console.log(adscripciones);
} catch (err) {
if (axios.isAxiosError(err)) {
if (err.response) {
@@ -86,7 +126,7 @@ export default function Page() {
};
fetchData();
}, []);
}, [api_url]);
const handleInputChange = (field: string, value: string) => {
setFormData((prev) => ({ ...prev, [field]: value }));
@@ -98,15 +138,32 @@ export default function Page() {
};
const handleGuardar = async () => {
const token = Cookies.get("token");
const headers = { Authorization: `Bearer ${token}` };
await axios.post(`${api_url}/equipos/crear`, formData, { headers });
toast.success("Equipo guardado");
try {
const token = Cookies.get("token");
const headers = { Authorization: `Bearer ${token}` };
await axios.post(`${api_url}/equipos/crear`, formData, { headers });
toast.success("Equipo guardado correctamente");
setFormData({
serie: "",
marca: "",
modelo: "",
tipoEquipo: "",
estado: "",
sistemaOperativo: "",
procesador: "",
tipoUso: "",
observaciones: "",
adscripcion: "",
lugar: "",
responsable: "",
});
} catch (err) {
toast.error("Error al guardar el equipo");
}
};
const handleCancelar = () => {
toast.success("Acción cancelada");
toast.error("Acción cancelada");
};
return (
@@ -119,6 +176,7 @@ export default function Page() {
<div className="formGroup">
<label>Serie</label>
<input
required
type="text"
placeholder="Ingresa serie"
value={formData.serie}
@@ -129,13 +187,14 @@ export default function Page() {
<div className="formGroup">
<label>Marca</label>
<select
required
value={formData.marca}
onChange={(e) => handleInputChange("marca", e.target.value)}
>
<option value="">Selecciona una marca</option>
{marcas.map((m) => (
<option key={m} value={m}>
{m}
<option key={m.id_marca} value={m.tipo_marca}>
{m.tipo_marca}
</option>
))}
</select>
@@ -154,6 +213,7 @@ export default function Page() {
<div className="formGroup">
<label>Tipo de equipo</label>
<select
required
value={formData.tipoEquipo}
onChange={(e) =>
handleInputChange("tipoEquipo", e.target.value)
@@ -161,8 +221,8 @@ export default function Page() {
>
<option value="">Selecciona tipo de equipo</option>
{tiposEquipo.map((t) => (
<option key={t} value={t}>
{t}
<option key={t.id_tipo_de_equipo} value={t.tipo_equipo}>
{t.tipo_equipo}
</option>
))}
</select>
@@ -171,13 +231,14 @@ export default function Page() {
<div className="formGroup">
<label>Estado</label>
<select
required
value={formData.estado}
onChange={(e) => handleInputChange("estado", e.target.value)}
>
<option value="">Selecciona estado</option>
{estados.map((e) => (
<option key={e} value={e}>
{e}
<option key={e.id_estado} value={e.estado}>
{e.estado}
</option>
))}
</select>
@@ -187,15 +248,16 @@ export default function Page() {
{/* Columna Centro */}
<div className="column">
<div className="formGroup">
<label>Tipo uso</label>
<label>Tipo de uso</label>
<select
required
value={formData.tipoUso}
onChange={(e) => handleInputChange("tipoUso", e.target.value)}
>
<option value="">Selecciona tipo de uso</option>
{tiposUso.map((t) => (
<option key={t} value={t}>
{t}
<option key={t.id_tipo_uso} value={t.tipo_uso}>
{t.tipo_uso}
</option>
))}
</select>
@@ -213,8 +275,8 @@ export default function Page() {
>
<option value="">Selecciona procesador</option>
{procesadores.map((p) => (
<option key={p} value={p}>
{p}
<option key={p.id_procesador} value={p.procesador}>
{p.procesador}
</option>
))}
</select>
@@ -230,8 +292,11 @@ export default function Page() {
>
<option value="">Selecciona sistema operativo</option>
{sistemasOperativos.map((so) => (
<option key={so} value={so}>
{so}
<option
key={so.id_sistema_operativo}
value={so.sistema_operativo}
>
{so.sistema_operativo}
</option>
))}
</select>
@@ -242,6 +307,7 @@ export default function Page() {
<div className="formGroup">
<label>Adscripción</label>
<input
required
type="text"
placeholder="Selecciona adscripción"
value={formData.adscripcion}
@@ -264,7 +330,6 @@ export default function Page() {
</div>
</div>
{/* Columna Derecha */}
<div className="column">
<div className="formGroup">
<label>Lugar</label>
+4 -3
View File
@@ -27,7 +27,7 @@ export default function Dashboard() {
toast.error("Equipo no encontrado");
}
} catch (error) {
toast.error("Equipo no encontrado");
toast.error(`Equipo no encontrado,${error}`);
}
};
@@ -42,11 +42,12 @@ export default function Dashboard() {
if (data) {
router.push(`/Editar?equipoId=${data.id}`);
} else {
router.push(`/AgregarEquipo?equipoId=${code}`);
router.push(`/AgregarEquipo?equipoId=${data.id}`);
}
} catch (error) {
router.push(`/AgregarEquipo?equipoId=${code}`);
toast.error("Error No se encontro el equipo");
}
toast(`${code}`);
};
return (
-1
View File
@@ -4,7 +4,6 @@ import { useState } from "react";
import "../styles/layout/login.scss";
import "../styles/base/globales.scss";
import Image from "next/image";
import Link from "next/link";
import axios from "axios";
import { useRouter } from "next/navigation";
import toast from "react-hot-toast";
+2 -14
View File
@@ -27,7 +27,6 @@ export default function BarcodeScanner({ onScan }: BarcodeScannerProps) {
useEffect(() => {
if (!scannerRef.current) return;
// ✅ Sin `any`: usamos un objeto literal tipado implícitamente
const config: QuaggaConfig = {
inputStream: {
name: "Live",
@@ -40,19 +39,8 @@ export default function BarcodeScanner({ onScan }: BarcodeScannerProps) {
},
},
decoder: {
readers: [
"code_128_reader",
"code_39_reader",
"code_39_vin_reader",
"ean_reader",
"ean_8_reader",
"upc_reader",
"upc_e_reader",
"i2of5_reader",
"2of5_reader",
"code_93_reader",
],
},
readers: ["code_128_reader"],
},
locate: true,
};
+108 -41
View File
@@ -10,9 +10,10 @@ import toast from "react-hot-toast";
export default function Editar() {
const searchParams = useSearchParams();
const equipoId = searchParams.get("equipoId");
const inventario = searchParams.get("equipoId");
const [formData, setFormData] = useState({
inventario: "",
serie: "",
marca: "",
modelo: "",
@@ -27,17 +28,55 @@ export default function Editar() {
responsable: "",
});
const [tiposUso, setTiposUso] = useState<string[]>([]);
const [marcas, setMarcas] = useState<string[]>([]);
const [estados, setEstados] = useState<string[]>([]);
const [adscripciones, setAdscripciones] = useState<string[]>([]);
const [tiposEquipo, setTiposEquipo] = useState<string[]>([]);
const [sistemasOperativos, setSistemasOperativos] = useState<string[]>([]);
const [procesadores, setProcesadores] = useState<string[]>([]);
interface TipoUso {
id_tipo_uso: number;
tipo_uso: string;
}
interface Estado {
id_estado: number;
estado: string;
}
interface TipoEquipo {
id_tipo_de_equipo: number;
tipo_equipo: string;
}
interface SistemaOperativo {
id_sistema_operativo: number;
sistema_operativo: string;
}
interface Procesador {
id_procesador: number;
procesador: string;
}
interface Marca {
id_marca: number;
tipo_marca: string;
}
interface Adscripcion {
id_adscripcion: number;
adscripcion: string;
}
const [tiposUso, setTiposUso] = useState<TipoUso[]>([]);
const [marcas, setMarcas] = useState<Marca[]>([]);
const [estados, setEstados] = useState<Estado[]>([]);
const [adscripciones, setAdscripciones] = useState<Adscripcion[]>([]);
const [tiposEquipo, setTiposEquipo] = useState<TipoEquipo[]>([]);
const [sistemasOperativos, setSistemasOperativos] = useState<
SistemaOperativo[]
>([]);
const [procesadores, setProcesadores] = useState<Procesador[]>([]);
const api_url = process.env.NEXT_PUBLIC_API_URL;
const mostrarCamposComputadora = formData.tipoEquipo !== "Impresora";
// 🔹 Cargar catálogos
useEffect(() => {
const fetchCatalogos = async () => {
const token = Cookies.get("token");
@@ -80,26 +119,55 @@ export default function Editar() {
useEffect(() => {
const fetchEquipo = async () => {
if (!inventario) return;
const token = Cookies.get("token");
const headers = { Authorization: `Bearer ${token}` };
try {
const response = await axios.get(`${api_url}/equipos/${equipoId}`, {
headers,
const response = await axios.get(
`${api_url}/equipos/buscar/${inventario}`,
{
headers,
}
);
const equipo = response.data;
setFormData({
inventario: equipo.inventario || "",
serie: equipo.serie || "",
marca: equipo.marca?.marca || "", // <- relación
modelo: equipo.modelo || "",
tipoEquipo: equipo.tipoEquipo?.tipo || "",
estado: equipo.estado?.estado || "",
sistemaOperativo: equipo.sistemaOperativo?.nombre || "",
procesador: equipo.procesador?.nombre || "",
tipoUso: equipo.tipoUso?.tipo || "",
observaciones: equipo.observaciones || "",
adscripcion: equipo.adscripcion?.adscripcion || "",
lugar: equipo.lugar || "",
responsable: equipo.responsable || "", // <- devuelto por searchResponsable
});
setFormData(response.data);
} catch (error) {
console.error("Error al obtener el equipo:", error);
toast.error("No se pudo cargar la información del equipo.");
}
};
if (equipoId) fetchEquipo();
}, [equipoId]);
fetchEquipo();
}, [inventario]);
// 🔹 Guardar cambios
const handleGuardar = async () => {
const token = Cookies.get("token");
const headers = { Authorization: `Bearer ${token}` };
try {
await axios.patch(`${api_url}/equipos/update/${equipoId}`, formData);
await axios.patch(
`${api_url}/equipos/update/${formData.inventario}`,
formData,
{ headers }
);
toast.success("Equipo actualizado correctamente");
} catch (error) {
console.error("Error al guardar:", error);
@@ -115,17 +183,21 @@ export default function Editar() {
<div className="agregarEquipoContainer">
<div className="innerContainer">
<h2 className="information">
{equipoId ? `Editar Equipo (${equipoId})` : "Agregar Equipo"}
{inventario ? `Editar Equipo (${inventario})` : "Agregar Equipo"}
</h2>
<form className="equipoForm">
{/* Columna 1 */}
<div className="column">
<div className="formGroup">
<label>Inventario</label>
<input type="text" value={formData.inventario} disabled />
</div>
<div className="formGroup">
<label>Serie</label>
<input
type="text"
placeholder="Ingresa serie"
value={formData.serie}
onChange={(e) =>
setFormData({ ...formData, serie: e.target.value })
@@ -143,8 +215,8 @@ export default function Editar() {
>
<option value="">Selecciona marca</option>
{marcas.map((m) => (
<option key={m} value={m}>
{m}
<option key={m.id_marca} value={m.tipo_marca}>
{m.tipo_marca}
</option>
))}
</select>
@@ -154,7 +226,6 @@ export default function Editar() {
<label>Modelo</label>
<input
type="text"
placeholder="Ingresa modelo"
value={formData.modelo}
onChange={(e) =>
setFormData({ ...formData, modelo: e.target.value })
@@ -170,10 +241,10 @@ export default function Editar() {
setFormData({ ...formData, tipoEquipo: e.target.value })
}
>
<option value="">Selecciona tipo de equipo</option>
<option value="">Selecciona tipo</option>
{tiposEquipo.map((t) => (
<option key={t} value={t}>
{t}
<option key={t.id_tipo_de_equipo} value={t.tipo_equipo}>
{t.tipo_equipo}
</option>
))}
</select>
@@ -189,8 +260,8 @@ export default function Editar() {
>
<option value="">Selecciona estado</option>
{estados.map((e) => (
<option key={e} value={e}>
{e}
<option key={e.id_estado} value={e.estado}>
{e.estado}
</option>
))}
</select>
@@ -207,10 +278,10 @@ export default function Editar() {
setFormData({ ...formData, tipoUso: e.target.value })
}
>
<option value="">Selecciona tipo de uso</option>
<option value="">Selecciona uso</option>
{tiposUso.map((t) => (
<option key={t} value={t}>
{t}
<option key={t.id_tipo_uso} value={t.tipo_uso}>
{t.tipo_uso}
</option>
))}
</select>
@@ -228,8 +299,8 @@ export default function Editar() {
>
<option value="">Selecciona procesador</option>
{procesadores.map((p) => (
<option key={p} value={p}>
{p}
<option key={p.id_procesador} value={p.procesador}>
{p.procesador}
</option>
))}
</select>
@@ -248,8 +319,11 @@ export default function Editar() {
>
<option value="">Selecciona sistema operativo</option>
{sistemasOperativos.map((so) => (
<option key={so} value={so}>
{so}
<option
key={so.id_sistema_operativo}
value={so.sistema_operativo}
>
{so.sistema_operativo}
</option>
))}
</select>
@@ -267,8 +341,8 @@ export default function Editar() {
>
<option value="">Selecciona adscripción</option>
{adscripciones.map((a) => (
<option key={a} value={a}>
{a}
<option key={a.id_adscripcion} value={a.adscripcion}>
{a.adscripcion}
</option>
))}
</select>
@@ -276,12 +350,7 @@ export default function Editar() {
<div className="formGroup">
<label>Responsable</label>
<input
type="text"
value={formData.responsable}
disabled
placeholder="Responsable asignado"
/>
<input type="text" value={formData.responsable} disabled />
</div>
</div>
@@ -290,7 +359,6 @@ export default function Editar() {
<div className="formGroup">
<label>Lugar</label>
<textarea
placeholder="Ingresa lugar"
value={formData.lugar}
onChange={(e) =>
setFormData({ ...formData, lugar: e.target.value })
@@ -303,7 +371,6 @@ export default function Editar() {
<div className="formGroup">
<label>Observaciones</label>
<textarea
placeholder="Ingresa observaciones"
value={formData.observaciones}
onChange={(e) =>
setFormData({ ...formData, observaciones: e.target.value })
+1 -1
View File
@@ -13,6 +13,6 @@ export function middleware(request: NextRequest) {
}
export const config = {
matcher: ["/AgregarEquipo", "/Escaner"],
matcher: [],
};
//IO