452 lines
10 KiB
Vue
452 lines
10 KiB
Vue
<script setup lang="ts">
|
|
import { ref } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import { authService } from '@/services/authService'
|
|
import { analyticsService } from '@/services/analyticsService'
|
|
import { supabase } from '@/supabase'
|
|
import { useAuthStore } from '@/stores/auth'
|
|
|
|
const emit = defineEmits(['toggle', 'success'])
|
|
|
|
const router = useRouter()
|
|
const authStore = useAuthStore()
|
|
|
|
const fullName = ref('')
|
|
const email = ref('')
|
|
const password = ref('')
|
|
const isLoading = ref(false)
|
|
const errorMessage = ref('')
|
|
const successMessage = ref('')
|
|
const showPassword = ref(false)
|
|
|
|
const handleRegister = async () => {
|
|
isLoading.value = true
|
|
errorMessage.value = ''
|
|
console.log('Intentando registrar usuario...')
|
|
|
|
try {
|
|
const cleanEmail = email.value.trim().toLowerCase()
|
|
const cleanPass = password.value
|
|
|
|
const regResponse = await authService.registerPassenger({
|
|
full_name: fullName.value.trim(),
|
|
email: cleanEmail,
|
|
password: cleanPass
|
|
})
|
|
console.log('Registro exitoso en Supabase:', regResponse)
|
|
|
|
analyticsService.logEvent({
|
|
event_name: 'sign_up',
|
|
properties: { method: 'email' }
|
|
})
|
|
|
|
console.log('Iniciando sesión automática...')
|
|
await authStore.login(cleanEmail, cleanPass)
|
|
|
|
successMessage.value = '¡Cuenta creada con éxito!'
|
|
|
|
setTimeout(() => {
|
|
const r = authStore.role || 'PASSENGER'
|
|
navigateByUserRole(r)
|
|
}, 1500)
|
|
|
|
} catch (error: any) {
|
|
console.error('Error detallado de registro:', error)
|
|
errorMessage.value = `Error: ${error.message || 'Error desconocido'}`
|
|
} finally {
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
|
|
const navigateByUserRole = (role: string) => {
|
|
const r = role.toUpperCase()
|
|
if (r === 'ADMIN') router.push('/admin')
|
|
else if (r === 'DRIVER') router.push('/driver')
|
|
else if (r === 'PROMOTER') router.push('/promoter')
|
|
else router.push('/map')
|
|
}
|
|
|
|
const handleGoogleRegister = async () => {
|
|
isLoading.value = true
|
|
errorMessage.value = ''
|
|
console.log('Iniciando Google Register...')
|
|
|
|
try {
|
|
const { error } = await supabase.auth.signInWithOAuth({
|
|
provider: 'google',
|
|
options: {
|
|
redirectTo: window.location.origin
|
|
}
|
|
})
|
|
if (error) throw error
|
|
// Redirect happens automatically
|
|
} catch (error: any) {
|
|
console.error('Error Google Register:', error)
|
|
errorMessage.value = `Error con Google: ${error.message || 'Intenta de nuevo'}`
|
|
} finally {
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="register-form">
|
|
|
|
<!-- Éxito -->
|
|
<div v-if="successMessage" class="success-card">
|
|
<span class="material-icons success-icon">check_circle</span>
|
|
<h3 class="success-title">¡Registro exitoso!</h3>
|
|
<p class="success-desc">{{ successMessage }}</p>
|
|
</div>
|
|
|
|
<!-- Formulario -->
|
|
<template v-else>
|
|
|
|
<!-- Google -->
|
|
<button
|
|
type="button"
|
|
class="google-btn"
|
|
:disabled="isLoading"
|
|
@click="handleGoogleRegister"
|
|
>
|
|
<img src="https://www.gstatic.com/firebasejs/ui/2.0.0/images/auth/google.svg" width="20" height="20" alt="Google" />
|
|
<span>Registrarse con Google</span>
|
|
</button>
|
|
|
|
<div class="divider">
|
|
<span class="divider-line"></span>
|
|
<span class="divider-text">o con correo</span>
|
|
<span class="divider-line"></span>
|
|
</div>
|
|
|
|
<form @submit.prevent="handleRegister">
|
|
|
|
<!-- Nombre -->
|
|
<div class="field">
|
|
<label class="field-label" for="reg-name">Nombre completo</label>
|
|
<div class="input-wrap">
|
|
<span class="material-icons input-icon">person</span>
|
|
<input
|
|
id="reg-name"
|
|
type="text"
|
|
v-model="fullName"
|
|
placeholder="Tu nombre"
|
|
required
|
|
autocomplete="name"
|
|
class="field-input"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Email -->
|
|
<div class="field">
|
|
<label class="field-label" for="reg-email">Correo electrónico</label>
|
|
<div class="input-wrap">
|
|
<span class="material-icons input-icon">alternate_email</span>
|
|
<input
|
|
id="reg-email"
|
|
type="email"
|
|
v-model="email"
|
|
placeholder="tu@correo.com"
|
|
required
|
|
autocomplete="email"
|
|
class="field-input"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Contraseña -->
|
|
<div class="field">
|
|
<label class="field-label" for="reg-password">Contraseña</label>
|
|
<div class="input-wrap">
|
|
<span class="material-icons input-icon">lock</span>
|
|
<input
|
|
id="reg-password"
|
|
:type="showPassword ? 'text' : 'password'"
|
|
v-model="password"
|
|
placeholder="Mínimo 8 caracteres"
|
|
required
|
|
minlength="8"
|
|
autocomplete="new-password"
|
|
class="field-input"
|
|
/>
|
|
<button
|
|
type="button"
|
|
class="toggle-pw"
|
|
@click="showPassword = !showPassword"
|
|
tabindex="-1"
|
|
>
|
|
<span class="material-icons">{{ showPassword ? 'visibility_off' : 'visibility' }}</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Error -->
|
|
<p v-if="errorMessage" class="error-msg">
|
|
<span class="material-icons error-icon">error_outline</span>
|
|
{{ errorMessage }}
|
|
</p>
|
|
|
|
<!-- Botón enviar -->
|
|
<button type="submit" class="submit-btn" :disabled="isLoading">
|
|
<span v-if="isLoading" class="btn-spinner"></span>
|
|
<span>{{ isLoading ? 'Creando cuenta...' : 'Crear Cuenta' }}</span>
|
|
</button>
|
|
</form>
|
|
|
|
<!-- Switch a login -->
|
|
<p class="switch-text">
|
|
¿Ya tienes cuenta?
|
|
<button type="button" class="switch-link" @click="emit('toggle')">Inicia sesión</button>
|
|
</p>
|
|
|
|
</template>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.register-form {
|
|
padding: 1.5rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
}
|
|
|
|
/* ─── Google ─── */
|
|
.google-btn {
|
|
width: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.75rem;
|
|
padding: 0.875rem;
|
|
background: var(--bg-primary);
|
|
border: 1.5px solid var(--border-color);
|
|
border-radius: 0.875rem;
|
|
color: var(--text-primary);
|
|
font-size: 0.9375rem;
|
|
font-weight: 700;
|
|
font-family: inherit;
|
|
cursor: pointer;
|
|
transition: border-color 0.2s, background 0.2s;
|
|
}
|
|
|
|
.google-btn:hover:not(:disabled) {
|
|
border-color: var(--active-color);
|
|
}
|
|
|
|
.google-btn:disabled {
|
|
opacity: 0.6;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
/* ─── Divider ─── */
|
|
.divider {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.divider-line {
|
|
flex: 1;
|
|
height: 1px;
|
|
background: var(--border-color);
|
|
}
|
|
|
|
.divider-text {
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
color: var(--text-secondary);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* ─── Éxito ─── */
|
|
.success-card {
|
|
background: rgba(74, 222, 128, 0.08);
|
|
border: 1px solid rgba(74, 222, 128, 0.2);
|
|
border-radius: 1rem;
|
|
padding: 2rem 1.5rem;
|
|
text-align: center;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.success-icon {
|
|
font-size: 3rem;
|
|
color: #4ade80;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.success-title {
|
|
font-size: 1.125rem;
|
|
font-weight: 800;
|
|
color: var(--text-primary);
|
|
margin: 0;
|
|
}
|
|
|
|
.success-desc {
|
|
font-size: 0.875rem;
|
|
color: var(--text-secondary);
|
|
margin: 0;
|
|
}
|
|
|
|
/* ─── Campos ─── */
|
|
.field {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.375rem;
|
|
}
|
|
|
|
.field + .field {
|
|
margin-top: 0.875rem;
|
|
}
|
|
|
|
.field-label {
|
|
font-size: 0.75rem;
|
|
font-weight: 700;
|
|
color: var(--text-secondary);
|
|
padding-left: 0.25rem;
|
|
}
|
|
|
|
.input-wrap {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.input-icon {
|
|
position: absolute;
|
|
left: 0.875rem;
|
|
font-size: 1.125rem;
|
|
color: var(--text-secondary);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.field-input {
|
|
width: 100%;
|
|
padding: 0.875rem 0.875rem 0.875rem 2.75rem;
|
|
background: var(--bg-primary);
|
|
border: 1.5px solid var(--border-color);
|
|
border-radius: 0.875rem;
|
|
color: var(--text-primary);
|
|
font-size: 0.9375rem;
|
|
font-weight: 500;
|
|
font-family: inherit;
|
|
outline: none;
|
|
transition: border-color 0.2s;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.field-input::placeholder {
|
|
color: var(--text-secondary);
|
|
opacity: 0.5;
|
|
}
|
|
|
|
.field-input:focus {
|
|
border-color: var(--active-color);
|
|
}
|
|
|
|
.toggle-pw {
|
|
position: absolute;
|
|
right: 0.875rem;
|
|
background: none;
|
|
border: none;
|
|
color: var(--text-secondary);
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0;
|
|
}
|
|
|
|
.toggle-pw .material-icons {
|
|
font-size: 1.125rem;
|
|
}
|
|
|
|
/* ─── Error ─── */
|
|
.error-msg {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
background: rgba(239, 68, 68, 0.1);
|
|
border: 1px solid rgba(239, 68, 68, 0.2);
|
|
border-radius: 0.75rem;
|
|
padding: 0.75rem 1rem;
|
|
color: #ef4444;
|
|
font-size: 0.875rem;
|
|
font-weight: 600;
|
|
margin: 0;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
.error-icon {
|
|
font-size: 1.125rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* ─── Botón ─── */
|
|
.submit-btn {
|
|
width: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.5rem;
|
|
padding: 1rem;
|
|
background: var(--active-color);
|
|
color: #101820;
|
|
border: none;
|
|
border-radius: 0.875rem;
|
|
font-size: 0.9375rem;
|
|
font-weight: 800;
|
|
font-family: inherit;
|
|
cursor: pointer;
|
|
transition: opacity 0.2s, transform 0.15s;
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
.submit-btn:disabled {
|
|
opacity: 0.7;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.submit-btn:not(:disabled):active {
|
|
transform: scale(0.98);
|
|
}
|
|
|
|
.btn-spinner {
|
|
width: 1rem;
|
|
height: 1rem;
|
|
border: 2px solid rgba(16, 24, 32, 0.3);
|
|
border-top-color: #101820;
|
|
border-radius: 50%;
|
|
animation: spin 0.7s linear infinite;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
|
|
/* ─── Switch ─── */
|
|
.switch-text {
|
|
text-align: center;
|
|
font-size: 0.8125rem;
|
|
font-weight: 600;
|
|
color: var(--text-secondary);
|
|
margin: 0;
|
|
}
|
|
|
|
.switch-link {
|
|
background: none;
|
|
border: none;
|
|
color: var(--active-color);
|
|
font-size: inherit;
|
|
font-weight: 700;
|
|
font-family: inherit;
|
|
cursor: pointer;
|
|
padding: 0;
|
|
margin-left: 0.25rem;
|
|
text-decoration: underline;
|
|
text-underline-offset: 2px;
|
|
}
|
|
</style>
|