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:
85
src/i18n/locales/ar.ts
Normal file
85
src/i18n/locales/ar.ts
Normal 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
97
src/i18n/locales/en.ts
Normal 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;
|
||||
Reference in New Issue
Block a user