feat: adiciona geração de senha mnemônica no cadastro de alunos

- Implementa gerador de senhas mnemônicas (cor + animal + número)
- Adiciona campo de senha com opção de copiar e regenerar
- Remove geração de senha temporária aleatória
- Integra senha mnemônica com envio de email
- Adiciona feedback visual ao copiar senha
This commit is contained in:
Lucas Santana 2024-12-20 11:42:59 -03:00
parent 5952d83ec8
commit beef3da647
3 changed files with 82 additions and 18 deletions

View File

@ -1,9 +1,10 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import { ArrowLeft } from 'lucide-react';
import { ArrowLeft, Copy, RefreshCw } from 'lucide-react';
import { useDatabase } from '../../../hooks/useDatabase';
import { supabase } from '../../../lib/supabase';
import { sendStudentCredentialsEmail } from '../../../services/email';
import { generateMnemonicPassword } from '../../../utils/passwordGenerator';
interface Class {
id: string;
@ -22,8 +23,10 @@ export function AddStudentPage() {
guardian_name: '',
guardian_email: '',
guardian_phone: '',
password: generateMnemonicPassword()
});
const [formError, setFormError] = React.useState<string | null>(null);
const [successMessage, setSuccessMessage] = React.useState<string | null>(null);
const [isLoading, setIsLoading] = React.useState(false);
React.useEffect(() => {
@ -56,6 +59,23 @@ export function AddStudentPage() {
fetchClasses();
}, []);
const handleRegeneratePassword = () => {
setFormData(prev => ({
...prev,
password: generateMnemonicPassword()
}));
};
const handleCopyPassword = async () => {
try {
await navigator.clipboard.writeText(formData.password);
setSuccessMessage('Senha copiada!');
setTimeout(() => setSuccessMessage(null), 2000);
} catch (err) {
console.error('Erro ao copiar senha:', err);
}
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsLoading(true);
@ -75,13 +95,10 @@ export function AddStudentPage() {
if (schoolError) throw schoolError;
// Gerar senha temporária
const tempPassword = generateTempPassword();
// Criar usuário para o aluno
// Criar usuário para o aluno com a senha mnemônica
const { data: userData, error: userError } = await supabase.auth.signUp({
email: formData.email,
password: tempPassword,
password: formData.password,
options: {
data: {
role: 'student',
@ -116,7 +133,7 @@ export function AddStudentPage() {
const emailSent = await sendStudentCredentialsEmail({
studentName: formData.name,
studentEmail: formData.email,
password: tempPassword,
password: formData.password,
guardianName: formData.guardian_name,
guardianEmail: formData.guardian_email
});
@ -186,6 +203,47 @@ export function AddStudentPage() {
/>
</div>
<div>
<label htmlFor="password" className="block text-sm font-medium text-gray-700">
Senha de Acesso
</label>
<div className="mt-1 flex gap-2">
<div className="relative flex-1">
<input
type="text"
id="password"
value={formData.password}
readOnly
className="block w-full rounded-lg border-gray-300 shadow-sm focus:border-purple-500 focus:ring-purple-500 pr-10"
/>
<button
type="button"
onClick={handleCopyPassword}
className="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600"
>
<Copy className="h-5 w-5" />
</button>
</div>
<button
type="button"
onClick={handleRegeneratePassword}
className="p-2 text-gray-400 hover:text-gray-600 rounded-lg border border-gray-300"
title="Gerar nova senha"
>
<RefreshCw className="h-5 w-5" />
</button>
</div>
<p className="mt-1 text-sm text-gray-500">
Senha gerada automaticamente para fácil memorização
</p>
</div>
{successMessage && (
<div className="p-4 bg-green-50 text-green-600 rounded-lg">
{successMessage}
</div>
)}
<div>
<label htmlFor="class_id" className="block text-sm font-medium text-gray-700">
Turma
@ -269,15 +327,4 @@ export function AddStudentPage() {
</div>
</div>
);
}
// Função auxiliar para gerar senha temporária
function generateTempPassword() {
const length = 12;
const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*';
let password = '';
for (let i = 0; i < length; i++) {
password += charset.charAt(Math.floor(Math.random() * charset.length));
}
return password;
}

View File

@ -0,0 +1,5 @@
export { StudentDashboardLayout } from './StudentDashboardLayout';
export { StudentDashboardPage } from './StudentDashboardPage';
export { StudentStoriesPage } from './StudentStoriesPage';
export { CreateStoryPage } from './CreateStoryPage';
export { StoryPage } from './StoryPage';

View File

@ -0,0 +1,12 @@
const ANIMALS = ['leao', 'tigre', 'gato', 'cao', 'panda', 'urso', 'lobo', 'rato', 'sapo', 'peixe'];
const COLORS = ['azul', 'verde', 'rosa', 'roxo', 'ouro', 'prata', 'coral', 'jade', 'ruby', 'safira'];
const NUMBERS = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'];
export function generateMnemonicPassword(): string {
const randomAnimal = ANIMALS[Math.floor(Math.random() * ANIMALS.length)];
const randomColor = COLORS[Math.floor(Math.random() * COLORS.length)];
const randomNumber = NUMBERS[Math.floor(Math.random() * NUMBERS.length)] +
NUMBERS[Math.floor(Math.random() * NUMBERS.length)];
return `${randomColor}${randomAnimal}${randomNumber}`;
}