Refactor landing page components and improve session management

- Updated the Dashboard component to simplify layout and styling.
- Enhanced IconCircle component with improved CSS for better alignment and display.
- Refactored global styles for consistency and clarity.
- Improved LandingBody component to utilize Link for navigation.
- Revamped Header component to manage navigation state more effectively.
- Implemented a new LoginPage component for user authentication.
- Introduced useSession hook for managing user session state.
- Updated CargaArchivo component to use a consistent Button component.
- Enhanced Consulta component for better error handling and display.
- Cleaned up Footer component styling and structure.
This commit is contained in:
miguel
2025-06-25 16:21:33 -06:00
parent fa3f3da9f9
commit cab84e15cb
18 changed files with 556 additions and 566 deletions
+12 -16
View File
@@ -1,5 +1,6 @@
"use client";
import React, { useState } from "react";
import Button from "./button";
const CargaArchivo: React.FC = () => {
const [archivo, setArchivo] = useState<File | null>(null);
@@ -34,7 +35,9 @@ const CargaArchivo: React.FC = () => {
const data = await res.json();
if (res.ok) {
alert(`Archivo subido correctamente (ID Movimiento: ${data.id_movimiento})`);
alert(
`Archivo subido correctamente (ID Movimiento: ${data.id_movimiento})`
);
setArchivo(null);
if (data.errors && data.errors.length > 0) {
setErrores(data.errors); // ← Mostrar ventana si hay errores
@@ -56,29 +59,22 @@ const CargaArchivo: React.FC = () => {
onChange={handleChange}
className="mb-4 block text-sm"
/>
<button
onClick={handleUpload}
disabled={!archivo}
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
style={{
backgroundColor: "#2563eb",
color: "#fff",
}}
>
<Button onClick={handleUpload} disabled={!archivo} variant="azul">
Subir archivo
</button>
</Button>
{/* Modal de errores */}
{errores && (
<div className="fixed top-10 right-10 bg-white border border-red-400 text-red-700 p-4 rounded-lg shadow-lg w-96 z-50">
<div className="">
<div className="flex justify-between items-center mb-2">
<strong className="text-lg">Reporte de Errores</strong>
<button
<Button
variant="danger"
onClick={() => setErrores(null)}
className="text-red-600 font-bold text-xl hover:text-red-800"
className="text-sm"
>
×
</button>
<i className="fas fa-times"></i>
</Button>
</div>
<ul className="list-disc list-inside text-sm max-h-60 overflow-y-auto">
{errores.map((error, index) => (
+4 -7
View File
@@ -1,15 +1,12 @@
import React from "react";
import { motion } from "framer-motion";
export default function Footer() {
const date = new Date();
const year = date.getFullYear();
return (
return (
<motion.footer
// Empieza 50px abajo y totalmente transparente
initial={{ y: 50, opacity: 0 }}
@@ -17,7 +14,7 @@ return (
animate={{ y: 0, opacity: 1 }}
// Retraso para que aparezca justo después del main
transition={{ delay: 2, duration: 1, ease: "easeOut" }}
className="p-3 text-center small text-muted opacity-50"
className="p-3 text-center small text-muted"
>
<p className="mb-1">
Hecho en México. Todos los derechos reservados 2025.
@@ -29,5 +26,5 @@ return (
institución.
</p>
</motion.footer>
)
}
);
}
+12 -29
View File
@@ -1,10 +1,9 @@
'use client';
import React, { useState } from 'react';
import React, { useState } from "react";
export interface PasswordInputProps
extends Omit<
React.InputHTMLAttributes<HTMLInputElement>,
'type' | 'className'
"type" | "className"
> {
/**
* Texto de la etiqueta asociada al input (opcional).
@@ -33,53 +32,37 @@ export default function PasswordInput(props: PasswordInputProps) {
const [visible, setVisible] = useState(false);
// Determina el tipo de input según el estado de visibilidad
const inputType = visible ? 'text' : 'password';
const inputType = visible ? "text" : "password";
const inputId =
id ||
name ||
(label ? `password-${label.replace(/\s+/g, '-')}` : undefined);
(label ? `password-${label.toLowerCase().replace(/\s+/g, "-")}` : undefined);
const toggleVisibility = () => setVisible((v) => !v);
return (
<div className={className?.container || 'mb-3'}>
<div className={className?.container || "mb-3"}>
{label && inputId && (
<label htmlFor={inputId} className={className?.label || 'form-label'}>
<label htmlFor={inputId} className={className?.label || "form-label"}>
{label}
</label>
)}
<div className="input-group" style={{ display: 'flex', alignItems: 'stretch' }}>
<div className="input-group">
<input
id={inputId}
name={name}
type={inputType}
className={className?.input || 'form-control'}
style={{
borderRight: 'none',
borderTopRightRadius: 0,
borderBottomRightRadius: 0,
flex: 1
}}
className={className?.input || "form-control"}
{...rest}
/>
<button
type="button"
className={className?.button || 'btn btn-light border'}
className={className?.button || "btn btn-light border"}
onClick={toggleVisibility}
aria-label={visible ? 'Ocultar contraseña' : 'Mostrar contraseña'}
title={visible ? 'Ocultar contraseña' : 'Mostrar contraseña'}
style={{
borderLeft: 'none',
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
padding: '0.375rem 0.75rem',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
minWidth: '40px'
}}
aria-label={visible ? "Ocultar contraseña" : "Mostrar contraseña"}
title={visible ? "Ocultar contraseña" : "Mostrar contraseña"}
>
<i className={`bi ${visible ? 'bi-eye-slash' : 'bi-eye'}`} />
<i className={`bi ${visible ? "bi-eye-slash" : "bi-eye"}`} />
</button>
</div>
</div>