Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@
## 2025-06-17 - [Dynamic Theme Feedback and Functional Branding]
**Learning:** Converting static branding elements into functional navigation links (e.g., logo to dashboard) fulfills standard user expectations. Furthermore, synchronizing the theme toggle's icon and ARIA labels with the active theme via React state, combined with subtle CSS transitions (scale and rotation), creates a more accessible and "delightful" interaction that feels responsive to the user's current environment.
**Action:** Always ensure branding logos are functional links and use dynamic state for theme-related UI elements to provide clear, accessible feedback.

## 2025-06-18 - [Standardized Auth Visual Context and Accessibility]
**Learning:** Adding prefix icons to authentication inputs provides immediate visual context, improving scanability. Implementing a consistent password visibility toggle across all auth forms (Login/Signup) ensures a predictable user experience. Crucially, explicitly associating labels with inputs via `id` and `htmlFor` significantly improves accessibility for screen readers and touch target size.
**Action:** Always include relevant prefix icons in high-visibility forms and ensure strict label-input association for accessibility.

## 2025-02-03 - [Unified Loading Feedback in Buttons and Components]
**Learning:** Moving loading logic (spinners, text, disabled state) into base components like `CustomButton` reduces boilerplate and ensures consistent UX across the app. In Spanish interfaces, using semantic icons like `check_circle` within a `valign-wrapper` in toasts provides clear, professional feedback for critical actions like saving.
**Action:** Abstract loading states into reusable UI components and always include visual markers (icons) for status updates.
Expand Down
22 changes: 19 additions & 3 deletions src/v2/pages/V2ForgotPassword.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useState } from 'react';
import { useHistory, Link } from 'react-router-dom';
import { alertSuccess, alertError } from '../../services/AlertService';
import CustomPreloader from '../../components/custom/CustomPreloader';
import '../styles/v2-theme.css';

const V2ForgotPassword = () => {
Expand Down Expand Up @@ -48,19 +49,34 @@ const V2ForgotPassword = () => {
{!submitted ? (
<form onSubmit={handleResetRequest} style={{ display: 'flex', flexDirection: 'column', gap: '20px', textAlign: 'left' }}>
<div className="v2-input-outlined">
<label>Correo Electrónico</label>
<label htmlFor="email">Correo Electrónico</label>
<i className="material-icons" aria-hidden="true" style={{
position: 'absolute', left: '12px', top: '50%', transform: 'translateY(-50%)',
color: 'var(--md-sys-color-outline)', marginTop: '8px'
}}>mail</i>
<input
id="email"
type="email"
placeholder="doctor@medical.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
style={{ paddingLeft: '48px' }}
/>
</div>

<button type="submit" className="v2-btn-filled" style={{ justifyContent: 'center', height: '56px' }} disabled={loading}>
{loading ? 'Procesando...' : 'Enviar Instrucciones'}
{!loading && <i className="material-icons">send</i>}
{loading ? (
<span className="valign-wrapper" style={{ gap: '12px' }}>
<CustomPreloader size="small" />
Procesando...
</span>
) : (
<>
Enviar Instrucciones
<i className="material-icons">send</i>
</>
)}
</button>
</form>
) : (
Expand Down
25 changes: 22 additions & 3 deletions src/v2/pages/V2Login.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useHistory, Link } from 'react-router-dom';
import UserService from '../../services/UserService';
import Auth from '../../modules/Auth';
import { alertError } from '../../services/AlertService';
import CustomPreloader from '../../components/custom/CustomPreloader';
import '../styles/v2-theme.css';

const V2Login = () => {
Expand Down Expand Up @@ -55,26 +56,35 @@ const V2Login = () => {
<form onSubmit={handleLogin} style={{ display: 'flex', flexDirection: 'column', gap: '20px', textAlign: 'left' }}>
<div className="v2-input-outlined">
<label htmlFor="email">Correo Electrónico</label>
<i className="material-icons" aria-hidden="true" style={{
position: 'absolute', left: '12px', top: '50%', transform: 'translateY(-50%)',
color: 'var(--md-sys-color-outline)', marginTop: '8px'
}}>mail</i>
<input
id="email"
type="email"
placeholder="doctor@medical.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
style={{ paddingLeft: '48px' }}
/>
</div>

<div className="v2-input-outlined" style={{ position: 'relative' }}>
<label htmlFor="password">Contraseña</label>
<i className="material-icons" aria-hidden="true" style={{
position: 'absolute', left: '12px', top: '50%', transform: 'translateY(-50%)',
color: 'var(--md-sys-color-outline)', marginTop: '8px'
}}>lock</i>
<input
id="password"
type={showPassword ? "text" : "password"}
placeholder="••••••••"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
style={{ paddingRight: '48px' }}
style={{ paddingLeft: '48px', paddingRight: '48px' }}
/>
<button
type="button"
Expand Down Expand Up @@ -107,8 +117,17 @@ const V2Login = () => {
</div>

<button type="submit" className="v2-btn-filled" style={{ justifyContent: 'center', height: '56px' }} disabled={loading}>
{loading ? 'Iniciando sesión...' : 'Entrar'}
{!loading && <i className="material-icons" aria-hidden="true">arrow_forward</i>}
{loading ? (
<span className="valign-wrapper" style={{ gap: '12px' }}>
<CustomPreloader size="small" />
Iniciando sesión...
</span>
) : (
<>
Entrar
<i className="material-icons" aria-hidden="true">arrow_forward</i>
</>
)}
</button>
</form>

Expand Down
73 changes: 65 additions & 8 deletions src/v2/pages/V2Signup.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useHistory, Link } from 'react-router-dom';
import UserService from '../../services/UserService';
import Auth from '../../modules/Auth';
import { alertError } from '../../services/AlertService';
import CustomPreloader from '../../components/custom/CustomPreloader';
import '../styles/v2-theme.css';

const V2Signup = () => {
Expand All @@ -12,6 +13,7 @@ const V2Signup = () => {
username: '',
password: ''
});
const [showPassword, setShowPassword] = useState(false);
const [loading, setLoading] = useState(false);
const history = useHistory();

Expand Down Expand Up @@ -61,56 +63,111 @@ const V2Signup = () => {

<form onSubmit={handleSignup} style={{ display: 'flex', flexDirection: 'column', gap: '16px', textAlign: 'left' }}>
<div className="v2-input-outlined">
<label>Nombre Completo</label>
<label htmlFor="name">Nombre Completo</label>
<i className="material-icons" aria-hidden="true" style={{
position: 'absolute', left: '12px', top: '50%', transform: 'translateY(-50%)',
color: 'var(--md-sys-color-outline)', marginTop: '8px'
}}>person</i>
<input
id="name"
type="text"
name="name"
placeholder="Dr. García"
value={formData.name}
onChange={handleChange}
required
style={{ paddingLeft: '48px' }}
/>
</div>

<div className="v2-input-outlined">
<label>Correo Electrónico</label>
<label htmlFor="email">Correo Electrónico</label>
<i className="material-icons" aria-hidden="true" style={{
position: 'absolute', left: '12px', top: '50%', transform: 'translateY(-50%)',
color: 'var(--md-sys-color-outline)', marginTop: '8px'
}}>mail</i>
<input
id="email"
type="email"
name="email"
placeholder="doctor@medical.com"
value={formData.email}
onChange={handleChange}
required
style={{ paddingLeft: '48px' }}
/>
</div>

<div className="v2-input-outlined">
<label>Nombre de Usuario</label>
<label htmlFor="username">Nombre de Usuario</label>
<i className="material-icons" aria-hidden="true" style={{
position: 'absolute', left: '12px', top: '50%', transform: 'translateY(-50%)',
color: 'var(--md-sys-color-outline)', marginTop: '8px'
}}>badge</i>
<input
id="username"
type="text"
name="username"
placeholder="drgarcia"
value={formData.username}
onChange={handleChange}
required
style={{ paddingLeft: '48px' }}
/>
</div>

<div className="v2-input-outlined">
<label>Contraseña</label>
<div className="v2-input-outlined" style={{ position: 'relative' }}>
<label htmlFor="password">Contraseña</label>
<i className="material-icons" aria-hidden="true" style={{
position: 'absolute', left: '12px', top: '50%', transform: 'translateY(-50%)',
color: 'var(--md-sys-color-outline)', marginTop: '8px'
}}>lock</i>
<input
type="password"
id="password"
type={showPassword ? "text" : "password"}
name="password"
placeholder="••••••••"
value={formData.password}
onChange={handleChange}
required
style={{ paddingLeft: '48px', paddingRight: '48px' }}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
aria-label={showPassword ? "Ocultar contraseña" : "Mostrar contraseña"}
title={showPassword ? "Ocultar contraseña" : "Mostrar contraseña"}
style={{
position: 'absolute',
right: '12px',
top: '50%',
transform: 'translateY(-50%)',
background: 'none',
border: 'none',
cursor: 'pointer',
color: 'var(--md-sys-color-outline)',
display: 'flex',
alignItems: 'center',
padding: '8px',
marginTop: '12px'
}}
>
<i className="material-icons">{showPassword ? 'visibility_off' : 'visibility'}</i>
</button>
</div>

<button type="submit" className="v2-btn-filled" style={{ justifyContent: 'center', height: '56px', marginTop: '8px' }} disabled={loading}>
{loading ? 'Creando cuenta...' : 'Registrarse'}
{!loading && <i className="material-icons">person_add</i>}
{loading ? (
<span className="valign-wrapper" style={{ gap: '12px' }}>
<CustomPreloader size="small" />
Creando cuenta...
</span>
) : (
<>
Registrarse
<i className="material-icons">person_add</i>
</>
)}
</button>
</form>

Expand Down
Loading