feat: Permettre la génération et l'envoi de codes d'invitation aux parents
Les administrateurs ont besoin d'un moyen simple pour inviter les parents à rejoindre la plateforme. Cette fonctionnalité permet de générer des codes d'invitation uniques (8 caractères alphanumériques) avec une validité de 48h, de les envoyer par email, et de les activer via une page publique dédiée qui crée automatiquement le compte parent. L'interface d'administration offre l'envoi unitaire et en masse, le renvoi, le filtrage par statut, ainsi que la visualisation de l'état de chaque invitation (en attente, activée, expirée).
This commit is contained in:
@@ -31,6 +31,11 @@
|
||||
<span class="action-label">Gérer les utilisateurs</span>
|
||||
<span class="action-hint">Inviter et gérer</span>
|
||||
</a>
|
||||
<a class="action-card" href="/admin/parent-invitations">
|
||||
<span class="action-icon">✉️</span>
|
||||
<span class="action-label">Invitations parents</span>
|
||||
<span class="action-hint">Codes d'invitation</span>
|
||||
</a>
|
||||
<a class="action-card" href="/admin/classes">
|
||||
<span class="action-icon">🏫</span>
|
||||
<span class="action-label">Configurer les classes</span>
|
||||
|
||||
104
frontend/src/lib/features/import/api/parentInvitationImport.ts
Normal file
104
frontend/src/lib/features/import/api/parentInvitationImport.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import { getApiBaseUrl } from '$lib/api';
|
||||
import { authenticatedFetch } from '$lib/auth';
|
||||
|
||||
// === Types ===
|
||||
|
||||
export interface AnalyzeResult {
|
||||
columns: string[];
|
||||
rows: Record<string, string>[];
|
||||
totalRows: number;
|
||||
filename: string;
|
||||
suggestedMapping: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface ValidatedRow {
|
||||
studentName: string;
|
||||
email1: string;
|
||||
email2: string;
|
||||
studentId: string | null;
|
||||
studentMatch: string | null;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
export interface ValidateResult {
|
||||
validatedRows: ValidatedRow[];
|
||||
validCount: number;
|
||||
errorCount: number;
|
||||
}
|
||||
|
||||
export interface BulkResult {
|
||||
created: number;
|
||||
errors: { line: number; email?: string; error: string }[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
// === API Functions ===
|
||||
|
||||
/**
|
||||
* Upload et analyse un fichier CSV ou XLSX pour l'import d'invitations parents.
|
||||
*/
|
||||
export async function analyzeFile(file: File): Promise<AnalyzeResult> {
|
||||
const apiUrl = getApiBaseUrl();
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
const response = await authenticatedFetch(`${apiUrl}/import/parents/analyze`, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const data = await response.json().catch(() => null);
|
||||
throw new Error(
|
||||
data?.['hydra:description'] ?? data?.message ?? data?.detail ?? "Erreur lors de l'analyse du fichier"
|
||||
);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Valide les lignes mappées contre les élèves existants.
|
||||
*/
|
||||
export async function validateRows(
|
||||
rows: { studentName: string; email1: string; email2: string }[]
|
||||
): Promise<ValidateResult> {
|
||||
const apiUrl = getApiBaseUrl();
|
||||
const response = await authenticatedFetch(`${apiUrl}/import/parents/validate`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ rows })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const data = await response.json().catch(() => null);
|
||||
throw new Error(
|
||||
data?.['hydra:description'] ?? data?.message ?? data?.detail ?? 'Erreur lors de la validation'
|
||||
);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie les invitations en masse via l'endpoint bulk existant.
|
||||
*/
|
||||
export async function sendBulkInvitations(
|
||||
invitations: { studentId: string; parentEmail: string }[]
|
||||
): Promise<BulkResult> {
|
||||
const apiUrl = getApiBaseUrl();
|
||||
const response = await authenticatedFetch(`${apiUrl}/parent-invitations/bulk`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ invitations })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const data = await response.json().catch(() => null);
|
||||
throw new Error(
|
||||
data?.['hydra:description'] ?? data?.message ?? data?.detail ?? "Erreur lors de l'envoi"
|
||||
);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
Reference in New Issue
Block a user