diff --git a/frontend/src/components/RegisterForm.tsx b/frontend/src/components/RegisterForm.tsx new file mode 100644 index 0000000..12943d2 --- /dev/null +++ b/frontend/src/components/RegisterForm.tsx @@ -0,0 +1,206 @@ +import { useState } from 'react' + +interface Props { + registerUrl: string + csrfToken: string +} + +type RegisterResponse = { + success?: boolean + message?: string +} + +export function RegisterForm({ registerUrl, csrfToken }: Props) { + const [password, setPassword] = useState('') + const [confPassword, setConfPassword] = useState('') + const [email, setEmail] = useState('') + const [confEmail, setConfEmail] = useState('') + const [recruiter, setRecruiter] = useState('') + const [accepted, setAccepted] = useState(false) + const [showPw, setShowPw] = useState(false) + const [showConfPw, setShowConfPw] = useState(false) + const [busy, setBusy] = useState(false) + const [response, setResponse] = useState<{ ok: boolean; html: string } | null>(null) + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault() + if (busy || !accepted) return + + setBusy(true) + setResponse(null) + + const body = new URLSearchParams() + body.set('password', password.trim()) + body.set('conf-password', confPassword.trim()) + body.set('email', email.trim()) + body.set('conf-email', confEmail.trim()) + body.set('recruiter', recruiter.trim()) + body.set('csrfmiddlewaretoken', csrfToken) + + try { + const resp = await fetch(registerUrl, { + 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: RegisterResponse = await resp.json() + const msg = data.message ?? '' + // El mensaje de éxito ya viene como HTML; el de error es texto. + setResponse({ + ok: !!data.success, + html: data.success ? msg : `${msg}`, + }) + } catch { + setResponse({ + ok: false, + html: 'Error en el servidor. Inténtalo de nuevo más tarde.', + }) + } finally { + setBusy(false) + } + } + + return ( + <> +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ setPassword(e.target.value)} + /> + setShowPw((v) => !v)} + /> +
+ setConfPassword(e.target.value)} + /> + setShowConfPw((v) => !v)} + /> +
+ setEmail(e.target.value)} + /> +
+ setConfEmail(e.target.value)} + /> +
+
+
Opcional: Sólo si eres reclutado por un amigo
+ setRecruiter(e.target.value)} + /> +
+ setAccepted(e.target.checked)} + /> + +
+ +
+
+
+
+ {response && } +
+ + ) +} diff --git a/frontend/src/entries/register.tsx b/frontend/src/entries/register.tsx new file mode 100644 index 0000000..72125a3 --- /dev/null +++ b/frontend/src/entries/register.tsx @@ -0,0 +1,15 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import { RegisterForm } from '../components/RegisterForm' + +const el = document.getElementById('register-app') +if (el) { + createRoot(el).render( + + + , + ) +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 65638b4..173de4b 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -16,6 +16,7 @@ export default defineConfig({ input: { home: resolve(__dirname, 'src/entries/home.tsx'), login: resolve(__dirname, 'src/entries/login.tsx'), + register: resolve(__dirname, 'src/entries/register.tsx'), }, }, }, diff --git a/home/templates/partials/register.html b/home/templates/partials/register.html index 34e03a6..47cdd9b 100644 --- a/home/templates/partials/register.html +++ b/home/templates/partials/register.html @@ -54,48 +54,13 @@
-
- {% csrf_token %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-

Opcional: Sólo si eres reclutado por un amigo
- - -
-
-
-
+ {% load django_vite %} + +
+ {% vite_asset 'src/entries/register.tsx' %}