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 React from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { ArrowLeft } from 'lucide-react';
|
import { ArrowLeft, Copy, RefreshCw } from 'lucide-react';
|
||||||
import { useDatabase } from '../../../hooks/useDatabase';
|
import { useDatabase } from '../../../hooks/useDatabase';
|
||||||
import { supabase } from '../../../lib/supabase';
|
import { supabase } from '../../../lib/supabase';
|
||||||
import { sendStudentCredentialsEmail } from '../../../services/email';
|
import { sendStudentCredentialsEmail } from '../../../services/email';
|
||||||
|
import { generateMnemonicPassword } from '../../../utils/passwordGenerator';
|
||||||
|
|
||||||
interface Class {
|
interface Class {
|
||||||
id: string;
|
id: string;
|
||||||
@ -22,8 +23,10 @@ export function AddStudentPage() {
|
|||||||
guardian_name: '',
|
guardian_name: '',
|
||||||
guardian_email: '',
|
guardian_email: '',
|
||||||
guardian_phone: '',
|
guardian_phone: '',
|
||||||
|
password: generateMnemonicPassword()
|
||||||
});
|
});
|
||||||
const [formError, setFormError] = React.useState<string | null>(null);
|
const [formError, setFormError] = React.useState<string | null>(null);
|
||||||
|
const [successMessage, setSuccessMessage] = React.useState<string | null>(null);
|
||||||
const [isLoading, setIsLoading] = React.useState(false);
|
const [isLoading, setIsLoading] = React.useState(false);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@ -56,6 +59,23 @@ export function AddStudentPage() {
|
|||||||
fetchClasses();
|
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) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
@ -75,13 +95,10 @@ export function AddStudentPage() {
|
|||||||
|
|
||||||
if (schoolError) throw schoolError;
|
if (schoolError) throw schoolError;
|
||||||
|
|
||||||
// Gerar senha temporária
|
// Criar usuário para o aluno com a senha mnemônica
|
||||||
const tempPassword = generateTempPassword();
|
|
||||||
|
|
||||||
// Criar usuário para o aluno
|
|
||||||
const { data: userData, error: userError } = await supabase.auth.signUp({
|
const { data: userData, error: userError } = await supabase.auth.signUp({
|
||||||
email: formData.email,
|
email: formData.email,
|
||||||
password: tempPassword,
|
password: formData.password,
|
||||||
options: {
|
options: {
|
||||||
data: {
|
data: {
|
||||||
role: 'student',
|
role: 'student',
|
||||||
@ -116,7 +133,7 @@ export function AddStudentPage() {
|
|||||||
const emailSent = await sendStudentCredentialsEmail({
|
const emailSent = await sendStudentCredentialsEmail({
|
||||||
studentName: formData.name,
|
studentName: formData.name,
|
||||||
studentEmail: formData.email,
|
studentEmail: formData.email,
|
||||||
password: tempPassword,
|
password: formData.password,
|
||||||
guardianName: formData.guardian_name,
|
guardianName: formData.guardian_name,
|
||||||
guardianEmail: formData.guardian_email
|
guardianEmail: formData.guardian_email
|
||||||
});
|
});
|
||||||
@ -186,6 +203,47 @@ export function AddStudentPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</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>
|
<div>
|
||||||
<label htmlFor="class_id" className="block text-sm font-medium text-gray-700">
|
<label htmlFor="class_id" className="block text-sm font-medium text-gray-700">
|
||||||
Turma
|
Turma
|
||||||
@ -270,14 +328,3 @@ 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;
|
|
||||||
}
|
|
||||||
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