Frontend - i18n: install tailwindcss-rtl, Cairo font, RTL-aware direction in index.css. - Language toggle: localize aria-label / menu label, persist choice, update document dir synchronously. - Sidebar: add `side` prop so the drawer pins to the right in RTL; wire up AdminLmsLayout, RoleLayout (student/teacher) and AppSidebar to pass side = i18n.dir() === 'rtl' ? 'right' : 'left'. - AdminLmsLayout: convert every nav item from hard-coded title to titleKey, translate group labels (incl. the collapsible Training), breadcrumbs, user menu (Profile / Settings / Logout), help button and toggle aria labels; replace physical mr-/right- utilities with logical me-/end-. - AI components (AiTipBanner, AiInsightsPanel, AiAlertBanner, AiSearchBar, AiAssistantDrawer): apply dir="auto" at the container level, localize titles, loading / error / empty states. - Dashboards (admin / student / teacher): wrap numeric values in <bdi>, localize dates via ar-EG, fix flex direction for KPI and assignment cards. - UI primitives (breadcrumb, calendar, carousel, dropdown-menu, menubar, context-menu, pagination, sidebar): flip chevrons in RTL via a scoped CSS rule, swap pl-/pr-/ml-/mr- for ps-/pe-/ms-/me-. - Add logical-direction helpers and bidirectional isolation classes. Locales - Expand en.ts and ar.ts with full `nav`, `sidebarGroup`, `breadcrumb`, `userMenu`, `chrome`, `ai`, and dashboard key sets; keep key parity. API client - `api-client.ts` reads the active language from localStorage/i18n and sends `Accept-Language` on every request so the backend can localize AI output. Backend (encoach_ai) - openai_service: add _LANGUAGE_NAMES, normalize_language, language-aware system prompt injection for every OpenAI call. - coach_service + controllers (coach_controller, ai_controller): thread the requested language from headers / user locale down to OpenAIService. - ai_feedback: fix latent registry error by pointing course_id at op.course instead of the non-existent encoach.course. Other - .gitignore: ignore runtime odoo logs and local caches. Made-with: Cursor
97 lines
3.1 KiB
JSON
97 lines
3.1 KiB
JSON
{
|
|
"name": "vite_react_shadcn_ts",
|
|
"private": true,
|
|
"version": "0.0.0",
|
|
"type": "module",
|
|
"scripts": {
|
|
"dev": "vite",
|
|
"build": "vite build",
|
|
"build:dev": "vite build --mode development",
|
|
"lint": "eslint .",
|
|
"preview": "vite preview",
|
|
"test": "vitest run",
|
|
"test:watch": "vitest",
|
|
"test:e2e": "playwright test",
|
|
"test:e2e:install": "playwright install --with-deps chromium"
|
|
},
|
|
"dependencies": {
|
|
"@hookform/resolvers": "^3.10.0",
|
|
"@radix-ui/react-accordion": "^1.2.11",
|
|
"@radix-ui/react-alert-dialog": "^1.1.14",
|
|
"@radix-ui/react-aspect-ratio": "^1.1.7",
|
|
"@radix-ui/react-avatar": "^1.1.10",
|
|
"@radix-ui/react-checkbox": "^1.3.2",
|
|
"@radix-ui/react-collapsible": "^1.1.11",
|
|
"@radix-ui/react-context-menu": "^2.2.15",
|
|
"@radix-ui/react-dialog": "^1.1.14",
|
|
"@radix-ui/react-dropdown-menu": "^2.1.15",
|
|
"@radix-ui/react-hover-card": "^1.1.14",
|
|
"@radix-ui/react-label": "^2.1.7",
|
|
"@radix-ui/react-menubar": "^1.1.15",
|
|
"@radix-ui/react-navigation-menu": "^1.2.13",
|
|
"@radix-ui/react-popover": "^1.1.14",
|
|
"@radix-ui/react-progress": "^1.1.7",
|
|
"@radix-ui/react-radio-group": "^1.3.7",
|
|
"@radix-ui/react-scroll-area": "^1.2.9",
|
|
"@radix-ui/react-select": "^2.2.5",
|
|
"@radix-ui/react-separator": "^1.1.7",
|
|
"@radix-ui/react-slider": "^1.3.5",
|
|
"@radix-ui/react-slot": "^1.2.3",
|
|
"@radix-ui/react-switch": "^1.2.5",
|
|
"@radix-ui/react-tabs": "^1.1.12",
|
|
"@radix-ui/react-toast": "^1.2.14",
|
|
"@radix-ui/react-toggle": "^1.1.9",
|
|
"@radix-ui/react-toggle-group": "^1.1.10",
|
|
"@radix-ui/react-tooltip": "^1.2.7",
|
|
"@tanstack/react-query": "^5.83.0",
|
|
"class-variance-authority": "^0.7.1",
|
|
"clsx": "^2.1.1",
|
|
"cmdk": "^1.1.1",
|
|
"date-fns": "^3.6.0",
|
|
"embla-carousel-react": "^8.6.0",
|
|
"i18next": "^26.0.6",
|
|
"i18next-browser-languagedetector": "^8.2.1",
|
|
"input-otp": "^1.4.2",
|
|
"lucide-react": "^0.462.0",
|
|
"next-themes": "^0.3.0",
|
|
"react": "^18.3.1",
|
|
"react-day-picker": "^8.10.1",
|
|
"react-dom": "^18.3.1",
|
|
"react-hook-form": "^7.61.1",
|
|
"react-i18next": "^17.0.4",
|
|
"react-resizable-panels": "^2.1.9",
|
|
"react-router-dom": "^6.30.1",
|
|
"recharts": "^2.15.4",
|
|
"sonner": "^1.7.4",
|
|
"tailwind-merge": "^2.6.0",
|
|
"tailwindcss-animate": "^1.0.7",
|
|
"vaul": "^0.9.9",
|
|
"zod": "^3.25.76"
|
|
},
|
|
"devDependencies": {
|
|
"@eslint/js": "^9.32.0",
|
|
"@playwright/test": "^1.59.1",
|
|
"@tailwindcss/typography": "^0.5.16",
|
|
"@testing-library/jest-dom": "^6.6.0",
|
|
"@testing-library/react": "^16.0.0",
|
|
"@types/node": "^22.16.5",
|
|
"@types/react": "^18.3.23",
|
|
"@types/react-dom": "^18.3.7",
|
|
"@vitejs/plugin-react-swc": "^3.11.0",
|
|
"autoprefixer": "^10.4.21",
|
|
"eslint": "^9.32.0",
|
|
"eslint-plugin-react-hooks": "^5.2.0",
|
|
"eslint-plugin-react-refresh": "^0.4.20",
|
|
"globals": "^15.15.0",
|
|
"jsdom": "^20.0.3",
|
|
"lovable-tagger": "^1.1.13",
|
|
"postcss": "^8.5.6",
|
|
"tailwindcss": "^3.4.17",
|
|
"tailwindcss-rtl": "^0.9.0",
|
|
"typescript": "^5.8.3",
|
|
"typescript-eslint": "^8.38.0",
|
|
"vite": "^5.4.19",
|
|
"vitest": "^3.2.4"
|
|
}
|
|
}
|