diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..ca421cb --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,57 @@ +# Copilot Instructions for This Repo + +This workspace has a small Flask backend and a Vite + React + TypeScript frontend. Use these notes to be productive immediately and follow the codebase’s patterns. + +## Big picture +- Frontend: `frontend/` using Vite, React Router v7, Tailwind v4, Framer Motion, i18next. +- Backend: `backend/` small Flask service for form email with reCAPTCHA. +- Integration: The current frontend API client expects a JWT/auth API under `/api/...` using `VITE_BACKEND_URL`, but the Flask app only implements `/send-incentive`. Treat the API client as scaffolding unless those endpoints exist. + +## Frontend architecture & conventions +- Entrypoint: `src/main.tsx` (imports `./i18n` to enable translations). App routes defined in `src/App.tsx`. +- Routing: React Router v7 with layouts in `src/layout/` and pages in `src/pages/`. Protected routes via `src/routes/Privateroute.tsx` using `ApiClient.checkAuth()`. +- Styling: Tailwind CSS v4 (via `@tailwindcss/vite`). Utility classes throughout; minimal custom CSS in `src/styles/`. +- Animations: Framer Motion used for subtle motion in Nav, Footer, and sections. +- i18n: i18next + react-i18next + language detector. Resources in `src/locales//translation.json`. Initialize in `src/i18n.ts`. Global language switcher at `src/components/main/LanguageSwitcher.tsx` and embedded in `Nav`. + - Pattern: Category strings in `categoriesData.ts` use the Czech text as translation keys; `CategoryCard` calls `t(title)` and `t(description)`. If you change data text, update both `cs` and `en` JSON files. +- Assets: Put static assets in `frontend/public`. Reference them as absolute paths (`/images/...`, `/assets/...`, `/videos/...`). Do not import from `/public/...` at runtime. + +## API client & data flow +- Client: `src/api/Client.ts` wraps Axios with two instances: `apiClient` (auth) and `publicClient` (CSRF for anonymous calls). + - Base URL: `${import.meta.env.VITE_BACKEND_URL}/api` — set `VITE_BACKEND_URL` in an environment file (e.g., `.env`) for Vite. + - Auth flow: Intercepts 401s to refresh tokens via `ApiClient.refreshToken()`; on failure redirects to `/login`. + - Helpers: `ApiClient.request(method, endpoint, data, config)` returns `response.data` and normalizes params vs body. +- Current backend mismatch: Only `/send-incentive` exists in Flask. If you consume `ApiClient` endpoints (`/account/...`), ensure the backend implements them or mock responses. + +## Developer workflows +- Frontend + - Dev: `npm run dev` in `frontend/` (Vite server) + - Build: `npm run build` (outputs to `frontend/dist`) + - Env: Use `VITE_*` variables (e.g., `VITE_BACKEND_URL=http://localhost:5000`). CRA-style vars like `REACT_APP_*` are ignored by Vite. +- Backend + - Setup: `pip install -r backend/requirements.txt` + - Run: `python backend/app.py` (defaults to `http://0.0.0.0:5000`) + - Env: `backend/.env` contains `RECAPTCHA_SECRET`, email credentials, SMTP settings. + +## Patterns to follow +- Routing: Place route shells in `src/layout/` and pages in `src/pages/`; nest via ``. +- Protected routes: Wrap sections with `} />` and place guarded routes inside. +- i18n usage: `const { t } = useTranslation();` then `t('nav.home')`, etc. Add new keys to both `cs` and `en` JSON files. +- Category copy: Keep `categoriesData.ts` as the single source of keys (Czech text), and ensure translations exist for other languages. +- Axios calls: Prefer `ApiClient.request('get', '/resource', {q: 'x'})` for consistency and interceptor benefits. + +## Examples +- Add a new translatable label: + 1) Put `"nav.profile": "Profile"` in `en/translation.json` and `"nav.profile": "Profil"` in `cs/translation.json`. + 2) Use in code: `{t('nav.profile')}`. +- Call backend API: + ```ts + const data = await ApiClient.request('get', '/items', { page: 1 }); + ``` + +## Gotchas +- Vite env vars must start with `VITE_`. `.env-frontend` with `REACT_APP_*` keys won’t be read by Vite. +- Public asset paths are absolute from web root (e.g., `/assets/logo.png`), not `/public/...`. +- If i18n keys are missing, you’ll see the raw key in UI; add it to both locale files. + +If anything here looks off or incomplete (e.g., backend endpoints you actually have but aren’t in this repo), tell me and I’ll refine these rules. diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 39cd4b0..b6f7c1c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,6 +10,9 @@ "axios": "^1.12.2", "babel-plugin-react-compiler": "^19.1.0-rc.3", "framer-motion": "^12.23.22", + "i18next": "^25.6.0", + "i18next-browser-languagedetector": "^8.2.0", + "react-i18next": "^16.1.4", "react-icons": "^5.5.0", "react-router-dom": "^7.9.3", "tailwindcss": "^4.1.14" @@ -17,6 +20,7 @@ "devDependencies": { "@types/axios": "^0.9.36", "@types/react": "^19.2.0", + "@types/react-dom": "^19.2.2", "@vitejs/plugin-react": "^5.0.4" } }, @@ -252,6 +256,15 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", @@ -1449,6 +1462,16 @@ "csstype": "^3.0.2" } }, + "node_modules/@types/react-dom": { + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.2.tgz", + "integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, "node_modules/@types/react-router": { "version": "5.1.20", "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", @@ -2014,6 +2037,55 @@ "node": ">= 0.4" } }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "license": "MIT", + "dependencies": { + "void-elements": "3.1.0" + } + }, + "node_modules/i18next": { + "version": "25.6.0", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.6.0.tgz", + "integrity": "sha512-tTn8fLrwBYtnclpL5aPXK/tAYBLWVvoHM1zdfXoRNLcI+RvtMsoZRV98ePlaW3khHYKuNh/Q65W/+NVFUeIwVw==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.6" + }, + "peerDependencies": { + "typescript": "^5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.2.0.tgz", + "integrity": "sha512-P+3zEKLnOF0qmiesW383vsLdtQVyKtCNA9cjSoKCppTKPQVfKd2W8hbVo5ZhNJKDqeM7BOcvNoKJOjpHh4Js9g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, "node_modules/jiti": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", @@ -2479,6 +2551,32 @@ "react": "^19.2.0" } }, + "node_modules/react-i18next": { + "version": "16.1.4", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.1.4.tgz", + "integrity": "sha512-0UUKZDHjKnLk6dfbYXEZ9CVqLMpNiul+dHbPVQo2z2t1GkdirkeHXb/TtdsNuv+nyNOTDl1Jp6F6uwf9M3DMcg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.6", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 25.5.2", + "react": ">= 16.8.0", + "typescript": "^5" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, "node_modules/react-icons": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", @@ -2783,6 +2881,15 @@ } } }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index b5320a2..2c303a1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -5,6 +5,9 @@ "axios": "^1.12.2", "babel-plugin-react-compiler": "^19.1.0-rc.3", "framer-motion": "^12.23.22", + "i18next": "^25.6.0", + "i18next-browser-languagedetector": "^8.2.0", + "react-i18next": "^16.1.4", "react-icons": "^5.5.0", "react-router-dom": "^7.9.3", "tailwindcss": "^4.1.14" @@ -12,6 +15,7 @@ "devDependencies": { "@types/axios": "^0.9.36", "@types/react": "^19.2.0", + "@types/react-dom": "^19.2.2", "@vitejs/plugin-react": "^5.0.4" }, "scripts": { diff --git a/frontend/public/assets/README.md b/frontend/public/assets/README.md new file mode 100644 index 0000000..6f92eae --- /dev/null +++ b/frontend/public/assets/README.md @@ -0,0 +1,12 @@ +# Public assets + +Place your site-wide static assets in this folder. These files are served as-is from the web root. + +Required for navbar: +- Save the provided logo image as `logo-atcz.png` in this folder. + - Expected path in code: `/assets/logo-atcz.png` + - Recommended size: ~180–240px wide, transparent PNG or optimized WEBP + - The navbar displays it at `h-9` and auto width. + +Optional: +- Home video poster: `/assets/garage-poster.jpg` diff --git a/frontend/public/images/Seminar/poster.jpg b/frontend/public/images/Seminar/poster.jpg new file mode 100644 index 0000000..dba4e3d Binary files /dev/null and b/frontend/public/images/Seminar/poster.jpg differ diff --git a/frontend/public/images/Seminar/teacher.jpg b/frontend/public/images/Seminar/teacher.jpg new file mode 100644 index 0000000..be69d29 Binary files /dev/null and b/frontend/public/images/Seminar/teacher.jpg differ diff --git a/frontend/public/images/Seminar/team.jpg b/frontend/public/images/Seminar/team.jpg new file mode 100644 index 0000000..203ba25 Binary files /dev/null and b/frontend/public/images/Seminar/team.jpg differ diff --git a/frontend/public/images/logo.png b/frontend/public/images/logo.png new file mode 100644 index 0000000..1eab30f Binary files /dev/null and b/frontend/public/images/logo.png differ diff --git a/frontend/public/videos/README.md b/frontend/public/videos/README.md new file mode 100644 index 0000000..92dbd85 --- /dev/null +++ b/frontend/public/videos/README.md @@ -0,0 +1,17 @@ +# Videos + +Place your homepage background video here as `garage.mp4`. + +Recommended: +- Format: MP4 (H.264, AAC) +- Resolution: 1920x1080 (or 1280x720 for smaller size) +- Duration: 10–20s loop, trimmed and optimized +- File path: `/videos/garage.mp4` + +Optional poster (fallback image): +- Add an image at `/assets/garage-poster.jpg` for the `