mirror of
https://github.com/lucasrcsantana/story-generator.git
synced 2025-12-17 05:47:52 +00:00
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:
parent
5952d83ec8
commit
beef3da647
@ -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
|
||||
@ -270,14 +328,3 @@ export function AddStudentPage() {
|
||||
</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;
|
||||
}
|
||||
5
src/pages/student-dashboard/index.ts
Normal file
5
src/pages/student-dashboard/index.ts
Normal 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';
|
||||
12
src/utils/passwordGenerator.ts
Normal file
12
src/utils/passwordGenerator.ts
Normal 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}`;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user