- Removed infinite query options and related functions from Trading212, User Registration, and User API files. - Updated API client base URLs in privateClient.ts and publicClient.ts to use environment variable for backend URL. - Refactored Downloader component to directly call API functions for video info retrieval instead of using a hook.
8.3 KiB
8.3 KiB
Copilot Instructions for Vontor CZ
Overview
This monorepo contains a Django backend and a Vite/React frontend, orchestrated via Docker Compose. The project is designed for a Czech e-marketplace, with custom payment integrations and real-time features.
Architecture
- backend/: Django project (
vontor_cz), customaccountapp, and third-party payment integrations (thirdparty/).- Uses Django REST Framework, Channels (ASGI), Celery, and S3/static/media via
django-storages. - Custom user model:
account.CustomUser. - API docs: DRF Spectacular (
/api/schema/).
- Uses Django REST Framework, Channels (ASGI), Celery, and S3/static/media via
- frontend/: Vite + React + TypeScript app.
- Organized by
src/api/,components/,features/,layouts/,pages/,routes/. - Uses React Router layouts and nested routes (see
src/layouts/,src/routes/). - Uses Tailwind CSS for styling (configured via
src/index.csswith@import "tailwindcss";). Prefer utility classes over custom CSS.
- Organized by
Developer Workflows
- Backend
- Local dev:
python manage.py runserver(or use Docker Compose) - Migrations:
python manage.py makemigrations && python manage.py migrate - Celery:
celery -A vontor_cz worker -l info - Channels: Daphne/ASGI (see Docker Compose command)
- Env config:
.envfiles inbackend/(see.gitignorefor secrets)
- Local dev:
- Frontend
- Install:
npm install - Dev server:
npm run dev - Build:
npm run build - Preview:
npm run preview - Static assets:
src/assets/(import in JS/CSS),public/(referenced in HTML)
- Install:
Conventions & Patterns
- Backend
- Use environment variables for secrets and config (see
settings.py). - Static/media files: S3 in production, local in dev (see
settings.py). - API versioning and docs: DRF Spectacular config in
settings.py. - Custom permissions, filters, and serializers in each app.
- Use environment variables for secrets and config (see
- Frontend
- Use React Router layouts for shared UI (see
src/layouts/,LAYOUTS.md). - API calls and JWT handling in
src/api/. - Route definitions and guards in
src/routes/(ROUTES.md). - Use TypeScript strict mode (see
tsconfig.*.json). - Linting: ESLint config in
eslint.config.js. - Styling: Tailwind CSS is present. Prefer utility classes; keep minimal component-scoped CSS. Global/base styles live in
src/index.css. Avoid inline styles and CSS-in-JS unless necessary.
- Use React Router layouts for shared UI (see
Frontend API Client (required)
All frontend API calls must use the shared client at frontend/src/api/Client.ts.
- Client.public: no cookies, no Authorization header (for public Django endpoints).
- Client.auth: sends cookies and includes Bearer token; auto-refreshes on 401 (retries up to 2x).
- Centralized error handling: subscribe via Client.onError to show toasts/snackbars.
- Tokens are stored in cookies by Client.setTokens and cleared by Client.clearTokens.
Example usage (TypeScript)
import Client from "@/api/Client";
// Public request (no credentials)
async function listPublicItems() {
const res = await Client.public.get("/api/public/items/");
return res.data;
}
// Login (obtain tokens and persist to cookies)
async function login(username: string, password: string) {
// Default SimpleJWT endpoint (adjust if your backend differs)
const res = await Client.public.post("/api/token/", { username, password });
const { access, refresh } = res.data;
Client.setTokens(access, refresh);
}
// Authenticated requests (auto Bearer + refresh on 401)
async function fetchProfile() {
const res = await Client.auth.get("/api/users/me/");
return res.data;
}
function logout() {
Client.clearTokens();
window.location.assign("/login");
}
// Global error toasts
import { useEffect } from "react";
function useApiErrors(showToast: (msg: string) => void) {
useEffect(() => {
const unsubscribe = Client.onError((e) => {
const { message, status } = e.detail;
showToast(status ? `${status}: ${message}` : message);
});
return unsubscribe;
}, [showToast]);
}
Vite env used by the client:
- VITE_API_BASE_URL (default: http://localhost:8000)
- VITE_API_REFRESH_URL (default: /api/token/refresh/)
- VITE_LOGIN_PATH (default: /login)
Notes
- Public client never sends cookies or Authorization.
- Ensure Django CORS settings allow your frontend origin. See backend/vontor_cz/settings.py.
- Use React Router layouts and guards as documented in frontend/src/routes/ROUTES.md and frontend/src/layouts/LAYOUTS.md.
Integration Points
- Payments:
thirdparty/contains custom integrations for Stripe, GoPay, Trading212. - Real-time: Django Channels (ASGI, Redis) for websockets.
- Task queue: Celery + Redis for async/background jobs.
- API: REST endpoints, JWT auth, API key support.
OpenAPI Client Generation (Orval)
This project uses Orval to auto-generate TypeScript API clients from the Django OpenAPI schema.
Configuration
- Orval config:
frontend/src/orval.config.ts - Schema URL:
/api/schema/(DRF Spectacular endpoint) - Fetch script:
frontend/scripts/fetch-openapi.js - Commands:
npm run api:update— fetches schema + generates client- Runs:
node scripts/fetch-openapi.js && npx orval
Generated Output
- Location:
frontend/src/api/generated/ - Files: TypeScript interfaces, Axios-based API hooks
- Uses custom mutators:
publicMutatorandprivateMutator
Custom Mutators
Two Axios clients handle public/private API requests:
Public Client (frontend/src/api/publicClient.ts):
import axios, { type AxiosRequestConfig } from "axios";
const backendUrl = import.meta.env.VITE_BACKEND_URL || "http://localhost:8000";
export const publicApi = axios.create({
baseURL: backendUrl + "/api/",
withCredentials: false, // no cookies for public endpoints
});
export const publicMutator = async <T>(config: AxiosRequestConfig): Promise<T> => {
const response = await publicApi.request<T>(config);
return response.data;
};
Private Client (frontend/src/api/privateClient.ts):
import axios, { type AxiosRequestConfig } from "axios";
const backendUrl = import.meta.env.VITE_BACKEND_URL || "http://localhost:8000";
export const privateApi = axios.create({
baseURL: backendUrl + "/api/",
withCredentials: true, // sends HttpOnly cookies (access/refresh tokens)
});
// Auto-refresh on 401
privateApi.interceptors.response.use(
(res) => res,
async (error) => {
const original = error.config;
if (error.response?.status === 401 && !original._retry) {
original._retry = true;
try {
await privateApi.post("/auth/refresh/");
return privateApi(original);
} catch {
// optional: logout
}
}
return Promise.reject(error);
}
);
export const privateMutator = async <T>(config: AxiosRequestConfig): Promise<T> => {
const response = await privateApi.request<T>(config);
return response.data;
};
Environment Variables (Vite)
- IMPORTANT: Use
import.meta.env.VITE_*instead ofprocess.envin browser code - NEVER import
dotenv/configin frontend files (causes "process is not defined" error) - Available vars:
VITE_BACKEND_URL(default:http://localhost:8000)VITE_API_BASE_URL(if using Client.ts wrapper)VITE_API_REFRESH_URL(default:/api/token/refresh/)VITE_LOGIN_PATH(default:/login)
Usage Example
import { useGetOrders } from "@/api/generated/orders";
function OrdersList() {
const { data, isLoading, error } = useGetOrders();
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{data?.map(order => <li key={order.id}>{order.status}</li>)}
</ul>
);
}
Helpers
- Choices helper:
frontend/src/api/get_choices.ts- Function:
getChoices(requests, lang) - Returns:
{ "Model.field": [{ value, label }] }
- Function:
References
- frontend/REACT.md: Frontend structure, workflows, and conventions.
- frontend/src/layouts/LAYOUTS.md: Layout/component patterns.
- frontend/src/routes/ROUTES.md: Routing conventions.
- backend/vontor_cz/settings.py: All backend config, env, and integration details.
- docker-compose.yml: Service orchestration and dev workflow.
When in doubt, check the referenced markdown files and settings.py for project-specific logic and patterns.