feat: add password reset functionality for users, including modal interface for generating temporary passwords and copying to clipboard; enhance backend mutation for secure password management and email notifications
This commit is contained in:
@@ -18,7 +18,9 @@
|
||||
Check,
|
||||
UserPlus,
|
||||
Search,
|
||||
MoreVertical
|
||||
MoreVertical,
|
||||
Copy,
|
||||
CheckCircle2
|
||||
} from 'lucide-svelte';
|
||||
|
||||
type AvisoUsuario = {
|
||||
@@ -189,6 +191,9 @@
|
||||
let buscaFuncionario = $state('');
|
||||
|
||||
let modalExcluirAberto = $state(false);
|
||||
let modalResetarSenhaAberto = $state(false);
|
||||
let novaSenhaGerada = $state<string | null>(null);
|
||||
let senhaCopiada = $state(false);
|
||||
let processando = $state(false);
|
||||
let mensagem = $state<Mensagem | null>(null);
|
||||
|
||||
@@ -525,6 +530,68 @@
|
||||
usuarioSelecionado = null;
|
||||
}
|
||||
|
||||
function abrirModalResetarSenha(usuario: Usuario) {
|
||||
usuarioSelecionado = usuario;
|
||||
novaSenhaGerada = null;
|
||||
modalResetarSenhaAberto = true;
|
||||
}
|
||||
|
||||
function fecharModalResetarSenha() {
|
||||
modalResetarSenhaAberto = false;
|
||||
novaSenhaGerada = null;
|
||||
senhaCopiada = false;
|
||||
usuarioSelecionado = null;
|
||||
}
|
||||
|
||||
async function copiarSenha() {
|
||||
if (!novaSenhaGerada) return;
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(novaSenhaGerada);
|
||||
senhaCopiada = true;
|
||||
setTimeout(() => {
|
||||
senhaCopiada = false;
|
||||
}, 2000);
|
||||
} catch (error) {
|
||||
console.error('Erro ao copiar senha:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function resetarSenha() {
|
||||
if (!usuarioSelecionado || !currentUser?.data) {
|
||||
console.error('Erro: usuarioSelecionado ou currentUser não definido');
|
||||
mostrarMensagem('error', 'Erro: dados do usuário não encontrados');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
processando = true;
|
||||
console.log('Iniciando reset de senha para:', usuarioSelecionado._id);
|
||||
|
||||
const resultado = await client.mutation(api.usuarios.resetarSenhaUsuario, {
|
||||
usuarioId: usuarioSelecionado._id,
|
||||
resetadoPorId: currentUser.data._id
|
||||
});
|
||||
|
||||
console.log('Resultado do reset:', resultado);
|
||||
|
||||
if (resultado && resultado.sucesso) {
|
||||
novaSenhaGerada = resultado.senhaTemporaria;
|
||||
mostrarMensagem('success', 'Senha resetada com sucesso! Email enviado ao usuário.');
|
||||
} else {
|
||||
const erroMsg = resultado?.erro || 'Erro ao resetar senha';
|
||||
console.error('Erro no reset:', erroMsg);
|
||||
mostrarMensagem('error', erroMsg);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erro ao resetar senha:', error);
|
||||
const errorMessage = error instanceof Error ? error.message : 'Erro desconhecido ao resetar senha';
|
||||
mostrarMensagem('error', errorMessage);
|
||||
} finally {
|
||||
processando = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function excluirUsuario() {
|
||||
if (!usuarioSelecionado) {
|
||||
return;
|
||||
@@ -912,6 +979,17 @@
|
||||
{usuario.funcionario ? 'Alterar Funcionário' : 'Associar Funcionário'}
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => abrirModalResetarSenha(usuario)}
|
||||
disabled={processando}
|
||||
class="text-warning"
|
||||
>
|
||||
Resetar Senha
|
||||
</button>
|
||||
</li>
|
||||
<div class="divider my-0"></div>
|
||||
<li>
|
||||
{#if usuario.bloqueado}
|
||||
<button
|
||||
@@ -1133,4 +1211,103 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Modal Resetar Senha -->
|
||||
{#if modalResetarSenhaAberto && usuarioSelecionado}
|
||||
<div class="modal modal-open">
|
||||
<div class="modal-box">
|
||||
<h3 class="mb-4 text-lg font-bold">Resetar Senha do Usuário</h3>
|
||||
|
||||
<div class="mb-4">
|
||||
<p class="text-base-content/80 mb-2">
|
||||
<strong>Usuário:</strong>
|
||||
{usuarioSelecionado.nome} ({usuarioSelecionado.matricula})
|
||||
</p>
|
||||
<p class="text-base-content/70 mb-4 text-sm">
|
||||
Uma nova senha temporária será gerada e enviada por email ao usuário.
|
||||
</p>
|
||||
|
||||
{#if novaSenhaGerada}
|
||||
<div class="alert alert-success mb-4">
|
||||
<CheckCircle class="h-6 w-6 shrink-0 stroke-current" strokeWidth={2} />
|
||||
<div class="flex-1 w-full">
|
||||
<p class="font-semibold text-base">Senha resetada com sucesso!</p>
|
||||
<p class="mt-3 text-sm font-medium">
|
||||
<strong>Nova senha temporária:</strong>
|
||||
</p>
|
||||
<div class="bg-base-200 border-base-300 mt-2 flex items-center justify-between gap-2 rounded-lg border p-4">
|
||||
<code class="text-xl font-mono font-bold select-all">{novaSenhaGerada}</code>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-ghost shrink-0"
|
||||
onclick={copiarSenha}
|
||||
aria-label="Copiar senha"
|
||||
title="Copiar senha"
|
||||
>
|
||||
{#if senhaCopiada}
|
||||
<CheckCircle2 class="h-5 w-5 text-success" strokeWidth={2} />
|
||||
{:else}
|
||||
<Copy class="h-5 w-5" strokeWidth={2} />
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
<p class="mt-3 text-xs text-base-content/70">
|
||||
✓ Esta senha foi enviada por email para <strong>{usuarioSelecionado.email}</strong>
|
||||
</p>
|
||||
<p class="mt-1 text-xs text-base-content/60">
|
||||
O usuário precisará alterar a senha no próximo login.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="alert alert-warning">
|
||||
<AlertTriangle class="h-6 w-6 shrink-0 stroke-current" strokeWidth={2} />
|
||||
<span>
|
||||
O usuário precisará alterar a senha no próximo login. Todas as sessões ativas serão encerradas.
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="modal-action">
|
||||
{#if novaSenhaGerada}
|
||||
<button type="button" class="btn btn-primary" onclick={fecharModalResetarSenha}>
|
||||
Fechar
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
type="button"
|
||||
class="btn"
|
||||
onclick={fecharModalResetarSenha}
|
||||
disabled={processando}
|
||||
>
|
||||
Cancelar
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-warning"
|
||||
onclick={resetarSenha}
|
||||
disabled={processando}
|
||||
>
|
||||
{#if processando}
|
||||
<span class="loading loading-spinner loading-sm"></span>
|
||||
{/if}
|
||||
Resetar Senha
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-backdrop">
|
||||
<button
|
||||
type="button"
|
||||
onclick={fecharModalResetarSenha}
|
||||
onkeydown={(e) => e.key === 'Escape' && fecharModalResetarSenha()}
|
||||
aria-label="Fechar modal"
|
||||
class="sr-only"
|
||||
>
|
||||
Fechar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</ProtectedRoute>
|
||||
|
||||
Reference in New Issue
Block a user