188 lines
7.7 KiB
Markdown
188 lines
7.7 KiB
Markdown
# 10. Frontend Architecture
|
|
|
|
## State Management Strategy
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ STATE MANAGEMENT │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
│ │ TanStack Query │ │
|
|
│ │ (Server State / Cache) │ │
|
|
│ │ │ │
|
|
│ │ • Characters list │ │
|
|
│ │ • Accounts data │ │
|
|
│ │ • Teams & members │ │
|
|
│ │ • Progressions │ │
|
|
│ │ • DofusDB reference data │ │
|
|
│ └─────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
│ │ Zustand │ │
|
|
│ │ (Client/UI State) │ │
|
|
│ │ │ │
|
|
│ │ • Selected items (multi-select) │ │
|
|
│ │ • UI preferences (sidebar collapsed, etc.) │ │
|
|
│ │ • Filter states │ │
|
|
│ │ • Modal open/close states │ │
|
|
│ │ • Theme preference │ │
|
|
│ └─────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
│ │ React State │ │
|
|
│ │ (Component-local State) │ │
|
|
│ │ │ │
|
|
│ │ • Form inputs │ │
|
|
│ │ • Hover/focus states │ │
|
|
│ │ • Animation states │ │
|
|
│ │ • Temporary UI states │ │
|
|
│ └─────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## TanStack Query Setup
|
|
|
|
```typescript
|
|
// src/lib/client/query-client.ts
|
|
import { QueryClient } from '@tanstack/react-query';
|
|
|
|
export const queryClient = new QueryClient({
|
|
defaultOptions: {
|
|
queries: {
|
|
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
gcTime: 1000 * 60 * 30, // 30 minutes
|
|
retry: 1,
|
|
refetchOnWindowFocus: false,
|
|
},
|
|
mutations: {
|
|
onError: (error) => {
|
|
console.error('Mutation error:', error);
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
// Query keys factory
|
|
export const queryKeys = {
|
|
characters: {
|
|
all: ['characters'] as const,
|
|
list: (filters: CharacterFilters) => [...queryKeys.characters.all, 'list', filters] as const,
|
|
detail: (id: string) => [...queryKeys.characters.all, 'detail', id] as const,
|
|
},
|
|
accounts: {
|
|
all: ['accounts'] as const,
|
|
list: () => [...queryKeys.accounts.all, 'list'] as const,
|
|
detail: (id: string) => [...queryKeys.accounts.all, 'detail', id] as const,
|
|
},
|
|
teams: {
|
|
all: ['teams'] as const,
|
|
list: () => [...queryKeys.teams.all, 'list'] as const,
|
|
detail: (id: string) => [...queryKeys.teams.all, 'detail', id] as const,
|
|
},
|
|
progressions: {
|
|
all: ['progressions'] as const,
|
|
list: (type?: ProgressionType) => [...queryKeys.progressions.all, 'list', type] as const,
|
|
byCharacter: (characterId: string) => [...queryKeys.progressions.all, 'character', characterId] as const,
|
|
},
|
|
} as const;
|
|
```
|
|
|
|
## Zustand Store
|
|
|
|
```typescript
|
|
// src/lib/client/stores/ui-store.ts
|
|
import { create } from 'zustand';
|
|
import { persist } from 'zustand/middleware';
|
|
|
|
interface UIState {
|
|
// Sidebar
|
|
sidebarCollapsed: boolean;
|
|
toggleSidebar: () => void;
|
|
|
|
// Theme
|
|
theme: 'light' | 'dark';
|
|
setTheme: (theme: 'light' | 'dark') => void;
|
|
|
|
// Selection (for bulk actions)
|
|
selectedCharacterIds: string[];
|
|
selectCharacter: (id: string) => void;
|
|
deselectCharacter: (id: string) => void;
|
|
selectAllCharacters: (ids: string[]) => void;
|
|
clearSelection: () => void;
|
|
}
|
|
|
|
export const useUIStore = create<UIState>()(
|
|
persist(
|
|
(set) => ({
|
|
// Sidebar
|
|
sidebarCollapsed: false,
|
|
toggleSidebar: () => set((state) => ({ sidebarCollapsed: !state.sidebarCollapsed })),
|
|
|
|
// Theme
|
|
theme: 'dark',
|
|
setTheme: (theme) => set({ theme }),
|
|
|
|
// Selection
|
|
selectedCharacterIds: [],
|
|
selectCharacter: (id) =>
|
|
set((state) => ({
|
|
selectedCharacterIds: [...state.selectedCharacterIds, id]
|
|
})),
|
|
deselectCharacter: (id) =>
|
|
set((state) => ({
|
|
selectedCharacterIds: state.selectedCharacterIds.filter((i) => i !== id)
|
|
})),
|
|
selectAllCharacters: (ids) => set({ selectedCharacterIds: ids }),
|
|
clearSelection: () => set({ selectedCharacterIds: [] }),
|
|
}),
|
|
{
|
|
name: 'dofus-manager-ui',
|
|
partialize: (state) => ({
|
|
sidebarCollapsed: state.sidebarCollapsed,
|
|
theme: state.theme,
|
|
}),
|
|
}
|
|
)
|
|
);
|
|
```
|
|
|
|
## Routing Structure
|
|
|
|
```typescript
|
|
// src/routes/__root.tsx
|
|
import { createRootRoute, Outlet } from '@tanstack/react-router';
|
|
import { AppShell } from '@/components/layout/app-shell';
|
|
|
|
export const Route = createRootRoute({
|
|
component: () => (
|
|
<AppShell>
|
|
<Outlet />
|
|
</AppShell>
|
|
),
|
|
});
|
|
|
|
// Route tree
|
|
/*
|
|
src/routes/
|
|
├── __root.tsx # Root layout with AppShell
|
|
├── index.tsx # / → Dashboard
|
|
├── characters/
|
|
│ ├── index.tsx # /characters → List
|
|
│ └── $id.tsx # /characters/:id → Detail
|
|
├── accounts/
|
|
│ ├── index.tsx # /accounts → List
|
|
│ └── $id.tsx # /accounts/:id → Detail
|
|
├── teams/
|
|
│ ├── index.tsx # /teams → List
|
|
│ └── $id.tsx # /teams/:id → Detail
|
|
├── progressions/
|
|
│ └── index.tsx # /progressions → Tracker
|
|
└── settings/
|
|
└── index.tsx # /settings → Settings
|
|
*/
|
|
```
|
|
|
|
---
|