se agregó la pagina principal

This commit is contained in:
evenegas
2025-06-12 22:39:35 -06:00
parent 9a142011ec
commit 56e2b4b8d6
40 changed files with 1575 additions and 17368 deletions
+17
View File
@@ -0,0 +1,17 @@
'use client';
import { useEffect } from 'react';
export default function BootstrapClient() {
useEffect(() => {
const loadBootstrap = async () => {
if (typeof document !== 'undefined') {
// @ts-expect-error: Bootstrap is loaded dynamically
await import('bootstrap/dist/js/bootstrap.bundle.min.js');
}
};
loadBootstrap();
}, []);
return null;
}
+42
View File
@@ -0,0 +1,42 @@
import React from 'react';
export interface SimpleInputProps
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'className'> {
/** Etiqueta visible asociada al campo de entrada */
label?: string;
/** Clases CSS personalizables para distintos elementos */
className?: {
/** Clases para el contenedor del input */
container?: string;
/** Clases para la etiqueta */
label?: string;
/** Clases para el input */
input?: string;
};
}
/**
* Componente de entrada (`Input`) sin lógica de mostrar/ocultar contraseña.
*/
export default function SimpleInput(props: SimpleInputProps) {
const { label, name, id, className, ...rest } = props;
const inputId =
id || name || (label ? `input-${label.replace(/\s+/g, '-')}` : undefined);
return (
<div className={className?.container || 'mb-3'}>
{label && inputId && (
<label htmlFor={inputId} className={className?.label || 'form-label'}>
{label}
</label>
)}
<input
id={inputId}
name={name}
className={className?.input || 'form-control'}
{...rest}
/>
</div>
);
}
+70
View File
@@ -0,0 +1,70 @@
import React, { useState } from 'react';
export interface PasswordInputProps
extends Omit<
React.InputHTMLAttributes<HTMLInputElement>,
'type' | 'className'
> {
/**
* Texto de la etiqueta asociada al input (opcional).
*/
label?: string;
/**
* Clases CSS personalizables para distinto s elementos.
*/
className?: {
/** Clases para el contenedor del componente */
container?: string;
/** Clases para la etiqueta */
label?: string;
/** Clases para el campo input */
input?: string;
/** Clases para el botón de toggle */
button?: string;
};
}
/**
* Componente especializado para inputs de contraseña con botón de mostrar/ocultar.
*/
export default function PasswordInput(props: PasswordInputProps) {
const { label, name, id, className, ...rest } = props;
const [visible, setVisible] = useState(false);
// Determina el tipo de input según el estado de visibilidad
const inputType = visible ? 'text' : 'password';
const inputId =
id ||
name ||
(label ? `password-${label.replace(/\s+/g, '-')}` : undefined);
const toggleVisibility = () => setVisible((v) => !v);
return (
<div className={className?.container || 'mb-3'}>
{label && inputId && (
<label htmlFor={inputId} className={className?.label || 'form-label'}>
{label}
</label>
)}
<div className="input-group">
<input
id={inputId}
name={name}
type={inputType}
className={className?.input || 'form-control'}
{...rest}
/>
<button
type="button"
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'}
>
<i className={`bi ${visible ? 'bi-eye-slash' : 'bi-eye'}`} />
</button>
</div>
</div>
);
}