---
name: ui-hooks
description: "Tworzenie custom hooków do fetchowania danych, mutacji i zarządzania stanem. Projekt używa TanStack React Query 5 jako głównego wzorca fetch/mutacji. Użyj gdy tworzysz lub modyfikujesz hook (use*.ts)."
---

# Skill: UI / Hooki

## Opis
Tworzenie custom hooków do fetchowania danych, mutacji i zarządzania stanem.
Projekt używa **TanStack React Query 5** jako głównego wzorca fetch/mutacji.

## Kiedy używać
Użyj tego skilla gdy tworzysz lub modyfikujesz hook (use*.ts).

---

## Lokalizacja

```
src/hooks/use{Feature}{Action}.ts     ← hooki globalne
src/features/{domain}/hooks/          ← hooki domenowe
```

## Hook fetch — React Query (preferowany wzorzec)

```typescript
// src/features/dashboard/hooks/useProjectDashboard.ts
import { useQuery } from '@tanstack/react-query';
import type { ProjectDashboardWeb } from '../types/projectDashboard.types';
import { dashboardApi } from '../services/dashboardApi';

export function useProjectDashboard(tenantId: string, projectId: string) {
    return useQuery({
        queryKey: ['projectDashboard', tenantId, projectId],
        queryFn: () => dashboardApi.getProjectDashboard(tenantId, projectId),
        enabled: !!tenantId && !!projectId,
    });
}

// Użycie w komponencie:
// const { data, isLoading, error, refetch } = useProjectDashboard(tenantId, projectId);
```

## Hook mutacji — React Query (preferowany wzorzec)

```typescript
// src/hooks/useCreateProject.ts
import { useMutation, useQueryClient } from '@tanstack/react-query';
import type { CreateProjectRequest, ProjectDetailsWeb } from '../types/project.types';
import { projectApi } from '../api/projectApi';

export function useCreateProject(tenantId: string) {
    const queryClient = useQueryClient();

    return useMutation({
        mutationFn: (data: CreateProjectRequest): Promise<ProjectDetailsWeb> =>
            projectApi.create(tenantId, data),
        onSuccess: () => {
            queryClient.invalidateQueries({ queryKey: ['projects', tenantId] });
        },
    });
}

// Użycie w komponencie:
// const { mutateAsync: createProject, isPending, error } = useCreateProject(tenantId);
```

## Hook modala

```typescript
// src/hooks/useModal.ts — gotowy hook z projektu
import { useModal } from '../hooks/useModal';

// Użycie:
const createModal = useModal();
// createModal.isOpen, createModal.onOpen, createModal.onClose, createModal.toggle
```

## Hook uprawnień

```typescript
// Użycie gotowych hooków z projektu
const { canEdit, canManageMembers, canWriteResources } = useProjectPermissions(projectId);
const { canManageMembers, canCreateProject } = useTenantPermissions();

// Warunkowe renderowanie
{canEdit && <Button onClick={handleEdit}>Edytuj</Button>}
```

## Hook niestandardowy (bez React Query — dla stanu lokalnego / SignalR / WebSocket)

```typescript
// Używaj useState/useEffect tylko gdy React Query nie pasuje:
// - stan UI lokalny (otwarty/zamknięty, krok formularza)
// - subskrypcje SignalR / WebSocket
// - jednorazowe akcje nie będące fetch/mutacją

import { useState, useEffect, useCallback } from 'react';

export interface UseSignalRConnectionResult {
    isConnected: boolean;
    reconnect: () => void;
}

export function useSignalRConnection(hubUrl: string): UseSignalRConnectionResult {
    const [isConnected, setIsConnected] = useState(false);

    const connect = useCallback(() => {
        // logika połączenia SignalR
        setIsConnected(true);
    }, [hubUrl]);

    useEffect(() => {
        connect();
        return () => {
            setIsConnected(false);
        };
    }, [connect]);

    return { isConnected, reconnect: connect };
}
```

## Zasady

- **React Query jako primary** — dla fetch i mutacji zawsze używaj `useQuery` / `useMutation`
- `useState/useEffect` tylko dla stanu lokalnego UI i subskrypcji (SignalR)
- Jeden hook = jedna odpowiedzialność
- `queryKey` musi zawierać wszystkie zależności danych (tenantId, projectId itp.)
- `enabled: !!param` gdy parametry mogą być puste przy pierwszym renderze
- `invalidateQueries` w `onSuccess` mutacji żeby odświeżyć powiązane query
- Zakaz duplikowania logiki fetch między komponentami — wydziel do hooka
- Nazewnictwo: `use{Feature}` lub `use{Feature}{Action}`
- Zakaz `any` w typach hooka
