feat(frontend): Phase 2/3 hardening release

Roadmap P0
- Ship /logo.svg fallback and rewire asset references (superseded later by
  the project-manager PNG in a separate commit).

Roadmap P1
- Response envelope alignment ({items,total,page,size}) across services
  and query hooks.
- Approval/ticket UX updates tied to the new backend rollback semantics.

Roadmap P2
- React.lazy + Vite manualChunks to split vendor-radix/charts/icons/forms
  from the main bundle and cut initial JS.
- Fix the 181 tsc errors (PaginationParams class + per-service generics).
- Auto-refresh flow for JWT refresh tokens wired into api-client.ts.

Roadmap P3
- i18n: i18next + language detector, en/ar locales, RTL auto-switch,
  LanguageToggle component in RoleLayout and AdminLmsLayout.
- GDPR: PrivacyCenter page for data export and right-to-erasure, backed
  by gdpr.service.ts; logout via AuthContext after erasure.
- Human-in-the-loop exam review UI: admin ExamReviewQueue + ExamReviewDetail
  pages, useExamReview hooks, exam-review.service.ts.
- AI quality loop: AIPromptEditor (versioning + render preview),
  AIFeedbackButtons for students, AIFeedbackTriage admin page, dedicated
  services, hooks, and types.
- Dark mode: ThemeProvider + ThemeToggle + chart color tokens.
- Accessibility: DialogDescription on every DialogContent; alert-dialog
  and sheet updates.
- Retire dead pages: ExamPage marketing, Index, ProfilePage import.
- Playwright e2e scaffolding: playwright.config.ts + e2e/login.spec.ts
  smoke tests, npm scripts test:e2e / test:e2e:install.

Made-with: Cursor
This commit is contained in:
Yamen Ahmad
2026-04-19 14:16:32 +04:00
parent 9b20d6ce66
commit b02c2e7526
75 changed files with 3727 additions and 551 deletions

85
src/i18n/locales/ar.ts Normal file
View File

@@ -0,0 +1,85 @@
import type { Translations } from "./en";
/** Arabic (RTL) translations. */
const ar: Translations = {
common: {
cancel: "إلغاء",
save: "حفظ",
delete: "حذف",
edit: "تعديل",
close: "إغلاق",
back: "رجوع",
next: "التالي",
search: "بحث",
loading: "جارٍ التحميل…",
error: "خطأ",
retry: "إعادة المحاولة",
yes: "نعم",
no: "لا",
actions: "إجراءات",
status: "الحالة",
settings: "الإعدادات",
signOut: "تسجيل الخروج",
},
auth: {
signIn: "تسجيل الدخول",
signInTitle: "تسجيل الدخول إلى EnCoach",
email: "البريد الإلكتروني",
password: "كلمة المرور",
forgotPassword: "هل نسيت كلمة المرور؟",
needAccount: "ليس لديك حساب؟",
signUp: "إنشاء حساب",
invalidCredentials: "البريد الإلكتروني أو كلمة المرور غير صحيحة",
},
nav: {
dashboard: "لوحة التحكم",
courses: "الدورات",
students: "الطلاب",
teachers: "المعلمون",
exams: "الاختبارات",
reports: "التقارير",
settings: "الإعدادات",
profile: "الملف الشخصي",
privacy: "مركز الخصوصية",
reviewQueue: "قائمة المراجعة",
aiPrompts: "تعليمات الذكاء الاصطناعي",
aiFeedback: "ملاحظات الذكاء الاصطناعي",
},
privacy: {
title: "مركز الخصوصية",
description:
"إدارة بياناتك الشخصية لدى EnCoach وفقاً للائحة حماية البيانات العامة وما يماثلها.",
exportTitle: "تنزيل بياناتك",
exportDescription:
"سنقوم بتجميع ملفك الشخصي، عضويات الكيانات، محاولات الاختبارات، الإجابات، ملاحظات الذكاء الاصطناعي، والتذاكر في ملف JSON واحد.",
exportButton: "تنزيل بياناتي",
exportPreparing: "جارٍ تحضير التصدير…",
exportSuccess: "تم تنزيل البيانات بنجاح",
deleteTitle: "حذف حسابي",
deleteDescription:
"سيتم إخفاء هوية ملفك الشخصي وإزالة الحقول الشخصية من سجلاتنا. سيتم الاحتفاظ بمحاولات الاختبارات (دون معلومات تعريفية) لسلامة الإحصائيات. لا يمكن التراجع عن هذا الإجراء.",
deleteButton: "محو حسابي",
deleteErasing: "جارٍ المحو…",
confirmTitle: "هل تريد محو حسابك؟",
confirmDescription:
"سنقوم بإخفاء هوية بياناتك الشخصية وتعطيل تسجيل الدخول. ستبقى التحليلات المجمعة لكنها لن تكون مرتبطة بك.",
confirmTypeDelete: "اكتب DELETE للتأكيد",
confirmErased: "تم محو حسابك. جارٍ تسجيل الخروج…",
},
feedback: {
thumbsUp: "إعجاب",
thumbsDown: "عدم إعجاب",
whatWentWrong: "ما الخطأ الذي حدث؟",
commentPlaceholder: "مثال: إجابة خاطئة، صياغة مربكة، خارج الموضوع…",
commentRequired: "من فضلك أخبرنا بالخطأ.",
submit: "إرسال الملاحظات",
},
theme: {
title: "المظهر",
light: "فاتح",
dark: "داكن",
system: "النظام",
},
};
export default ar;

97
src/i18n/locales/en.ts Normal file
View File

@@ -0,0 +1,97 @@
/** English translations. Keep keys nested by feature area.
*
* We deliberately annotate the literal with `Translations` (not `as const`)
* so sibling locale files can widen their string values without fighting
* literal-string type mismatches.
*/
export interface Translations {
common: Record<string, string>;
auth: Record<string, string>;
nav: Record<string, string>;
privacy: Record<string, string>;
feedback: Record<string, string>;
theme: Record<string, string>;
}
const en: Translations = {
common: {
cancel: "Cancel",
save: "Save",
delete: "Delete",
edit: "Edit",
close: "Close",
back: "Back",
next: "Next",
search: "Search",
loading: "Loading…",
error: "Error",
retry: "Retry",
yes: "Yes",
no: "No",
actions: "Actions",
status: "Status",
settings: "Settings",
signOut: "Sign out",
},
auth: {
signIn: "Sign in",
signInTitle: "Sign in to EnCoach",
email: "Email",
password: "Password",
forgotPassword: "Forgot password?",
needAccount: "Don't have an account?",
signUp: "Sign up",
invalidCredentials: "Invalid email or password",
},
nav: {
dashboard: "Dashboard",
courses: "Courses",
students: "Students",
teachers: "Teachers",
exams: "Exams",
reports: "Reports",
settings: "Settings",
profile: "Profile",
privacy: "Privacy Center",
reviewQueue: "Review Queue",
aiPrompts: "AI Prompts",
aiFeedback: "AI Feedback",
},
privacy: {
title: "Privacy Center",
description:
"Manage your personal data held by EnCoach under GDPR and equivalent regulations.",
exportTitle: "Download your data",
exportDescription:
"We'll package your profile, entity memberships, exam attempts, answers, AI feedback, and tickets into a single JSON file.",
exportButton: "Download my data",
exportPreparing: "Preparing export…",
exportSuccess: "Data export downloaded",
deleteTitle: "Delete my account",
deleteDescription:
"This anonymises your profile and removes personal fields from our records. Exam attempts are retained (without identifying information) for statistical integrity. This action cannot be undone.",
deleteButton: "Erase my account",
deleteErasing: "Erasing…",
confirmTitle: "Erase your account?",
confirmDescription:
"We'll anonymise your personal data and deactivate your login. Aggregate analytics will remain but will no longer be linked to you.",
confirmTypeDelete: "Type DELETE to confirm",
confirmErased: "Your account has been erased. Signing out…",
},
feedback: {
thumbsUp: "Thumbs up",
thumbsDown: "Thumbs down",
whatWentWrong: "What went wrong?",
commentPlaceholder: "e.g. Wrong answer, confusing wording, off-topic…",
commentRequired: "Please tell us what was wrong.",
submit: "Submit feedback",
},
theme: {
title: "Theme",
light: "Light",
dark: "Dark",
system: "System",
},
};
export default en;