ValorSentimental / chatbot /src /views /PreferencesView.vue
iagofp's picture
Ajustes para primera validacion
b1fbbc5
<template>
<v-main class="pref-main">
<v-container class="pref-container" style="max-width:680px">
<!-- Topbar nav -->
<div class="pref-topbar">
<v-btn variant="text" size="small" prepend-icon="mdi-arrow-left" @click="router.back()">Volver</v-btn>
<div class="pref-brand">
<v-icon color="amber-darken-1" size="16" class="mr-1">mdi-cog-outline</v-icon>
<span class="pref-brand-text">Preferencias</span>
</div>
<v-btn variant="text" size="small" append-icon="mdi-chat-outline" to="/chat">Chat</v-btn>
</div>
<!-- Contraseña -->
<v-card class="pref-card" rounded="xl" elevation="0">
<v-card-item>
<template #prepend><v-icon color="primary" size="20">mdi-lock-outline</v-icon></template>
<v-card-title class="pref-stitle">Contraseña</v-card-title>
</v-card-item>
<v-card-text>
<div class="pwd-grid">
<v-text-field
v-model="oldPwd"
label="Contraseña actual"
:type="showOld ? 'text' : 'password'"
variant="outlined"
density="comfortable"
:append-inner-icon="showOld ? 'mdi-eye-off-outline' : 'mdi-eye-outline'"
hide-details
@click:append-inner="showOld = !showOld"
/>
<v-text-field
v-model="newPwd"
label="Nueva contraseña"
:type="showNew ? 'text' : 'password'"
variant="outlined"
density="comfortable"
:append-inner-icon="showNew ? 'mdi-eye-off-outline' : 'mdi-eye-outline'"
hide-details
@click:append-inner="showNew = !showNew"
/>
</div>
<v-alert v-if="pwdMsg.text" :type="pwdMsg.type" variant="tonal" density="compact" rounded="lg" class="mt-3">
{{ pwdMsg.text }}
</v-alert>
<v-btn
color="primary"
variant="flat"
rounded="lg"
class="mt-3"
:loading="pwdLoading"
:disabled="!oldPwd || !newPwd"
@click="changePassword"
>
Actualizar contraseña
</v-btn>
</v-card-text>
</v-card>
</v-container>
</v-main>
</template>
<script setup>
import { ref } from "vue";
import { useRouter } from "vue-router";
import API_BASE_URL from "../config.js";
const router = useRouter();
const oldPwd = ref("");
const newPwd = ref("");
const showOld = ref(false);
const showNew = ref(false);
const pwdLoading = ref(false);
const pwdMsg = ref({ text: "", type: "success" });
async function changePassword() {
pwdMsg.value = { text: "", type: "success" };
if (newPwd.value.length < 6) {
pwdMsg.value = { text: "La nueva contraseña debe tener al menos 6 caracteres.", type: "error" };
return;
}
pwdLoading.value = true;
try {
const res = await fetch(`${API_BASE_URL}/auth/password`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
token: localStorage.getItem("vs_token") || "",
old_password: oldPwd.value,
new_password: newPwd.value,
}),
});
const data = await res.json();
if (!res.ok) { pwdMsg.value = { text: data.error || "Error al actualizar.", type: "error" }; return; }
pwdMsg.value = { text: "Contraseña actualizada correctamente.", type: "success" };
oldPwd.value = newPwd.value = "";
} catch {
pwdMsg.value = { text: "Error de conexión.", type: "error" };
} finally {
pwdLoading.value = false;
}
}
</script>
<style scoped>
.pref-main { min-height: 100vh; }
.pref-container { padding: 24px 20px 48px; }
.pref-topbar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 28px;
}
.pref-brand { display: flex; align-items: center; }
.pref-brand-text { font-family: "Fraunces", serif; font-size: 1rem; font-weight: 700; color: var(--vs-text); }
.pref-card {
border: 1.5px solid var(--vs-border) !important;
background: var(--vs-panel) !important;
backdrop-filter: blur(12px);
}
.pref-stitle { font-size: 0.9rem !important; font-weight: 700 !important; color: var(--vs-text) !important; }
.pwd-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 540px) { .pwd-grid { grid-template-columns: 1fr; } }
</style>