refactor: enhance password change page with improved UI and functionality
- Updated the layout and styling of the password change page for a more modern and user-friendly experience. - Integrated new icons and visual elements to enhance the overall design and accessibility. - Improved form handling with better loading states and error messages for user feedback. - Added security tips and password requirements to guide users during the password change process.
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { resolve } from '$app/paths';
|
import { resolve } from '$app/paths';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
|
import { Key, Eye, EyeOff, CheckCircle2, XCircle, Shield, Lock, AlertCircle, Info } from 'lucide-svelte';
|
||||||
|
|
||||||
const convex = useConvexClient();
|
const convex = useConvexClient();
|
||||||
const currentUser = useQuery(api.auth.getCurrentUser, {});
|
const currentUser = useQuery(api.auth.getCurrentUser, {});
|
||||||
@@ -136,361 +137,288 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main class="container mx-auto max-w-2xl px-4 py-8">
|
<main class="container mx-auto max-w-4xl px-4 py-8">
|
||||||
<!-- Header -->
|
<!-- Header Moderno -->
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<div class="mb-2 flex items-center gap-3">
|
<div class="bg-linear-to-r from-primary/20 via-primary/10 to-primary/20 rounded-2xl p-6 mb-6 shadow-lg border border-primary/20">
|
||||||
<svg
|
<div class="flex items-center gap-4">
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
<div class="p-3 bg-primary/20 rounded-2xl">
|
||||||
class="text-primary h-10 w-10"
|
<Key class="h-8 w-8 text-primary" strokeWidth={2.5} />
|
||||||
fill="none"
|
</div>
|
||||||
viewBox="0 0 24 24"
|
<div class="flex-1">
|
||||||
stroke="currentColor"
|
<h1 class="text-primary text-4xl font-bold mb-2">Alterar Senha</h1>
|
||||||
>
|
<p class="text-base-content/70 text-lg">
|
||||||
<path
|
Atualize sua senha de acesso ao sistema de forma segura
|
||||||
stroke-linecap="round"
|
</p>
|
||||||
stroke-linejoin="round"
|
</div>
|
||||||
stroke-width="2"
|
<div class="badge badge-primary badge-lg gap-2">
|
||||||
d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"
|
<Shield class="h-4 w-4" strokeWidth={2} />
|
||||||
/>
|
Seguro
|
||||||
</svg>
|
</div>
|
||||||
<h1 class="text-primary text-4xl font-bold">Alterar Senha</h1>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-base-content/70 text-lg">Atualize sua senha de acesso ao sistema</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Breadcrumbs -->
|
<!-- Breadcrumbs -->
|
||||||
<div class="breadcrumbs mb-6 text-sm">
|
<div class="breadcrumbs text-sm mb-6">
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href={resolve('/')}>Dashboard</a></li>
|
<li><a href={resolve('/')} class="link link-hover">Dashboard</a></li>
|
||||||
<li>Alterar Senha</li>
|
<li>Alterar Senha</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Alertas -->
|
<!-- Alertas -->
|
||||||
{#if notice}
|
{#if notice}
|
||||||
<div class="alert {notice.type === 'success' ? 'alert-success' : 'alert-error'} mb-6 shadow-lg">
|
<div
|
||||||
<svg
|
class="alert {notice.type === 'success'
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
? 'alert-success'
|
||||||
class="h-6 w-6 shrink-0 stroke-current"
|
: 'alert-error'} mb-6 shadow-xl animate-in fade-in slide-in-from-top duration-300"
|
||||||
fill="none"
|
>
|
||||||
viewBox="0 0 24 24"
|
{#if notice.type === 'success'}
|
||||||
>
|
<CheckCircle2 class="h-6 w-6 shrink-0 stroke-current" strokeWidth={2} />
|
||||||
{#if notice.type === 'success'}
|
{:else}
|
||||||
<path
|
<XCircle class="h-6 w-6 shrink-0 stroke-current" strokeWidth={2} />
|
||||||
stroke-linecap="round"
|
{/if}
|
||||||
stroke-linejoin="round"
|
<span class="font-semibold">{notice.message}</span>
|
||||||
stroke-width="2"
|
|
||||||
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
||||||
/>
|
|
||||||
{:else}
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</svg>
|
|
||||||
<span>{notice.message}</span>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- Formulário -->
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||||
<div class="card bg-base-100 border-base-300 border shadow-xl">
|
<!-- Formulário Principal -->
|
||||||
<div class="card-body">
|
<div class="lg:col-span-2">
|
||||||
<form onsubmit={handleSubmit} class="space-y-6">
|
<div
|
||||||
<!-- Senha Atual -->
|
class="card bg-base-100 border-base-300/50 border-2 shadow-xl hover:shadow-2xl transition-all duration-300"
|
||||||
<div class="form-control">
|
>
|
||||||
<label class="label" for="senha-atual">
|
<div class="card-body p-8">
|
||||||
<span class="label-text font-semibold">Senha Atual</span>
|
<div class="flex items-center gap-3 mb-6">
|
||||||
<span class="label-text-alt text-error">*</span>
|
<div class="p-2 bg-primary/10 rounded-xl">
|
||||||
</label>
|
<Lock class="h-6 w-6 text-primary" strokeWidth={2} />
|
||||||
<div class="relative">
|
</div>
|
||||||
<input
|
<h2 class="text-2xl font-bold text-base-content">Formulário de Alteração</h2>
|
||||||
id="senha-atual"
|
|
||||||
type={mostrarSenhaAtual ? 'text' : 'password'}
|
|
||||||
placeholder="Digite sua senha atual"
|
|
||||||
class="input input-bordered input-primary w-full pr-12"
|
|
||||||
bind:value={senhaAtual}
|
|
||||||
required
|
|
||||||
disabled={carregando}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-sm btn-circle absolute top-1/2 right-3 -translate-y-1/2"
|
|
||||||
onclick={() => (mostrarSenhaAtual = !mostrarSenhaAtual)}
|
|
||||||
>
|
|
||||||
{#if mostrarSenhaAtual}
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-5 w-5"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
{:else}
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-5 w-5"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
{/if}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Nova Senha -->
|
<form onsubmit={handleSubmit} class="space-y-6">
|
||||||
<div class="form-control">
|
<!-- Senha Atual -->
|
||||||
<label class="label" for="nova-senha">
|
<div class="form-control">
|
||||||
<span class="label-text font-semibold">Nova Senha</span>
|
<label class="label" for="senha-atual">
|
||||||
<span class="label-text-alt text-error">*</span>
|
<span class="label-text font-semibold text-base">Senha Atual</span>
|
||||||
</label>
|
<span class="label-text-alt text-error font-bold">*</span>
|
||||||
<div class="relative">
|
</label>
|
||||||
<input
|
<div class="relative">
|
||||||
id="nova-senha"
|
<input
|
||||||
type={mostrarNovaSenha ? 'text' : 'password'}
|
id="senha-atual"
|
||||||
placeholder="Digite sua nova senha"
|
type={mostrarSenhaAtual ? 'text' : 'password'}
|
||||||
class="input input-bordered input-primary w-full pr-12"
|
placeholder="Digite sua senha atual"
|
||||||
bind:value={novaSenha}
|
class="input input-bordered input-primary w-full pr-12 h-12 text-base"
|
||||||
required
|
bind:value={senhaAtual}
|
||||||
disabled={carregando}
|
required
|
||||||
/>
|
disabled={carregando}
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-sm btn-circle absolute top-1/2 right-3 -translate-y-1/2"
|
|
||||||
onclick={() => (mostrarNovaSenha = !mostrarNovaSenha)}
|
|
||||||
>
|
|
||||||
{#if mostrarNovaSenha}
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-5 w-5"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
{:else}
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-5 w-5"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
{/if}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="label">
|
|
||||||
<span class="label-text-alt text-base-content/60">
|
|
||||||
Mínimo 8 caracteres, com letras maiúsculas, minúsculas, números e caracteres especiais
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Confirmar Senha -->
|
|
||||||
<div class="form-control">
|
|
||||||
<label class="label" for="confirmar-senha">
|
|
||||||
<span class="label-text font-semibold">Confirmar Nova Senha</span>
|
|
||||||
<span class="label-text-alt text-error">*</span>
|
|
||||||
</label>
|
|
||||||
<div class="relative">
|
|
||||||
<input
|
|
||||||
id="confirmar-senha"
|
|
||||||
type={mostrarConfirmarSenha ? 'text' : 'password'}
|
|
||||||
placeholder="Digite novamente sua nova senha"
|
|
||||||
class="input input-bordered input-primary w-full pr-12"
|
|
||||||
bind:value={confirmarSenha}
|
|
||||||
required
|
|
||||||
disabled={carregando}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-sm btn-circle absolute top-1/2 right-3 -translate-y-1/2"
|
|
||||||
onclick={() => (mostrarConfirmarSenha = !mostrarConfirmarSenha)}
|
|
||||||
>
|
|
||||||
{#if mostrarConfirmarSenha}
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-5 w-5"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
{:else}
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-5 w-5"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
{/if}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Requisitos de Senha -->
|
|
||||||
<div class="alert alert-info">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-6 w-6 shrink-0 stroke-current"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<div>
|
|
||||||
<h3 class="font-bold">Requisitos de Senha:</h3>
|
|
||||||
<ul class="mt-2 list-inside list-disc space-y-1 text-sm">
|
|
||||||
<li>Mínimo de 8 caracteres</li>
|
|
||||||
<li>Pelo menos uma letra maiúscula (A-Z)</li>
|
|
||||||
<li>Pelo menos uma letra minúscula (a-z)</li>
|
|
||||||
<li>Pelo menos um número (0-9)</li>
|
|
||||||
<li>Pelo menos um caractere especial (!@#$%^&*...)</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Botões -->
|
|
||||||
<div class="mt-8 flex justify-end gap-4">
|
|
||||||
<button type="button" class="btn" onclick={cancelar} disabled={carregando}>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-5 w-5"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M6 18L18 6M6 6l12 12"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Cancelar
|
|
||||||
</button>
|
|
||||||
<button type="submit" class="btn btn-primary" disabled={carregando}>
|
|
||||||
{#if carregando}
|
|
||||||
<span class="loading loading-spinner loading-sm"></span>
|
|
||||||
Alterando...
|
|
||||||
{:else}
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-5 w-5"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M5 13l4 4L19 7"
|
|
||||||
/>
|
/>
|
||||||
</svg>
|
<button
|
||||||
Alterar Senha
|
type="button"
|
||||||
{/if}
|
class="btn btn-sm btn-ghost btn-circle absolute top-1/2 right-2 -translate-y-1/2 hover:bg-primary/10"
|
||||||
</button>
|
onclick={() => (mostrarSenhaAtual = !mostrarSenhaAtual)}
|
||||||
</div>
|
disabled={carregando}
|
||||||
</form>
|
aria-label={mostrarSenhaAtual ? 'Ocultar senha' : 'Mostrar senha'}
|
||||||
</div>
|
>
|
||||||
</div>
|
{#if mostrarSenhaAtual}
|
||||||
|
<EyeOff class="h-5 w-5 text-base-content/60" strokeWidth={2} />
|
||||||
|
{:else}
|
||||||
|
<Eye class="h-5 w-5 text-base-content/60" strokeWidth={2} />
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Dicas de Segurança -->
|
<!-- Nova Senha -->
|
||||||
<div class="card bg-base-200 mt-6 shadow-lg">
|
<div class="form-control">
|
||||||
<div class="card-body">
|
<label class="label" for="nova-senha">
|
||||||
<h3 class="card-title text-lg">
|
<span class="label-text font-semibold text-base">Nova Senha</span>
|
||||||
<svg
|
<span class="label-text-alt text-error font-bold">*</span>
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
</label>
|
||||||
class="text-warning h-6 w-6"
|
<div class="relative">
|
||||||
fill="none"
|
<input
|
||||||
viewBox="0 0 24 24"
|
id="nova-senha"
|
||||||
stroke="currentColor"
|
type={mostrarNovaSenha ? 'text' : 'password'}
|
||||||
>
|
placeholder="Digite sua nova senha"
|
||||||
<path
|
class="input input-bordered input-primary w-full pr-12 h-12 text-base"
|
||||||
stroke-linecap="round"
|
bind:value={novaSenha}
|
||||||
stroke-linejoin="round"
|
required
|
||||||
stroke-width="2"
|
disabled={carregando}
|
||||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
/>
|
||||||
/>
|
<button
|
||||||
</svg>
|
type="button"
|
||||||
Dicas de Segurança
|
class="btn btn-sm btn-ghost btn-circle absolute top-1/2 right-2 -translate-y-1/2 hover:bg-primary/10"
|
||||||
</h3>
|
onclick={() => (mostrarNovaSenha = !mostrarNovaSenha)}
|
||||||
<ul class="text-base-content/70 space-y-2 text-sm">
|
disabled={carregando}
|
||||||
<li>✅ Nunca compartilhe sua senha com ninguém</li>
|
aria-label={mostrarNovaSenha ? 'Ocultar senha' : 'Mostrar senha'}
|
||||||
<li>✅ Use uma senha única para cada sistema</li>
|
>
|
||||||
<li>✅ Altere sua senha regularmente</li>
|
{#if mostrarNovaSenha}
|
||||||
<li>✅ Não use informações pessoais óbvias (nome, data de nascimento, etc.)</li>
|
<EyeOff class="h-5 w-5 text-base-content/60" strokeWidth={2} />
|
||||||
<li>✅ Considere usar um gerenciador de senhas</li>
|
{:else}
|
||||||
</ul>
|
<Eye class="h-5 w-5 text-base-content/60" strokeWidth={2} />
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="label">
|
||||||
|
<span class="label-text-alt text-base-content/60 text-xs">
|
||||||
|
Mínimo 8 caracteres com maiúsculas, minúsculas, números e especiais
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Confirmar Senha -->
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label" for="confirmar-senha">
|
||||||
|
<span class="label-text font-semibold text-base">Confirmar Nova Senha</span>
|
||||||
|
<span class="label-text-alt text-error font-bold">*</span>
|
||||||
|
</label>
|
||||||
|
<div class="relative">
|
||||||
|
<input
|
||||||
|
id="confirmar-senha"
|
||||||
|
type={mostrarConfirmarSenha ? 'text' : 'password'}
|
||||||
|
placeholder="Digite novamente sua nova senha"
|
||||||
|
class="input input-bordered input-primary w-full pr-12 h-12 text-base"
|
||||||
|
bind:value={confirmarSenha}
|
||||||
|
required
|
||||||
|
disabled={carregando}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm btn-ghost btn-circle absolute top-1/2 right-2 -translate-y-1/2 hover:bg-primary/10"
|
||||||
|
onclick={() => (mostrarConfirmarSenha = !mostrarConfirmarSenha)}
|
||||||
|
disabled={carregando}
|
||||||
|
aria-label={mostrarConfirmarSenha ? 'Ocultar senha' : 'Mostrar senha'}
|
||||||
|
>
|
||||||
|
{#if mostrarConfirmarSenha}
|
||||||
|
<EyeOff class="h-5 w-5 text-base-content/60" strokeWidth={2} />
|
||||||
|
{:else}
|
||||||
|
<Eye class="h-5 w-5 text-base-content/60" strokeWidth={2} />
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Botões -->
|
||||||
|
<div class="flex flex-col sm:flex-row justify-end gap-4 mt-8 pt-6 border-t border-base-300">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline btn-lg flex-1 sm:flex-initial"
|
||||||
|
onclick={cancelar}
|
||||||
|
disabled={carregando}
|
||||||
|
>
|
||||||
|
<XCircle class="h-5 w-5" strokeWidth={2} />
|
||||||
|
Cancelar
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="btn btn-primary btn-lg flex-1 sm:flex-initial shadow-lg hover:shadow-xl transition-all duration-200"
|
||||||
|
disabled={carregando}
|
||||||
|
>
|
||||||
|
{#if carregando}
|
||||||
|
<span class="loading loading-spinner loading-sm"></span>
|
||||||
|
Alterando...
|
||||||
|
{:else}
|
||||||
|
<CheckCircle2 class="h-5 w-5" strokeWidth={2} />
|
||||||
|
Alterar Senha
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sidebar com Informações -->
|
||||||
|
<div class="space-y-6">
|
||||||
|
<!-- Requisitos de Senha -->
|
||||||
|
<div
|
||||||
|
class="card bg-linear-to-br from-info/10 to-info/5 border-2 border-info/20 shadow-lg"
|
||||||
|
>
|
||||||
|
<div class="card-body p-6">
|
||||||
|
<div class="flex items-center gap-3 mb-4">
|
||||||
|
<div class="p-2 bg-info/20 rounded-xl">
|
||||||
|
<Info class="h-6 w-6 text-info" strokeWidth={2} />
|
||||||
|
</div>
|
||||||
|
<h3 class="text-lg font-bold text-base-content">Requisitos de Senha</h3>
|
||||||
|
</div>
|
||||||
|
<ul class="space-y-3 text-sm">
|
||||||
|
<li class="flex items-start gap-2">
|
||||||
|
<CheckCircle2 class="h-5 w-5 text-success shrink-0 mt-0.5" strokeWidth={2} />
|
||||||
|
<span class="text-base-content/80">Mínimo de 8 caracteres</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-start gap-2">
|
||||||
|
<CheckCircle2 class="h-5 w-5 text-success shrink-0 mt-0.5" strokeWidth={2} />
|
||||||
|
<span class="text-base-content/80">Pelo menos uma letra maiúscula (A-Z)</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-start gap-2">
|
||||||
|
<CheckCircle2 class="h-5 w-5 text-success shrink-0 mt-0.5" strokeWidth={2} />
|
||||||
|
<span class="text-base-content/80">Pelo menos uma letra minúscula (a-z)</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-start gap-2">
|
||||||
|
<CheckCircle2 class="h-5 w-5 text-success shrink-0 mt-0.5" strokeWidth={2} />
|
||||||
|
<span class="text-base-content/80">Pelo menos um número (0-9)</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-start gap-2">
|
||||||
|
<CheckCircle2 class="h-5 w-5 text-success shrink-0 mt-0.5" strokeWidth={2} />
|
||||||
|
<span class="text-base-content/80">Pelo menos um caractere especial (!@#$%...)</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Dicas de Segurança -->
|
||||||
|
<div
|
||||||
|
class="card bg-linear-to-br from-warning/10 to-warning/5 border-2 border-warning/20 shadow-lg"
|
||||||
|
>
|
||||||
|
<div class="card-body p-6">
|
||||||
|
<div class="flex items-center gap-3 mb-4">
|
||||||
|
<div class="p-2 bg-warning/20 rounded-xl">
|
||||||
|
<Shield class="h-6 w-6 text-warning" strokeWidth={2} />
|
||||||
|
</div>
|
||||||
|
<h3 class="text-lg font-bold text-base-content">Dicas de Segurança</h3>
|
||||||
|
</div>
|
||||||
|
<ul class="space-y-3 text-sm">
|
||||||
|
<li class="flex items-start gap-2">
|
||||||
|
<AlertCircle class="h-5 w-5 text-warning shrink-0 mt-0.5" strokeWidth={2} />
|
||||||
|
<span class="text-base-content/80">Nunca compartilhe sua senha</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-start gap-2">
|
||||||
|
<AlertCircle class="h-5 w-5 text-warning shrink-0 mt-0.5" strokeWidth={2} />
|
||||||
|
<span class="text-base-content/80">Use uma senha única para cada sistema</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-start gap-2">
|
||||||
|
<AlertCircle class="h-5 w-5 text-warning shrink-0 mt-0.5" strokeWidth={2} />
|
||||||
|
<span class="text-base-content/80">Altere sua senha regularmente</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-start gap-2">
|
||||||
|
<AlertCircle class="h-5 w-5 text-warning shrink-0 mt-0.5" strokeWidth={2} />
|
||||||
|
<span class="text-base-content/80">Evite informações pessoais óbvias</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-start gap-2">
|
||||||
|
<AlertCircle class="h-5 w-5 text-warning shrink-0 mt-0.5" strokeWidth={2} />
|
||||||
|
<span class="text-base-content/80">Considere usar um gerenciador de senhas</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@keyframes fade-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-in {
|
||||||
|
animation: fade-in 0.3s ease-out;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
2
packages/backend/convex/_generated/api.d.ts
vendored
2
packages/backend/convex/_generated/api.d.ts
vendored
@@ -15,6 +15,7 @@ import type * as actions_smtp from "../actions/smtp.js";
|
|||||||
import type * as actions_utils_nodeCrypto from "../actions/utils/nodeCrypto.js";
|
import type * as actions_utils_nodeCrypto from "../actions/utils/nodeCrypto.js";
|
||||||
import type * as atestadosLicencas from "../atestadosLicencas.js";
|
import type * as atestadosLicencas from "../atestadosLicencas.js";
|
||||||
import type * as ausencias from "../ausencias.js";
|
import type * as ausencias from "../ausencias.js";
|
||||||
|
import type * as autenticacao from "../autenticacao.js";
|
||||||
import type * as auth from "../auth.js";
|
import type * as auth from "../auth.js";
|
||||||
import type * as auth_utils from "../auth/utils.js";
|
import type * as auth_utils from "../auth/utils.js";
|
||||||
import type * as chamados from "../chamados.js";
|
import type * as chamados from "../chamados.js";
|
||||||
@@ -63,6 +64,7 @@ declare const fullApi: ApiFromModules<{
|
|||||||
"actions/utils/nodeCrypto": typeof actions_utils_nodeCrypto;
|
"actions/utils/nodeCrypto": typeof actions_utils_nodeCrypto;
|
||||||
atestadosLicencas: typeof atestadosLicencas;
|
atestadosLicencas: typeof atestadosLicencas;
|
||||||
ausencias: typeof ausencias;
|
ausencias: typeof ausencias;
|
||||||
|
autenticacao: typeof autenticacao;
|
||||||
auth: typeof auth;
|
auth: typeof auth;
|
||||||
"auth/utils": typeof auth_utils;
|
"auth/utils": typeof auth_utils;
|
||||||
chamados: typeof chamados;
|
chamados: typeof chamados;
|
||||||
|
|||||||
72
packages/backend/convex/autenticacao.ts
Normal file
72
packages/backend/convex/autenticacao.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { mutation } from './_generated/server';
|
||||||
|
import { v } from 'convex/values';
|
||||||
|
import { updatePassword } from './auth';
|
||||||
|
import { authComponent } from './auth';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alterar senha do usuário autenticado
|
||||||
|
*/
|
||||||
|
export const alterarSenha = mutation({
|
||||||
|
args: {
|
||||||
|
token: v.string(), // Token não é usado, mas mantido para compatibilidade
|
||||||
|
senhaAtual: v.string(),
|
||||||
|
novaSenha: v.string()
|
||||||
|
},
|
||||||
|
returns: v.union(
|
||||||
|
v.object({ sucesso: v.literal(true) }),
|
||||||
|
v.object({ sucesso: v.literal(false), erro: v.string() })
|
||||||
|
),
|
||||||
|
handler: async (ctx, args) => {
|
||||||
|
try {
|
||||||
|
// Verificar se o usuário está autenticado
|
||||||
|
const authUser = await authComponent.safeGetAuthUser(ctx);
|
||||||
|
if (!authUser) {
|
||||||
|
return {
|
||||||
|
sucesso: false as const,
|
||||||
|
erro: 'Usuário não autenticado'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validar que a nova senha não está vazia
|
||||||
|
if (!args.novaSenha || args.novaSenha.trim().length === 0) {
|
||||||
|
return {
|
||||||
|
sucesso: false as const,
|
||||||
|
erro: 'A nova senha não pode estar vazia'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chamar a função de atualização de senha
|
||||||
|
await updatePassword(ctx, {
|
||||||
|
currentPassword: args.senhaAtual,
|
||||||
|
newPassword: args.novaSenha
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
sucesso: true as const
|
||||||
|
};
|
||||||
|
} catch (error: any) {
|
||||||
|
// Capturar erros específicos do Better Auth
|
||||||
|
let mensagemErro = 'Erro ao alterar senha';
|
||||||
|
|
||||||
|
if (error?.message) {
|
||||||
|
mensagemErro = error.message;
|
||||||
|
} else if (typeof error === 'string') {
|
||||||
|
mensagemErro = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mensagens de erro mais amigáveis
|
||||||
|
if (mensagemErro.toLowerCase().includes('password') ||
|
||||||
|
mensagemErro.toLowerCase().includes('senha') ||
|
||||||
|
mensagemErro.toLowerCase().includes('incorrect') ||
|
||||||
|
mensagemErro.toLowerCase().includes('incorreta')) {
|
||||||
|
mensagemErro = 'Senha atual incorreta';
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
sucesso: false as const,
|
||||||
|
erro: mensagemErro
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Reference in New Issue
Block a user