@the-bearded-bear/claude-craft 8.8.1 → 8.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dev/i18n/de/Angular/commands/check-compliance.md +218 -168
- package/Dev/i18n/de/Common/agents/refactoring-specialist.md +146 -69
- package/Dev/i18n/de/Common/commands/add-technology.md +166 -68
- package/Dev/i18n/de/Common/commands/setup-ci.md +216 -33
- package/Dev/i18n/de/Laravel/commands/check-compliance.md +223 -115
- package/Dev/i18n/de/Python/checklists/new-feature.md +319 -44
- package/Dev/i18n/de/React/rules/02-architecture.md +483 -322
- package/Dev/i18n/de/React/rules/03-coding-standards.md +584 -368
- package/Dev/i18n/de/Symfony/commands/check-compliance.md +186 -114
- package/Dev/i18n/de/Symfony/rules/07-testing-symfony.md +679 -10
- package/Dev/i18n/de/UIUX/commands/a11y-component.md +284 -26
- package/Dev/i18n/de/UIUX/commands/design-tokens.md +199 -35
- package/Dev/i18n/de/UIUX/commands/user-flow.md +179 -16
- package/Dev/i18n/de/VueJS/commands/check-compliance.md +227 -102
- package/Dev/i18n/es/Angular/commands/check-compliance.md +217 -167
- package/Dev/i18n/es/Common/commands/add-technology.md +158 -60
- package/Dev/i18n/es/Laravel/commands/check-compliance.md +223 -115
- package/Dev/i18n/es/React/commands/check-testing.md +263 -169
- package/Dev/i18n/es/ReactNative/commands/check-testing.md +256 -198
- package/Dev/i18n/es/Symfony/commands/check-compliance.md +189 -117
- package/Dev/i18n/es/Symfony/commands/check-testing.md +301 -102
- package/Dev/i18n/es/Symfony/commands/migration-plan.md +267 -34
- package/Dev/i18n/es/UIUX/commands/a11y-component.md +274 -16
- package/Dev/i18n/es/UIUX/commands/design-tokens.md +186 -22
- package/Dev/i18n/es/UIUX/commands/user-flow.md +173 -10
- package/Dev/i18n/es/VueJS/commands/check-compliance.md +227 -102
- package/Dev/i18n/fr/Angular/commands/check-compliance.md +217 -167
- package/Dev/i18n/fr/Common/commands/ralph-run.md +138 -60
- package/Dev/i18n/fr/Laravel/commands/check-compliance.md +223 -115
- package/Dev/i18n/fr/React/commands/check-architecture.md +370 -53
- package/Dev/i18n/fr/React/commands/check-code-quality.md +454 -64
- package/Dev/i18n/fr/React/commands/check-testing.md +476 -102
- package/Dev/i18n/fr/Symfony/commands/check-compliance.md +188 -116
- package/Dev/i18n/fr/VueJS/commands/check-compliance.md +227 -102
- package/Dev/i18n/pt/Angular/commands/check-compliance.md +225 -175
- package/Dev/i18n/pt/Common/agents/ralph-conductor.md +211 -30
- package/Dev/i18n/pt/Common/commands/add-technology.md +149 -51
- package/Dev/i18n/pt/Common/commands/ralph-run.md +239 -35
- package/Dev/i18n/pt/Flutter/commands/analyze-performance.md +331 -46
- package/Dev/i18n/pt/Flutter/commands/check-compliance.md +234 -29
- package/Dev/i18n/pt/Flutter/commands/generate-feature.md +526 -51
- package/Dev/i18n/pt/Flutter/commands/generate-widget.md +515 -27
- package/Dev/i18n/pt/Flutter/commands/golden-update.md +322 -46
- package/Dev/i18n/pt/Flutter/commands/localization-check.md +278 -37
- package/Dev/i18n/pt/Laravel/commands/check-compliance.md +212 -104
- package/Dev/i18n/pt/React/commands/accessibility-check.md +151 -42
- package/Dev/i18n/pt/React/commands/bundle-analyze.md +300 -25
- package/Dev/i18n/pt/React/commands/check-architecture.md +369 -34
- package/Dev/i18n/pt/React/commands/check-code-quality.md +440 -34
- package/Dev/i18n/pt/React/commands/check-compliance.md +232 -62
- package/Dev/i18n/pt/React/commands/check-security.md +370 -31
- package/Dev/i18n/pt/React/commands/check-testing.md +473 -43
- package/Dev/i18n/pt/React/commands/generate-component.md +454 -19
- package/Dev/i18n/pt/React/commands/generate-hook.md +425 -24
- package/Dev/i18n/pt/React/commands/storybook-story.md +520 -35
- package/Dev/i18n/pt/ReactNative/commands/app-size.md +358 -387
- package/Dev/i18n/pt/ReactNative/commands/check-code-quality.md +246 -75
- package/Dev/i18n/pt/ReactNative/commands/check-compliance.md +191 -85
- package/Dev/i18n/pt/ReactNative/commands/check-security.md +363 -115
- package/Dev/i18n/pt/ReactNative/commands/check-testing.md +269 -79
- package/Dev/i18n/pt/ReactNative/commands/generate-screen.md +491 -324
- package/Dev/i18n/pt/ReactNative/commands/native-module.md +539 -82
- package/Dev/i18n/pt/ReactNative/commands/store-prepare.md +429 -185
- package/Dev/i18n/pt/ReactNative/rules/02-architecture.md +1084 -31
- package/Dev/i18n/pt/Symfony/commands/check-compliance.md +195 -123
- package/Dev/i18n/pt/UIUX/commands/a11y-audit.md +208 -24
- package/Dev/i18n/pt/UIUX/commands/a11y-component.md +281 -23
- package/Dev/i18n/pt/UIUX/commands/design-tokens.md +197 -33
- package/Dev/i18n/pt/UIUX/commands/user-flow.md +183 -20
- package/Dev/i18n/pt/VueJS/commands/check-compliance.md +228 -103
- package/Dev/i18n/pt/Workflow/commands/analyze.md +147 -146
- package/Dev/i18n/pt/Workflow/commands/design.md +205 -204
- package/Dev/i18n/pt/Workflow/commands/implement.md +184 -184
- package/Project/i18n/de/Sprint/commands/dev.md +339 -86
- package/Project/i18n/es/Sprint/commands/dev.md +342 -91
- package/Project/i18n/pt/Sprint/commands/dev.md +343 -92
- package/bundles/cursor/.cursorrules +1 -1
- package/bundles/windsurf/.windsurfrules +1 -1
- package/package.json +1 -1
|
@@ -1,75 +1,218 @@
|
|
|
1
|
-
# React-Architektur
|
|
1
|
+
# React-Architektur – Prinzipien und Organisation
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Grundlegende Architekturprinzipien
|
|
4
4
|
|
|
5
|
-
###
|
|
5
|
+
### 1. Trennung der Zuständigkeiten
|
|
6
|
+
|
|
7
|
+
Jeder Teil des Codes sollte eine einzige, klar definierte Verantwortung haben:
|
|
8
|
+
|
|
9
|
+
- **Komponenten**: Anzeige und Benutzerinteraktion
|
|
10
|
+
- **Hooks**: Geschäftslogik und Zustandsverwaltung
|
|
11
|
+
- **Services**: API-Kommunikation
|
|
12
|
+
- **Utils**: Reine Hilfsfunktionen
|
|
13
|
+
- **Types**: TypeScript-Definitionen
|
|
14
|
+
|
|
15
|
+
### 2. Modularität
|
|
16
|
+
|
|
17
|
+
Der Code sollte in unabhängige und wiederverwendbare Module gegliedert werden.
|
|
18
|
+
|
|
19
|
+
### 3. Skalierbarkeit
|
|
20
|
+
|
|
21
|
+
Die Architektur sollte das Projektwachstum ohne größeres Refactoring unterstützen.
|
|
22
|
+
|
|
23
|
+
## Feature-basierte Ordnerstruktur
|
|
24
|
+
|
|
25
|
+
### Allgemeine Organisation
|
|
6
26
|
|
|
7
27
|
```
|
|
8
28
|
src/
|
|
9
|
-
├──
|
|
10
|
-
│ ├──
|
|
29
|
+
├── app/ # Anwendungskonfiguration
|
|
30
|
+
│ ├── App.tsx # Root-Komponente
|
|
31
|
+
│ ├── AppProviders.tsx # Globale Provider
|
|
32
|
+
│ └── router.tsx # Routing-Konfiguration
|
|
33
|
+
│
|
|
34
|
+
├── components/ # Gemeinsame Komponenten (Atomic Design)
|
|
35
|
+
│ ├── atoms/ # Atomare Komponenten
|
|
11
36
|
│ │ ├── Button/
|
|
37
|
+
│ │ │ ├── Button.tsx
|
|
38
|
+
│ │ │ ├── Button.test.tsx
|
|
39
|
+
│ │ │ ├── Button.stories.tsx
|
|
40
|
+
│ │ │ └── index.ts
|
|
12
41
|
│ │ ├── Input/
|
|
13
|
-
│ │ ├──
|
|
42
|
+
│ │ ├── Label/
|
|
43
|
+
│ │ ├── Icon/
|
|
14
44
|
│ │ └── Spinner/
|
|
15
|
-
│
|
|
45
|
+
│ │
|
|
46
|
+
│ ├── molecules/ # Molekulare Komponenten
|
|
16
47
|
│ │ ├── FormField/
|
|
48
|
+
│ │ │ ├── FormField.tsx
|
|
49
|
+
│ │ │ ├── FormField.test.tsx
|
|
50
|
+
│ │ │ └── index.ts
|
|
17
51
|
│ │ ├── SearchBar/
|
|
18
|
-
│ │
|
|
19
|
-
│ └──
|
|
20
|
-
│
|
|
21
|
-
│
|
|
22
|
-
│
|
|
52
|
+
│ │ ├── Card/
|
|
53
|
+
│ │ └── Modal/
|
|
54
|
+
│ │
|
|
55
|
+
│ ├── organisms/ # Organismus-Komponenten
|
|
56
|
+
│ │ ├── Header/
|
|
57
|
+
│ │ │ ├── Header.tsx
|
|
58
|
+
│ │ │ ├── Header.test.tsx
|
|
59
|
+
│ │ │ ├── components/ # Spezifische Unterkomponenten
|
|
60
|
+
│ │ │ │ ├── HeaderNav.tsx
|
|
61
|
+
│ │ │ │ └── UserMenu.tsx
|
|
62
|
+
│ │ │ └── index.ts
|
|
63
|
+
│ │ ├── Sidebar/
|
|
64
|
+
│ │ ├── DataTable/
|
|
65
|
+
│ │ └── Form/
|
|
66
|
+
│ │
|
|
67
|
+
│ └── templates/ # Seiten-Templates
|
|
68
|
+
│ ├── DashboardTemplate/
|
|
69
|
+
│ ├── AuthTemplate/
|
|
70
|
+
│ └── SettingsTemplate/
|
|
23
71
|
│
|
|
24
|
-
├── features/
|
|
72
|
+
├── features/ # Geschäftliche Features
|
|
25
73
|
│ ├── auth/
|
|
26
|
-
│ │ ├── components/
|
|
27
|
-
│ │ ├──
|
|
28
|
-
│ │ ├──
|
|
29
|
-
│ │ ├──
|
|
30
|
-
│ │ └──
|
|
74
|
+
│ │ ├── components/ # Authentifizierungsspezifische Komponenten
|
|
75
|
+
│ │ │ ├── LoginForm/
|
|
76
|
+
│ │ │ │ ├── LoginForm.tsx
|
|
77
|
+
│ │ │ │ ├── LoginForm.test.tsx
|
|
78
|
+
│ │ │ │ └── index.ts
|
|
79
|
+
│ │ │ ├── RegisterForm/
|
|
80
|
+
│ │ │ └── PasswordReset/
|
|
81
|
+
│ │ │
|
|
82
|
+
│ │ ├── hooks/ # Custom Hooks für Auth
|
|
83
|
+
│ │ │ ├── useAuth.ts
|
|
84
|
+
│ │ │ ├── useAuth.test.ts
|
|
85
|
+
│ │ │ ├── useLogin.ts
|
|
86
|
+
│ │ │ └── useRegister.ts
|
|
87
|
+
│ │ │
|
|
88
|
+
│ │ ├── services/ # API-Services für Auth
|
|
89
|
+
│ │ │ ├── auth.service.ts
|
|
90
|
+
│ │ │ ├── auth.service.test.ts
|
|
91
|
+
│ │ │ └── index.ts
|
|
92
|
+
│ │ │
|
|
93
|
+
│ │ ├── types/ # TypeScript-Typen
|
|
94
|
+
│ │ │ ├── auth.types.ts
|
|
95
|
+
│ │ │ ├── user.types.ts
|
|
96
|
+
│ │ │ └── index.ts
|
|
97
|
+
│ │ │
|
|
98
|
+
│ │ ├── utils/ # Spezifische Hilfsfunktionen
|
|
99
|
+
│ │ │ ├── tokenStorage.ts
|
|
100
|
+
│ │ │ ├── validators.ts
|
|
101
|
+
│ │ │ └── index.ts
|
|
102
|
+
│ │ │
|
|
103
|
+
│ │ ├── store/ # Lokale Zustandsverwaltung
|
|
104
|
+
│ │ │ ├── authStore.ts
|
|
105
|
+
│ │ │ └── index.ts
|
|
106
|
+
│ │ │
|
|
107
|
+
│ │ ├── constants/ # Konstanten
|
|
108
|
+
│ │ │ └── auth.constants.ts
|
|
109
|
+
│ │ │
|
|
110
|
+
│ │ └── index.ts # Feature-Einstiegspunkt
|
|
111
|
+
│ │
|
|
31
112
|
│ ├── users/
|
|
113
|
+
│ │ ├── components/
|
|
114
|
+
│ │ │ ├── UserList/
|
|
115
|
+
│ │ │ ├── UserProfile/
|
|
116
|
+
│ │ │ └── UserForm/
|
|
117
|
+
│ │ ├── hooks/
|
|
118
|
+
│ │ │ ├── useUsers.ts
|
|
119
|
+
│ │ │ ├── useUserMutations.ts
|
|
120
|
+
│ │ │ └── useUserFilters.ts
|
|
121
|
+
│ │ ├── services/
|
|
122
|
+
│ │ ├── types/
|
|
123
|
+
│ │ ├── utils/
|
|
124
|
+
│ │ └── index.ts
|
|
125
|
+
│ │
|
|
126
|
+
│ ├── products/
|
|
127
|
+
│ ├── orders/
|
|
32
128
|
│ └── dashboard/
|
|
33
129
|
│
|
|
34
|
-
├── hooks/
|
|
35
|
-
│ ├──
|
|
130
|
+
├── hooks/ # Globale wiederverwendbare Hooks
|
|
131
|
+
│ ├── useDebounce.ts
|
|
36
132
|
│ ├── useLocalStorage.ts
|
|
37
|
-
│
|
|
133
|
+
│ ├── useMediaQuery.ts
|
|
134
|
+
│ ├── useOnClickOutside.ts
|
|
135
|
+
│ ├── usePagination.ts
|
|
136
|
+
│ └── index.ts
|
|
38
137
|
│
|
|
39
|
-
├── services/
|
|
40
|
-
│ ├── api
|
|
41
|
-
│ ├──
|
|
42
|
-
│
|
|
138
|
+
├── services/ # Globale Services
|
|
139
|
+
│ ├── api/
|
|
140
|
+
│ │ ├── axios.config.ts # Axios-Konfiguration
|
|
141
|
+
│ │ ├── apiClient.ts # API-Client
|
|
142
|
+
│ │ └── interceptors.ts # Interceptors
|
|
143
|
+
│ ├── storage/
|
|
144
|
+
│ │ ├── localStorage.service.ts
|
|
145
|
+
│ │ └── sessionStorage.service.ts
|
|
146
|
+
│ ├── analytics/
|
|
147
|
+
│ │ └── analytics.service.ts
|
|
148
|
+
│ └── index.ts
|
|
43
149
|
│
|
|
44
|
-
├──
|
|
45
|
-
│ ├──
|
|
46
|
-
│ ├──
|
|
47
|
-
│
|
|
150
|
+
├── store/ # Globale Zustandsverwaltung
|
|
151
|
+
│ ├── slices/ # Zustand-Slices
|
|
152
|
+
│ │ ├── uiStore.ts
|
|
153
|
+
│ │ ├── themeStore.ts
|
|
154
|
+
│ │ └── notificationStore.ts
|
|
155
|
+
│ ├── index.ts
|
|
156
|
+
│ └── types.ts
|
|
48
157
|
│
|
|
49
|
-
├── types/
|
|
158
|
+
├── types/ # Globale Typen
|
|
159
|
+
│ ├── global.types.ts
|
|
50
160
|
│ ├── api.types.ts
|
|
51
|
-
│ ├──
|
|
52
|
-
│ └──
|
|
161
|
+
│ ├── common.types.ts
|
|
162
|
+
│ └── index.ts
|
|
53
163
|
│
|
|
54
|
-
├──
|
|
55
|
-
│ ├──
|
|
56
|
-
│ ├──
|
|
57
|
-
│
|
|
164
|
+
├── utils/ # Globale Hilfsfunktionen
|
|
165
|
+
│ ├── formatters/
|
|
166
|
+
│ │ ├── date.ts
|
|
167
|
+
│ │ ├── currency.ts
|
|
168
|
+
│ │ └── number.ts
|
|
169
|
+
│ ├── validators/
|
|
170
|
+
│ │ ├── email.ts
|
|
171
|
+
│ │ └── phone.ts
|
|
172
|
+
│ ├── helpers/
|
|
173
|
+
│ │ ├── array.ts
|
|
174
|
+
│ │ ├── object.ts
|
|
175
|
+
│ │ └── string.ts
|
|
176
|
+
│ └── index.ts
|
|
58
177
|
│
|
|
59
|
-
├── styles/
|
|
178
|
+
├── styles/ # Globale Stile
|
|
60
179
|
│ ├── globals.css
|
|
61
|
-
│
|
|
180
|
+
│ ├── variables.css
|
|
181
|
+
│ ├── theme.ts
|
|
182
|
+
│ └── tailwind.config.ts
|
|
62
183
|
│
|
|
63
|
-
├──
|
|
64
|
-
├──
|
|
65
|
-
|
|
184
|
+
├── config/ # Konfiguration
|
|
185
|
+
│ ├── env.ts # Umgebungsvariablen
|
|
186
|
+
│ ├── constants.ts # Globale Konstanten
|
|
187
|
+
│ ├── routes.ts # Routen-Definitionen
|
|
188
|
+
│ └── features.ts # Feature-Flags
|
|
189
|
+
│
|
|
190
|
+
├── assets/ # Statische Assets
|
|
191
|
+
│ ├── images/
|
|
192
|
+
│ ├── icons/
|
|
193
|
+
│ └── fonts/
|
|
194
|
+
│
|
|
195
|
+
├── lib/ # Konfigurierte Drittanbieter-Bibliotheken
|
|
196
|
+
│ ├── react-query/
|
|
197
|
+
│ │ └── queryClient.ts
|
|
198
|
+
│ ├── router/
|
|
199
|
+
│ │ └── router.config.ts
|
|
200
|
+
│ └── i18n/
|
|
201
|
+
│ └── i18n.config.ts
|
|
202
|
+
│
|
|
203
|
+
└── pages/ # Seiten (bei dateibasiertem Routing)
|
|
204
|
+
├── HomePage.tsx
|
|
205
|
+
├── DashboardPage.tsx
|
|
206
|
+
└── NotFoundPage.tsx
|
|
66
207
|
```
|
|
67
208
|
|
|
68
|
-
## Atomic
|
|
209
|
+
## Atomic-Design-Pattern
|
|
69
210
|
|
|
70
|
-
###
|
|
211
|
+
### Komponentenhierarchie
|
|
71
212
|
|
|
72
|
-
|
|
213
|
+
#### 1. Atoms (Atome)
|
|
214
|
+
|
|
215
|
+
**Grundlegendste Komponenten, nicht weiter zerlegbar.**
|
|
73
216
|
|
|
74
217
|
```typescript
|
|
75
218
|
// components/atoms/Button/Button.tsx
|
|
@@ -81,410 +224,428 @@ const buttonVariants = cva(
|
|
|
81
224
|
{
|
|
82
225
|
variants: {
|
|
83
226
|
variant: {
|
|
84
|
-
|
|
85
|
-
secondary: 'bg-
|
|
86
|
-
outline: 'border border-
|
|
227
|
+
primary: 'bg-blue-600 text-white hover:bg-blue-700',
|
|
228
|
+
secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
|
|
229
|
+
outline: 'border border-gray-300 bg-transparent hover:bg-gray-100',
|
|
230
|
+
ghost: 'hover:bg-gray-100',
|
|
231
|
+
danger: 'bg-red-600 text-white hover:bg-red-700'
|
|
87
232
|
},
|
|
88
233
|
size: {
|
|
89
234
|
sm: 'h-9 px-3 text-sm',
|
|
90
|
-
md: 'h-10 px-4',
|
|
235
|
+
md: 'h-10 px-4 text-base',
|
|
91
236
|
lg: 'h-11 px-8 text-lg'
|
|
92
237
|
}
|
|
93
238
|
},
|
|
94
239
|
defaultVariants: {
|
|
95
|
-
variant: '
|
|
240
|
+
variant: 'primary',
|
|
96
241
|
size: 'md'
|
|
97
242
|
}
|
|
98
243
|
}
|
|
99
244
|
);
|
|
100
245
|
|
|
101
|
-
interface ButtonProps
|
|
246
|
+
export interface ButtonProps
|
|
102
247
|
extends ButtonHTMLAttributes<HTMLButtonElement>,
|
|
103
|
-
VariantProps<typeof buttonVariants> {
|
|
248
|
+
VariantProps<typeof buttonVariants> {
|
|
249
|
+
isLoading?: boolean;
|
|
250
|
+
}
|
|
104
251
|
|
|
105
252
|
export const Button: FC<ButtonProps> = ({
|
|
106
253
|
variant,
|
|
107
254
|
size,
|
|
255
|
+
isLoading,
|
|
256
|
+
disabled,
|
|
257
|
+
children,
|
|
108
258
|
className,
|
|
109
259
|
...props
|
|
110
260
|
}) => {
|
|
111
261
|
return (
|
|
112
262
|
<button
|
|
113
263
|
className={buttonVariants({ variant, size, className })}
|
|
264
|
+
disabled={disabled || isLoading}
|
|
114
265
|
{...props}
|
|
115
|
-
|
|
266
|
+
>
|
|
267
|
+
{isLoading ? <Spinner size="sm" /> : children}
|
|
268
|
+
</button>
|
|
116
269
|
);
|
|
117
270
|
};
|
|
118
271
|
```
|
|
119
272
|
|
|
120
|
-
|
|
273
|
+
```typescript
|
|
274
|
+
// components/atoms/Input/Input.tsx
|
|
275
|
+
import { FC, InputHTMLAttributes, forwardRef } from 'react';
|
|
276
|
+
import { cn } from '@/utils/classnames';
|
|
121
277
|
|
|
122
|
-
|
|
278
|
+
export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
|
|
279
|
+
error?: boolean;
|
|
280
|
+
fullWidth?: boolean;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
284
|
+
({ error, fullWidth, className, ...props }, ref) => {
|
|
285
|
+
return (
|
|
286
|
+
<input
|
|
287
|
+
ref={ref}
|
|
288
|
+
className={cn(
|
|
289
|
+
'px-3 py-2 border rounded-md outline-none transition-colors',
|
|
290
|
+
'focus:ring-2 focus:ring-blue-500',
|
|
291
|
+
error && 'border-red-500 focus:ring-red-500',
|
|
292
|
+
fullWidth && 'w-full',
|
|
293
|
+
className
|
|
294
|
+
)}
|
|
295
|
+
{...props}
|
|
296
|
+
/>
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
Input.displayName = 'Input';
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
#### 2. Molecules (Moleküle)
|
|
305
|
+
|
|
306
|
+
**Kombination mehrerer Atome.**
|
|
123
307
|
|
|
124
308
|
```typescript
|
|
125
309
|
// components/molecules/FormField/FormField.tsx
|
|
126
|
-
import { FC,
|
|
127
|
-
import { Input } from '@/components/atoms/Input';
|
|
310
|
+
import { FC, ReactNode } from 'react';
|
|
311
|
+
import { Input, InputProps } from '@/components/atoms/Input';
|
|
128
312
|
import { Label } from '@/components/atoms/Label';
|
|
129
313
|
|
|
130
|
-
interface FormFieldProps extends
|
|
314
|
+
export interface FormFieldProps extends InputProps {
|
|
131
315
|
label: string;
|
|
132
316
|
error?: string;
|
|
133
317
|
helperText?: string;
|
|
318
|
+
required?: boolean;
|
|
134
319
|
}
|
|
135
320
|
|
|
136
321
|
export const FormField: FC<FormFieldProps> = ({
|
|
137
322
|
label,
|
|
138
323
|
error,
|
|
139
324
|
helperText,
|
|
325
|
+
required,
|
|
140
326
|
id,
|
|
141
327
|
...inputProps
|
|
142
328
|
}) => {
|
|
329
|
+
const inputId = id || `field-${label.toLowerCase().replace(/\s/g, '-')}`;
|
|
330
|
+
|
|
143
331
|
return (
|
|
144
|
-
<div className="space-y-
|
|
145
|
-
<Label htmlFor={
|
|
332
|
+
<div className="space-y-1">
|
|
333
|
+
<Label htmlFor={inputId} required={required}>
|
|
334
|
+
{label}
|
|
335
|
+
</Label>
|
|
146
336
|
<Input
|
|
147
|
-
id={
|
|
337
|
+
id={inputId}
|
|
338
|
+
error={!!error}
|
|
148
339
|
aria-invalid={!!error}
|
|
149
|
-
aria-describedby={error ? `${
|
|
340
|
+
aria-describedby={error ? `${inputId}-error` : undefined}
|
|
150
341
|
{...inputProps}
|
|
151
342
|
/>
|
|
152
343
|
{error && (
|
|
153
|
-
<p id={`${
|
|
344
|
+
<p id={`${inputId}-error`} className="text-sm text-red-600">
|
|
154
345
|
{error}
|
|
155
346
|
</p>
|
|
156
347
|
)}
|
|
157
348
|
{helperText && !error && (
|
|
158
|
-
<p className="text-sm text-
|
|
349
|
+
<p className="text-sm text-gray-500">{helperText}</p>
|
|
159
350
|
)}
|
|
160
351
|
</div>
|
|
161
352
|
);
|
|
162
353
|
};
|
|
163
354
|
```
|
|
164
355
|
|
|
165
|
-
|
|
356
|
+
## Container/Presenter-Pattern
|
|
166
357
|
|
|
167
|
-
|
|
358
|
+
### Trennung von Logik und Präsentation
|
|
168
359
|
|
|
169
|
-
|
|
170
|
-
// components/organisms/LoginForm/LoginForm.tsx
|
|
171
|
-
import { FC } from 'react';
|
|
172
|
-
import { useForm } from 'react-hook-form';
|
|
173
|
-
import { zodResolver } from '@hookform/resolvers/zod';
|
|
174
|
-
import { z } from 'zod';
|
|
175
|
-
import { FormField } from '@/components/molecules/FormField';
|
|
176
|
-
import { Button } from '@/components/atoms/Button';
|
|
177
|
-
|
|
178
|
-
const loginSchema = z.object({
|
|
179
|
-
email: z.string().email('Ungültige E-Mail'),
|
|
180
|
-
password: z.string().min(8, 'Passwort muss mindestens 8 Zeichen haben')
|
|
181
|
-
});
|
|
360
|
+
#### Container (Smart Component)
|
|
182
361
|
|
|
183
|
-
|
|
362
|
+
**Verwaltet Logik, Nebeneffekte und Zustand.**
|
|
184
363
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
364
|
+
```typescript
|
|
365
|
+
// features/users/components/UserList/UserListContainer.tsx
|
|
366
|
+
import { FC } from 'react';
|
|
367
|
+
import { useUsers } from '@/features/users/hooks/useUsers';
|
|
368
|
+
import { UserListPresenter } from './UserListPresenter';
|
|
189
369
|
|
|
190
|
-
export const
|
|
370
|
+
export const UserListContainer: FC = () => {
|
|
191
371
|
const {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
372
|
+
users,
|
|
373
|
+
isLoading,
|
|
374
|
+
error,
|
|
375
|
+
pagination,
|
|
376
|
+
handlePageChange,
|
|
377
|
+
handleSearch,
|
|
378
|
+
handleSort
|
|
379
|
+
} = useUsers();
|
|
380
|
+
|
|
381
|
+
if (error) {
|
|
382
|
+
return <ErrorMessage error={error} />;
|
|
383
|
+
}
|
|
198
384
|
|
|
199
385
|
return (
|
|
200
|
-
<
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
<FormField
|
|
209
|
-
label="Passwort"
|
|
210
|
-
type="password"
|
|
211
|
-
error={errors.password?.message}
|
|
212
|
-
{...register('password')}
|
|
213
|
-
/>
|
|
214
|
-
|
|
215
|
-
<Button type="submit" className="w-full" disabled={isLoading}>
|
|
216
|
-
{isLoading ? 'Anmeldung läuft...' : 'Anmelden'}
|
|
217
|
-
</Button>
|
|
218
|
-
</form>
|
|
386
|
+
<UserListPresenter
|
|
387
|
+
users={users}
|
|
388
|
+
isLoading={isLoading}
|
|
389
|
+
pagination={pagination}
|
|
390
|
+
onPageChange={handlePageChange}
|
|
391
|
+
onSearch={handleSearch}
|
|
392
|
+
onSort={handleSort}
|
|
393
|
+
/>
|
|
219
394
|
);
|
|
220
395
|
};
|
|
221
396
|
```
|
|
222
397
|
|
|
223
|
-
|
|
398
|
+
#### Presenter (Dumb Component)
|
|
224
399
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
Jedes Feature ist selbständig mit eigenen Komponenten, Hooks und Services.
|
|
400
|
+
**Nur Anzeige, empfängt alles über Props.**
|
|
228
401
|
|
|
229
402
|
```typescript
|
|
230
|
-
// features/users/index.ts
|
|
231
|
-
export { UserList } from './components/UserList';
|
|
232
|
-
export { UserForm } from './components/UserForm';
|
|
233
|
-
export { useUsers, useCreateUser } from './hooks/useUsers';
|
|
234
|
-
export type { User, CreateUserInput } from './types';
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### Feature-Komponente mit Container/Presenter
|
|
238
|
-
|
|
239
|
-
```typescript
|
|
240
|
-
// features/users/components/UserList/UserListContainer.tsx
|
|
241
|
-
import { FC } from 'react';
|
|
242
|
-
import { useUsers } from '../../hooks/useUsers';
|
|
243
|
-
import { UserListPresenter } from './UserListPresenter';
|
|
244
|
-
import { Spinner } from '@/components/atoms/Spinner';
|
|
245
|
-
import { ErrorMessage } from '@/components/atoms/ErrorMessage';
|
|
246
|
-
|
|
247
|
-
export const UserListContainer: FC = () => {
|
|
248
|
-
const { data: users, isLoading, error } = useUsers();
|
|
249
|
-
|
|
250
|
-
if (isLoading) return <Spinner />;
|
|
251
|
-
if (error) return <ErrorMessage error={error} />;
|
|
252
|
-
if (!users) return null;
|
|
253
|
-
|
|
254
|
-
return <UserListPresenter users={users} />;
|
|
255
|
-
};
|
|
256
|
-
|
|
257
403
|
// features/users/components/UserList/UserListPresenter.tsx
|
|
258
404
|
import { FC } from 'react';
|
|
259
|
-
import
|
|
260
|
-
import {
|
|
405
|
+
import { User } from '@/features/users/types';
|
|
406
|
+
import { DataTable } from '@/components/organisms/DataTable';
|
|
407
|
+
import { SearchBar } from '@/components/molecules/SearchBar';
|
|
408
|
+
import { Pagination } from '@/components/molecules/Pagination';
|
|
261
409
|
|
|
262
|
-
interface UserListPresenterProps {
|
|
410
|
+
export interface UserListPresenterProps {
|
|
263
411
|
users: User[];
|
|
412
|
+
isLoading: boolean;
|
|
413
|
+
pagination: PaginationState;
|
|
414
|
+
onPageChange: (page: number) => void;
|
|
415
|
+
onSearch: (query: string) => void;
|
|
416
|
+
onSort: (field: string) => void;
|
|
264
417
|
}
|
|
265
418
|
|
|
266
|
-
export const UserListPresenter: FC<UserListPresenterProps> = ({
|
|
419
|
+
export const UserListPresenter: FC<UserListPresenterProps> = ({
|
|
420
|
+
users,
|
|
421
|
+
isLoading,
|
|
422
|
+
pagination,
|
|
423
|
+
onPageChange,
|
|
424
|
+
onSearch,
|
|
425
|
+
onSort
|
|
426
|
+
}) => {
|
|
427
|
+
const columns = useMemo(() => [
|
|
428
|
+
{
|
|
429
|
+
accessorKey: 'name',
|
|
430
|
+
header: 'Name'
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
accessorKey: 'email',
|
|
434
|
+
header: 'E-Mail'
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
accessorKey: 'role',
|
|
438
|
+
header: 'Rolle'
|
|
439
|
+
}
|
|
440
|
+
], []);
|
|
441
|
+
|
|
267
442
|
return (
|
|
268
|
-
<div className="
|
|
269
|
-
{
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
443
|
+
<div className="space-y-4">
|
|
444
|
+
<SearchBar onSearch={onSearch} placeholder="Benutzer suchen..." />
|
|
445
|
+
|
|
446
|
+
{isLoading ? (
|
|
447
|
+
<Skeleton />
|
|
448
|
+
) : (
|
|
449
|
+
<DataTable data={users} columns={columns} />
|
|
450
|
+
)}
|
|
451
|
+
|
|
452
|
+
<Pagination
|
|
453
|
+
currentPage={pagination.currentPage}
|
|
454
|
+
totalPages={pagination.totalPages}
|
|
455
|
+
onPageChange={onPageChange}
|
|
456
|
+
/>
|
|
275
457
|
</div>
|
|
276
458
|
);
|
|
277
459
|
};
|
|
278
460
|
```
|
|
279
461
|
|
|
280
|
-
##
|
|
281
|
-
|
|
282
|
-
### Lokaler State (useState)
|
|
462
|
+
## Organisation von Custom Hooks
|
|
283
463
|
|
|
284
|
-
|
|
464
|
+
### Hook-Struktur
|
|
285
465
|
|
|
286
466
|
```typescript
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
### Server State (TanStack Query)
|
|
467
|
+
// hooks/useExample/useExample.ts
|
|
468
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
292
469
|
|
|
293
|
-
|
|
470
|
+
export interface UseExampleOptions {
|
|
471
|
+
initialValue?: string;
|
|
472
|
+
onSuccess?: (data: string) => void;
|
|
473
|
+
}
|
|
294
474
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
queryKey: ['users'],
|
|
303
|
-
queryFn: () => userService.getAll()
|
|
304
|
-
});
|
|
305
|
-
};
|
|
475
|
+
export interface UseExampleReturn {
|
|
476
|
+
value: string;
|
|
477
|
+
isLoading: boolean;
|
|
478
|
+
error: Error | null;
|
|
479
|
+
update: (newValue: string) => void;
|
|
480
|
+
reset: () => void;
|
|
481
|
+
}
|
|
306
482
|
|
|
307
|
-
|
|
308
|
-
|
|
483
|
+
/**
|
|
484
|
+
* Custom Hook zur Verwaltung von [Beschreibung]
|
|
485
|
+
*
|
|
486
|
+
* @param options - Konfigurationsoptionen
|
|
487
|
+
* @returns Zustand und Methoden zur Verwaltung von [Funktionalität]
|
|
488
|
+
*
|
|
489
|
+
* @example
|
|
490
|
+
* ```tsx
|
|
491
|
+
* const { value, update } = useExample({ initialValue: 'test' });
|
|
492
|
+
* ```
|
|
493
|
+
*/
|
|
494
|
+
export const useExample = (
|
|
495
|
+
options: UseExampleOptions = {}
|
|
496
|
+
): UseExampleReturn => {
|
|
497
|
+
const { initialValue = '', onSuccess } = options;
|
|
498
|
+
|
|
499
|
+
const [value, setValue] = useState(initialValue);
|
|
500
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
501
|
+
const [error, setError] = useState<Error | null>(null);
|
|
502
|
+
|
|
503
|
+
const update = useCallback(
|
|
504
|
+
async (newValue: string) => {
|
|
505
|
+
setIsLoading(true);
|
|
506
|
+
setError(null);
|
|
507
|
+
|
|
508
|
+
try {
|
|
509
|
+
// Geschäftslogik
|
|
510
|
+
setValue(newValue);
|
|
511
|
+
onSuccess?.(newValue);
|
|
512
|
+
} catch (err) {
|
|
513
|
+
setError(err as Error);
|
|
514
|
+
} finally {
|
|
515
|
+
setIsLoading(false);
|
|
516
|
+
}
|
|
517
|
+
},
|
|
518
|
+
[onSuccess]
|
|
519
|
+
);
|
|
309
520
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
521
|
+
const reset = useCallback(() => {
|
|
522
|
+
setValue(initialValue);
|
|
523
|
+
setError(null);
|
|
524
|
+
}, [initialValue]);
|
|
525
|
+
|
|
526
|
+
return {
|
|
527
|
+
value,
|
|
528
|
+
isLoading,
|
|
529
|
+
error,
|
|
530
|
+
update,
|
|
531
|
+
reset
|
|
532
|
+
};
|
|
316
533
|
};
|
|
317
534
|
```
|
|
318
535
|
|
|
319
|
-
|
|
536
|
+
## Bewährte Architekturpraktiken
|
|
320
537
|
|
|
321
|
-
|
|
538
|
+
### 1. Index-Barrel-Dateien
|
|
322
539
|
|
|
323
|
-
|
|
324
|
-
// stores/useAuthStore.ts
|
|
325
|
-
import { create } from 'zustand';
|
|
326
|
-
import { persist } from 'zustand/middleware';
|
|
327
|
-
|
|
328
|
-
interface AuthState {
|
|
329
|
-
user: User | null;
|
|
330
|
-
token: string | null;
|
|
331
|
-
login: (user: User, token: string) => void;
|
|
332
|
-
logout: () => void;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
export const useAuthStore = create<AuthState>()(
|
|
336
|
-
persist(
|
|
337
|
-
(set) => ({
|
|
338
|
-
user: null,
|
|
339
|
-
token: null,
|
|
340
|
-
login: (user, token) => set({ user, token }),
|
|
341
|
-
logout: () => set({ user: null, token: null })
|
|
342
|
-
}),
|
|
343
|
-
{
|
|
344
|
-
name: 'auth-storage'
|
|
345
|
-
}
|
|
346
|
-
)
|
|
347
|
-
);
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
## Routing-Architektur
|
|
351
|
-
|
|
352
|
-
### Route-Konfiguration
|
|
540
|
+
**Imports mit Index-Dateien vereinfachen.**
|
|
353
541
|
|
|
354
542
|
```typescript
|
|
355
|
-
//
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
import { Layout } from './components/Layout';
|
|
543
|
+
// features/users/components/index.ts
|
|
544
|
+
export { UserList } from './UserList';
|
|
545
|
+
export { UserProfile } from './UserProfile';
|
|
546
|
+
export { UserForm } from './UserForm';
|
|
360
547
|
|
|
361
|
-
//
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
548
|
+
// features/users/hooks/index.ts
|
|
549
|
+
export { useUsers, useUser } from './useUsers';
|
|
550
|
+
export { useCreateUser, useUpdateUser, useDeleteUser } from './useUserMutations';
|
|
551
|
+
export { useUserFilters } from './useUserFilters';
|
|
365
552
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
children: [
|
|
371
|
-
{
|
|
372
|
-
index: true,
|
|
373
|
-
element: <HomePage />
|
|
374
|
-
},
|
|
375
|
-
{
|
|
376
|
-
path: 'dashboard',
|
|
377
|
-
element: (
|
|
378
|
-
<ProtectedRoute>
|
|
379
|
-
<DashboardPage />
|
|
380
|
-
</ProtectedRoute>
|
|
381
|
-
)
|
|
382
|
-
},
|
|
383
|
-
{
|
|
384
|
-
path: 'users',
|
|
385
|
-
element: (
|
|
386
|
-
<ProtectedRoute>
|
|
387
|
-
<UsersPage />
|
|
388
|
-
</ProtectedRoute>
|
|
389
|
-
)
|
|
390
|
-
}
|
|
391
|
-
]
|
|
392
|
-
}
|
|
393
|
-
]);
|
|
553
|
+
// features/users/index.ts
|
|
554
|
+
export * from './components';
|
|
555
|
+
export * from './hooks';
|
|
556
|
+
export * from './types';
|
|
394
557
|
```
|
|
395
558
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
### Service Layer
|
|
399
|
-
|
|
559
|
+
**Verwendung**:
|
|
400
560
|
```typescript
|
|
401
|
-
//
|
|
402
|
-
import
|
|
403
|
-
|
|
404
|
-
export class ApiService {
|
|
405
|
-
private client: AxiosInstance;
|
|
561
|
+
// Statt mehrerer Imports
|
|
562
|
+
import { UserList } from '@/features/users/components/UserList';
|
|
563
|
+
import { UserProfile } from '@/features/users/components/UserProfile';
|
|
406
564
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
timeout: 10000
|
|
411
|
-
});
|
|
412
|
-
}
|
|
565
|
+
// Ein einzelner Import
|
|
566
|
+
import { UserList, UserProfile } from '@/features/users/components';
|
|
567
|
+
```
|
|
413
568
|
|
|
414
|
-
|
|
415
|
-
const response = await this.client.get<T>(url);
|
|
416
|
-
return response.data;
|
|
417
|
-
}
|
|
569
|
+
### 2. Absolute Imports
|
|
418
570
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
571
|
+
**tsconfig.json-Konfiguration**:
|
|
572
|
+
|
|
573
|
+
```json
|
|
574
|
+
{
|
|
575
|
+
"compilerOptions": {
|
|
576
|
+
"baseUrl": ".",
|
|
577
|
+
"paths": {
|
|
578
|
+
"@/*": ["./src/*"],
|
|
579
|
+
"@/components/*": ["./src/components/*"],
|
|
580
|
+
"@/features/*": ["./src/features/*"],
|
|
581
|
+
"@/hooks/*": ["./src/hooks/*"],
|
|
582
|
+
"@/utils/*": ["./src/utils/*"],
|
|
583
|
+
"@/types/*": ["./src/types/*"]
|
|
584
|
+
}
|
|
422
585
|
}
|
|
423
586
|
}
|
|
424
|
-
|
|
425
|
-
export const apiService = new ApiService(
|
|
426
|
-
import.meta.env.VITE_API_BASE_URL
|
|
427
|
-
);
|
|
428
587
|
```
|
|
429
588
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
### 1. Komponenten-Aufteilung
|
|
433
|
-
|
|
589
|
+
**Verwendung**:
|
|
434
590
|
```typescript
|
|
435
|
-
//
|
|
436
|
-
|
|
437
|
-
return (
|
|
438
|
-
<Card>
|
|
439
|
-
<UserAvatar user={user} />
|
|
440
|
-
<UserInfo user={user} />
|
|
441
|
-
<UserActions user={user} />
|
|
442
|
-
</Card>
|
|
443
|
-
);
|
|
444
|
-
};
|
|
445
|
-
|
|
446
|
-
// ❌ Schlecht - Monolithische Komponente
|
|
447
|
-
export const UserCard: FC<{ user: User }> = ({ user }) => {
|
|
448
|
-
return (
|
|
449
|
-
<Card>
|
|
450
|
-
{/* 200 Zeilen Code... */}
|
|
451
|
-
</Card>
|
|
452
|
-
);
|
|
453
|
-
};
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
### 2. Absolute Imports
|
|
591
|
+
// ❌ Schlecht – relative Imports
|
|
592
|
+
import { Button } from '../../../components/atoms/Button';
|
|
457
593
|
|
|
458
|
-
|
|
459
|
-
// ✅ Gut - Absolute Imports
|
|
594
|
+
// ✅ Gut – absolute Imports
|
|
460
595
|
import { Button } from '@/components/atoms/Button';
|
|
461
|
-
import { useAuth } from '@/hooks/useAuth';
|
|
462
|
-
|
|
463
|
-
// ❌ Schlecht - Relative Imports
|
|
464
|
-
import { Button } from '../../../components/atoms/Button';
|
|
465
|
-
import { useAuth } from '../../hooks/useAuth';
|
|
466
596
|
```
|
|
467
597
|
|
|
468
|
-
### 3.
|
|
598
|
+
### 3. Lazy Loading
|
|
599
|
+
|
|
600
|
+
**Code-Splitting nach Route**:
|
|
469
601
|
|
|
470
602
|
```typescript
|
|
471
|
-
//
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
603
|
+
// app/router.tsx
|
|
604
|
+
import { lazy, Suspense } from 'react';
|
|
605
|
+
import { createBrowserRouter } from 'react-router-dom';
|
|
606
|
+
import { Spinner } from '@/components/atoms/Spinner';
|
|
475
607
|
|
|
476
|
-
//
|
|
477
|
-
|
|
608
|
+
// Seiten lazy laden
|
|
609
|
+
const HomePage = lazy(() => import('@/pages/HomePage'));
|
|
610
|
+
const DashboardPage = lazy(() => import('@/pages/DashboardPage'));
|
|
611
|
+
const UsersPage = lazy(() => import('@/features/users/pages/UsersPage'));
|
|
612
|
+
|
|
613
|
+
export const router = createBrowserRouter([
|
|
614
|
+
{
|
|
615
|
+
path: '/',
|
|
616
|
+
element: (
|
|
617
|
+
<Suspense fallback={<Spinner />}>
|
|
618
|
+
<HomePage />
|
|
619
|
+
</Suspense>
|
|
620
|
+
)
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
path: '/dashboard',
|
|
624
|
+
element: (
|
|
625
|
+
<Suspense fallback={<Spinner />}>
|
|
626
|
+
<DashboardPage />
|
|
627
|
+
</Suspense>
|
|
628
|
+
)
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
path: '/users',
|
|
632
|
+
element: (
|
|
633
|
+
<Suspense fallback={<Spinner />}>
|
|
634
|
+
<UsersPage />
|
|
635
|
+
</Suspense>
|
|
636
|
+
)
|
|
637
|
+
}
|
|
638
|
+
]);
|
|
478
639
|
```
|
|
479
640
|
|
|
480
641
|
## Fazit
|
|
481
642
|
|
|
482
|
-
Eine
|
|
643
|
+
Eine gut durchdachte Architektur ist die Grundlage einer wartbaren und skalierbaren React-Anwendung. Wichtigste Prinzipien:
|
|
483
644
|
|
|
484
|
-
1. ✅ **
|
|
485
|
-
2. ✅ **
|
|
486
|
-
3. ✅ **
|
|
487
|
-
4. ✅ **
|
|
488
|
-
5. ✅ **
|
|
645
|
+
1. ✅ **Feature-basiert**: Organisation nach geschäftlichen Funktionalitäten
|
|
646
|
+
2. ✅ **Atomic Design**: Klare Komponentenhierarchie
|
|
647
|
+
3. ✅ **Trennung der Zuständigkeiten**: Container/Presenter, Hooks, Services
|
|
648
|
+
4. ✅ **Modularität**: Wiederverwendbare Komponenten und Hooks
|
|
649
|
+
5. ✅ **Skalierbarkeit**: Struktur, die mit dem Projekt wächst
|
|
489
650
|
|
|
490
|
-
**Goldene Regel**:
|
|
651
|
+
**Goldene Regel**: Jede Datei, jeder Ordner und jede Komponente sollte eine einzige, klare Verantwortung haben.
|