diff --git a/frontend/src/components/ChangeEmailForm.tsx b/frontend/src/components/ChangeEmailForm.tsx new file mode 100644 index 0000000..8a96dd2 --- /dev/null +++ b/frontend/src/components/ChangeEmailForm.tsx @@ -0,0 +1,102 @@ +import { useState } from 'react' + +interface Props { + url: string + csrfToken: string +} + +interface Field { + name: string + type: 'password' | 'email' | 'text' + placeholder: string + maxLength?: number + toggle?: boolean +} + +const FIELDS: Field[] = [ + { name: 'cur-password', type: 'password', placeholder: 'Contraseña actual', maxLength: 16, toggle: true }, + { name: 'cur-email', type: 'email', placeholder: 'Correo actual' }, + { name: 'new-email', type: 'email', placeholder: 'Nuevo correo' }, + { name: 'conf-new-email', type: 'email', placeholder: 'Confirmar nuevo correo' }, + { name: 'security-token', type: 'text', placeholder: 'Token de seguridad', maxLength: 6 }, +] + +export function ChangeEmailForm({ url, csrfToken }: Props) { + const [values, setValues] = useState>({}) + const [showPw, setShowPw] = useState(false) + const [busy, setBusy] = useState(false) + const [message, setMessage] = useState<{ ok: boolean; html: string } | null>(null) + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault() + if (busy) return + setBusy(true) + setMessage(null) + + const body = new URLSearchParams() + for (const f of FIELDS) body.set(f.name, (values[f.name] || '').trim()) + body.set('csrfmiddlewaretoken', csrfToken) + + try { + const resp = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'X-CSRFToken': csrfToken, + Accept: 'application/json', + }, + credentials: 'same-origin', + body: body.toString(), + }) + if (!resp.ok) throw new Error(`HTTP ${resp.status}`) + const data: { success?: boolean; message?: string } = await resp.json() + setMessage({ ok: !!data.success, html: data.message || '' }) + } catch { + setMessage({ ok: false, html: 'Error en el servidor. Inténtalo más tarde.' }) + } finally { + setBusy(false) + } + } + + return ( +
+ + + {FIELDS.map((f) => ( + + + + ))} + + + + +
+ setValues((v) => ({ ...v, [f.name]: e.target.value }))} + /> + {f.toggle && ( + setShowPw((s) => !s)} + /> + )} +
+ +
+
+
+ {message && } +
+
+ ) +} diff --git a/frontend/src/entries/change_email.tsx b/frontend/src/entries/change_email.tsx new file mode 100644 index 0000000..269e5fa --- /dev/null +++ b/frontend/src/entries/change_email.tsx @@ -0,0 +1,12 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import { ChangeEmailForm } from '../components/ChangeEmailForm' + +const el = document.getElementById('change-email-app') +if (el) { + createRoot(el).render( + + + , + ) +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index b96948d..7957025 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -23,6 +23,7 @@ export default defineConfig({ my_account: resolve(__dirname, 'src/entries/my_account.tsx'), change_password: resolve(__dirname, 'src/entries/change_password.tsx'), security_token: resolve(__dirname, 'src/entries/security_token.tsx'), + change_email: resolve(__dirname, 'src/entries/change_email.tsx'), }, }, }, diff --git a/home/templates/partials/change_email.html b/home/templates/partials/change_email.html index 67a5c80..7208db9 100644 --- a/home/templates/partials/change_email.html +++ b/home/templates/partials/change_email.html @@ -15,37 +15,13 @@

-
- {% csrf_token %} - - - - - - - - - - - - - - - - - - - - -
- - -
- -
-
-
-
+{% load django_vite %} + +
+{% vite_asset 'src/entries/change_email.tsx' %}