Turtor Docs
Frontend

React Query hooky

Feature-organizované hooky, Orval generování, STALE_TIMES a architektura

Frontend hooky jsou organizovány podle feature v apps/admin/app/hooks/. Všechny API volání používají Orval-generované hooky z @turtor/api-types.

Adresářová struktura

apps/admin/app/hooks/
├── index.ts                    # Barrel export (import z ~/hooks)
├── constants.ts                # STALE_TIMES, GC_TIMES
├── auth/                       # useCurrentUser, useLogout, useSwitchRole, ...
├── instructor/                 # usePendingConfirmations, useInstructorCourses, ...
├── course/                     # useCourseList, useKanbanCourses, useTeamActions, ...
├── company/                    # useCompanyList, useCompanyMembers, ...
├── garant/                     # useApplications, useReplacements, useProposals, ...
├── sales/                      # useLeads, usePipeline, useCreateLead, ...
├── admin/                      # useAdminStats, useSystemHealth
├── notifications/              # useNotificationList, useUnreadNotificationCount, ...
├── profile/                    # useFormState
├── withdrawal/                 # usePendingWithdrawalRequests, useReviewWithdrawalRequest
├── verify/                     # usePhoneVerification
├── realtime/                   # useRealtimeUpdates
└── utils/                      # useTablePagination, useMutationWithToast, queryKeys

Import vzor

Všechny hooky se importují z jednoho barrel exportu:

// Správně -- z barrel exportu
import { useMyProfile, useNotifications, STALE_TIMES } from '~/hooks';

// Špatně -- přímý import
import { useMyProfile } from '~/hooks/instructor/useProfile';

Stale Time konstanty

import { STALE_TIMES, GC_TIMES } from '~/hooks';

// STALE_TIMES -- jak dlouho jsou data považována za čerstvá
STALE_TIMES.USER_DATA   // 30s  -- auth, notifikace
STALE_TIMES.LISTS       // 1m   -- seznamy kurzů, instruktorů
STALE_TIMES.STATS       // 5m   -- dashboard statistiky
STALE_TIMES.CONFIG      // 30m  -- města, kraje, konfigurační data

// GC_TIMES -- jak dlouho zůstávají data v cache po odpojení
GC_TIMES.SHORT          // 10m  -- často přistupovaná data
GC_TIMES.DEFAULT        // 30m  -- výchozí
GC_TIMES.LONG           // 1h   -- referenční data

Klíčové principy

1. Pouze Orval-generované hooky

Všechna API volání musí používat hooky z @turtor/api-types:

// Správně
import { useInstructorsControllerFindAllApiV1 } from '@turtor/api-types/instructors';

// Špatně -- nikdy nepoužívat přímý apiClient v hook souborech
const data = await apiClient.get('/instructors');

2. Region-aware dotazy

Admin hooky automaticky injektují regionId z ManageRegionContext:

export function useCourseList({ filters } = {}) {
  const regionId = useCurrentRegionId();
  const isRegionReady = useIsRegionReady();

  return useCoursesControllerFindAllApiV1(
    { ...filters, regionId },
    { query: { enabled: isRegionReady } }
  );
}

3. WebSocket = invalidace cache

WebSocket eventy spouští invalidaci React Query cache, ne lokální stav:

// Správně -- invalidovat query, React Query refetchne
socket.on('notification', () => {
  queryClient.invalidateQueries({ queryKey: queryKeys.notifications.all });
});

// Špatně -- lokální stav vedle React Query
const [notifications, setNotifications] = useState([]);

4. Optimistické aktualizace

Optimistické aktualizace pouze pro nedestruktivní operace:

  • Status přepínání (schválení/odmítnutí, přečteno/nepřečteno)
  • Změny dostupnosti
  • Přepínání rolí

Ne pro: smazání, platby, vytváření (čekat na server ID).

5. useMutationWithToast

Factory hook pro standardní mutace s toast notifikacemi:

export function useGenerateCourse() {
  return useMutationWithToast({
    mutationFn: (data) => generateMutation.mutateAsync({ data }),
    successMessage: 'Kurz byl vygenerován',
    errorMessage: 'Nepodařilo se vygenerovat kurz',
    invalidateKeys: [queryKeys.generation.all],
  });
}

Utility hooky

HookSouborÚčel
useCurrentRegionIduseRegionQueryParams.tsAktuální region z kontextu
useIsRegionReadyuseRegionQueryParams.tsKontrola, zda je region načtený
useTablePaginationuseTablePagination.tsURL-synchronizovaná paginace pro tabulky
useMutationWithToastuseMutationWithToast.tsFactory: mutace + toast + invalidace
queryKeysqueryKeys.tsManuální query key factory

Stránkovaná odpověď

API vrací stránkované odpovědi ve formátu { data: T[], meta }:

const query = useCoursesControllerFindAllApiV1(params);

// Správně -- extrahovat data z paginované odpovědi
return {
  ...query,
  data: query.data?.data ?? [],       // T[] z response.data
  meta: query.data?.meta,              // Metadata paginace
};

Web aplikace -- hooky

Web app má minimální sadu hooků:

apps/web/app/hooks/
├── auth/           # useCurrentUser, useLogin, useRegister, useSendMagicLink, ...
├── checkout/       # useCheckout
├── course/         # useCourseList, useCourseDetail, useBookCourse
└── profile/        # useMyBookings, useUpdateProfile, useChangePassword

Navíc specifické hooky:

  • useGeolocation -- GPS poloha uživatele, výpočet vzdálenosti
  • useAresLookup -- vyhledávání firem v ARES (IČO lookup)