initial commit
This commit is contained in:
187
docs/architecture/10-frontend-architecture.md
Normal file
187
docs/architecture/10-frontend-architecture.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# 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
|
||||
*/
|
||||
```
|
||||
|
||||
---
|
||||
Reference in New Issue
Block a user