feat: Round 23 - Practice exercises, enhanced virtual character, inter-school federation, voice chat

- Practice exercises: AI-generated multiple choice questions, practice mode with score tracking
- Enhanced virtual character: More animations, accessories, AI chat integration, XP system
- Inter-school federation: School leaderboard, team scores, federation competitions
- Enhanced communication: Voice chat, file sharing, note sharing, teacher notes panel
- Added isFederation and federationSchedule fields to Competition model
- 170+ new i18n keys
- All lints pass
This commit is contained in:
Mustafa Cetin committed 2026-07-29 21:01:32 +00:00
1 parent 2f266a2143
commit 44ff170fca
12 files changed
+3655 -476

No files matched your search

+44 -1
View File
@@ -3110,11 +3110,13 @@ export interface CompetitionData {
rules: string | null;
isPublic: boolean;
isDemo: boolean;
isFederation: boolean;
federationSchedule: string | null;
createdById: string;
createdAt: string;
updatedAt: string;
deletedAt: string | null;
school?: { id: string; name: string };
school?: { id: string; name: string; primaryColor?: string | null };
createdBy?: { id: string; firstName: string; lastName: string };
subject?: { id: string; name: string } | null;
_count?: { participants: number; rewards: number };
@@ -3202,6 +3204,47 @@ export function fetchCompetitions(
return apiGet<{ competitions: CompetitionData[]; total: number }>(`/api/competitions?${params.toString()}`);
}
export interface FederationLeaderboardEntry {
schoolId: string;
schoolName: string;
teamScore: number;
participantCount: number;
rank: number;
}
export function fetchFederationCompetitions(
category?: string,
schedule?: string,
status?: string,
limit?: number,
offset?: number
): Promise<{ competitions: CompetitionData[]; total: number; schoolLeaderboard: FederationLeaderboardEntry[] }> {
const params = new URLSearchParams();
if (category) params.set('category', category);
if (schedule) params.set('schedule', schedule);
if (status) params.set('status', status);
if (limit) params.set('limit', String(limit));
if (offset) params.set('offset', String(offset));
return apiGet<{ competitions: CompetitionData[]; total: number; schoolLeaderboard: FederationLeaderboardEntry[] }>(`/api/competitions/federation?${params.toString()}`);
}
export function createFederationCompetition(data: {
schoolId: string;
title: string;
description?: string | null;
category: string;
subjectId?: string | null;
schedule: string;
startDate: string;
endDate: string;
registrationDeadline?: string | null;
maxParticipants?: number | null;
rules?: string | null;
isPublic?: boolean;
}): Promise<CompetitionData> {
return apiPost<CompetitionData>('/api/competitions/federation', data);
}
export function fetchPublicCompetitions(
limit?: number,
offset?: number