Aller au contenu principal

Frontend Documentation

Overview

The frontend is built with React 18, TypeScript, and modern tooling following best practices.

Project Structure

frontend/src/
├── __tests__/ # Unit tests
├── _generated/ # Generated types from OpenAPI
├── auth/ # Authentication context and hooks
├── client/ # API client hooks (TanStack Query)
├── components/ # Reusable UI components
├── hooks/ # Custom React hooks
├── i18n/ # Internationalization
├── lib/ # Utility functions
├── pages/ # Page components (organized by domain)
│ ├── admin/ # Admin panel pages
│ ├── auth/ # Authentication pages
│ ├── client/ # Client portal pages
│ ├── contacts/ # Contact management pages
│ ├── sessions/ # Session management pages
│ ├── quotes/ # Quote management pages
│ ├── invoices/ # Invoice management pages
│ ├── contracts/ # Contract management pages
│ ├── users/ # User-related pages
│ ├── settings/ # Settings pages
│ ├── dashboard/ # Dashboard pages
│ └── ... # Other domain pages
├── router/ # Route guards
├── router.tsx # Router configuration
└── store/ # Zustand state management

Page Organization: Pages are organized by domain for better maintainability and logical grouping. Each domain contains related pages (list, view, form pages) grouped together.

Key Concepts

Authentication

  • AuthProvider: Authentication context provider
  • useAuth: Hook to access authentication state
  • Token management and refresh
  • User status checks

API Client

  • TanStack Query v5 for data fetching
  • Type-safe API calls using generated types
  • Automatic caching and refetching
  • Error handling

Structure: client/{module}/use{Module}.ts

TanStack Query v5 Mutation Callbacks: All onSuccess and onError callbacks in useMutation hooks must accept 4 arguments: (data/error, variables, context, mutation).

Example:

import { useSessions } from "@/client/sessions/useSessions";

const { data, isLoading, error } = useSessions();
const createSession = useCreateSession({
onSuccess: (data, variables, context, mutation) => {
toast.success("Session created successfully");
void queryClient.invalidateQueries({ queryKey: ["sessions"] });
},
onError: (error, variables, context, mutation) => {
toast.error(
error instanceof Error ? error.message : "Failed to create session"
);
},
});

Routing

  • TanStack Router for type-safe routing
  • Route guards for authentication
  • Status-based redirects

Forms

  • TanStack Form for form management
  • Zod for validation
  • Auto-fill on page load

Example:

const form = useForm({
defaultValues: { name: "" },
onSubmit: async ({ value }) => {
await createSession.mutateAsync(value);
},
});

State Management

  • Zustand for global state
  • Auth store: User and token
  • UI store: Sidebar, theme
  • WebSocket store: Connection state

See: frontend/src/store/README.md for detailed documentation

Components

  • Reusable UI components in components/ui/
  • Layout components in components/layout/
  • Feature components in components/{feature}/
  • Molecule components in components/molecules/ (e.g., StatusBadge, PageHeader, DataField, PayInvoiceButton, PayInvoiceDialog)
  • Organism components in components/organisms/ (e.g., FormPageLayout, ViewPageLayout, ListPageLayout)

Layout Components:

  • Sidebar: Desktop sidebar navigation (collapsible)
  • MobileSidebar: Mobile sidebar with slide-in animation
  • UserDropdown: Desktop user menu dropdown
  • UserMenuMobile: Full-screen mobile user menu with expandable sub-menus
    • Automatically used on mobile devices (< 1024px)
    • Scrollable content for long menus
    • Supports nested menu sections (Mon compte, Paramètres, Système)

StatusBadge: Reusable component for displaying status labels with optional tooltips and dynamic variants. Always use StatusBadge instead of manually creating status badges.

Stripe Payment Components:

  • PayInvoiceButton: Button component that opens a payment dialog for invoices
  • PayInvoiceDialog: Dialog component for processing invoice payments with Stripe Elements
  • PaymentForm: Form component with Stripe Elements for secure card input

Usage Example:

import { PayInvoiceButton } from "@/components/molecules";

<PayInvoiceButton
invoiceId={invoice.id}
amount={invoice.amount}
currency="eur"
onPaymentSuccess={() => {
// Refresh invoice data
void queryClient.invalidateQueries({ queryKey: ["invoices", invoice.id] });
}}
/>;

Stripe Integration

  • Stripe Elements for secure payment processing
  • Payment intents for invoice payments
  • Subscription management hooks
  • Webhook event handling (backend)

Environment Variables:

  • VITE_STRIPE_PUBLISHABLE_KEY - Stripe publishable key (required for frontend)

API Client: client/stripe/useStripe.ts

  • useMySubscription() - Get current subscription
  • usePaymentHistory() - Get payment history
  • useCreateSubscription() - Create a subscription
  • useCancelSubscription() - Cancel a subscription
  • useCreateInvoicePayment() - Create payment intent for invoice

Internationalization

  • react-i18next for translations
  • English and French supported
  • Translation keys in i18n/locales/{lang}/translation.json

Usage:

const { t } = useTranslation();
return <h1>{t("page.title")}</h1>;

Development Guidelines

Adding a New Page

  1. Create page directory in the appropriate domain: pages/{domain}/{PageName}/{PageName}.tsx
    • Choose the domain based on the page's purpose (e.g., contacts/, sessions/, quotes/, invoices/, contracts/, users/, settings/, admin/, auth/, etc.)
    • If creating a new domain, create the domain directory first
  2. Add route in router.tsx
  3. Add page title in components/layout/usePageTitle.ts
  4. Add translations in i18n/locales/

Example: Creating a new contact form page:

  • Create: pages/contacts/ContactFormPage/ContactFormPage.tsx
  • Add route: /contacts/new and /contacts/$id/edit
  • Add page title configuration
  • Add translations for the form

Client portal (/client)

  • Public routes:
    • /client?token=... renders the dashboard.
    • /client/quotes/:quoteId?token=... renders a focused quote view (with accept CTA).
  • Resolve tokens with useClientAccess(token, quoteId?) (client/client-access) instead of using the authenticated API client.
  • Query param token must be preserved; AppLayout treats /client (and /client/quotes/*) as public (no login redirect).
  • Keep actions stacked on mobile (flex-col sm:flex-row) and design for touch first.

Adding a New API Hook

  1. Create hook file: client/{module}/use{Module}.ts
  2. Use TanStack Query mutations/queries
  3. Use generated types from _generated/types.gen.ts
  4. Export from client/{module}/index.ts

Creating Reusable Components

  1. Create component in components/ui/ or components/{feature}/
  2. Make it responsive (mobile + desktop)
  3. Add TypeScript types
  4. Use i18n for all text
  5. Follow existing component patterns

Form Best Practices

  1. Use TanStack Form
  2. Validate with Zod
  3. Fill form on page load with existing data
  4. Show validation errors
  5. Disable submit button during submission

Styling

  • Use Tailwind CSS
  • Follow existing design patterns
  • Make components responsive
  • Use shadcn/ui style components

Mobile Responsiveness

Mobile-First Approach:

  • All components should be mobile-responsive
  • Use Tailwind breakpoints: sm:, md:, lg: (1024px), xl:
  • Mobile sidebar uses slide-in animation
  • User menu automatically switches to full-screen on mobile
  • Touch-friendly interactions (larger tap targets, proper spacing)

Mobile Navigation:

  • MobileSidebar: Slide-in sidebar for mobile navigation
  • UserMenuMobile: Full-screen user menu with expandable sections
  • Automatic detection: UserDropdown detects screen size and uses UserMenuMobile on mobile

Type Safety

  • Always use types from _generated/types.gen.ts
  • Don't create duplicate types
  • Use TypeScript strictly

Testing

Run tests:

npm run test

Write tests for:

  • Components
  • Hooks
  • Utility functions
  • API client

Environment Variables

See .env.example for required variables:

  • API base URL
  • WebSocket URL
  • Feature flags