ntc-dev-kit 1.0.0__py3-none-any.whl
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.
- ntc/__init__.py +5 -0
- ntc/app_creator.py +296 -0
- ntc/cli.py +4 -0
- ntc/cli_enhanced.py +820 -0
- ntc/composer/__init__.py +0 -0
- ntc/composer/main.py +443 -0
- ntc/deployment/__init__.py +0 -0
- ntc/templates/__init__.py +2 -0
- ntc/templates/auth_app/__init__.py +0 -0
- ntc/templates/auth_app/admin.py +24 -0
- ntc/templates/auth_app/apps.py +6 -0
- ntc/templates/auth_app/models.py +30 -0
- ntc/templates/auth_app/serializers.py +101 -0
- ntc/templates/auth_app/urls.py +19 -0
- ntc/templates/auth_app/views.py +175 -0
- ntc/templates/drf_structure/__init__.py +1 -0
- ntc/templates/drf_structure/drf_structure.md +83 -0
- ntc/templates/drf_structure/main.py +389 -0
- ntc/templates/drf_structure/structure.json +126 -0
- ntc/templates/drf_structure/templates/.entry/start_core_backend.sh +37 -0
- ntc/templates/drf_structure/templates/.entry/start_tasks_backend.sh +30 -0
- ntc/templates/drf_structure/templates/CLAUDE.md +194 -0
- ntc/templates/drf_structure/templates/README.md +117 -0
- ntc/templates/drf_structure/templates/apps/app_admin.py +3 -0
- ntc/templates/drf_structure/templates/apps/app_apps.py +6 -0
- ntc/templates/drf_structure/templates/apps/app_models.py +4 -0
- ntc/templates/drf_structure/templates/apps/app_urls.py +8 -0
- ntc/templates/drf_structure/templates/apps/app_views.py +6 -0
- ntc/templates/drf_structure/templates/apps/common_apps.py +6 -0
- ntc/templates/drf_structure/templates/apps/common_models.py +8 -0
- ntc/templates/drf_structure/templates/config/gunicorn_config.py +9 -0
- ntc/templates/drf_structure/templates/config/http.conf +33 -0
- ntc/templates/drf_structure/templates/core/base_model.py +9 -0
- ntc/templates/drf_structure/templates/core/formatter.py +5 -0
- ntc/templates/drf_structure/templates/core/logger.py +8 -0
- ntc/templates/drf_structure/templates/core/pagination.py +6 -0
- ntc/templates/drf_structure/templates/core/response_wrapper.py +7 -0
- ntc/templates/drf_structure/templates/core/services/azure/email/backend.py +108 -0
- ntc/templates/drf_structure/templates/core/services/azure/email/client.py +296 -0
- ntc/templates/drf_structure/templates/core/services/azure/email/notifier.py +102 -0
- ntc/templates/drf_structure/templates/core/services/object_storage.py +285 -0
- ntc/templates/drf_structure/templates/django_project/asgi.py +5 -0
- ntc/templates/drf_structure/templates/django_project/celery.py +8 -0
- ntc/templates/drf_structure/templates/django_project/dev_settings.py +2 -0
- ntc/templates/drf_structure/templates/django_project/django_settings.txt +322 -0
- ntc/templates/drf_structure/templates/django_project/management_command.py +114 -0
- ntc/templates/drf_structure/templates/django_project/prod_settings.py +3 -0
- ntc/templates/drf_structure/templates/django_project/schema.py +7 -0
- ntc/templates/drf_structure/templates/django_project/urls.py +6 -0
- ntc/templates/drf_structure/templates/django_project/wsgi.py +5 -0
- ntc/templates/drf_structure/templates/docker/docker-compose.yml +11 -0
- ntc/templates/drf_structure/templates/knowledge/knowledge_description.md +201 -0
- ntc/templates/drf_structure/templates/knowledge/knowledge_design_pattern.md +722 -0
- ntc/templates/drf_structure/templates/knowledge/knowledge_structure.md +151 -0
- ntc/templates/drf_structure/templates/root/README.md +307 -0
- ntc/templates/drf_structure/templates/root/django_env.txt +98 -0
- ntc/templates/drf_structure/templates/root/django_requirements.txt +24 -0
- ntc/templates/drf_structure/templates/root/gitignore.txt +6 -0
- ntc/templates/drf_structure/templates/root/manage.py +11 -0
- ntc/templates/flutter/.gitignore +44 -0
- ntc/templates/flutter/.knowledge/knowledge_description.md +215 -0
- ntc/templates/flutter/.knowledge/knowledge_design_pattern.md +310 -0
- ntc/templates/flutter/.knowledge/knowledge_structure.md +83 -0
- ntc/templates/flutter/CLAUDE.md +190 -0
- ntc/templates/flutter/__init__.py +0 -0
- ntc/templates/flutter/analysis_options.yaml +9 -0
- ntc/templates/flutter/main.py +187 -0
- ntc/templates/flutter/pubspec.yaml +65 -0
- ntc/templates/flutter/structure.json +51 -0
- ntc/templates/nextjs/.env.example +10 -0
- ntc/templates/nextjs/.gitignore +36 -0
- ntc/templates/nextjs/CLAUDE.md +181 -0
- ntc/templates/nextjs/README.md +94 -0
- ntc/templates/nextjs/__init__.py +0 -0
- ntc/templates/nextjs/knowledge/knowledge_description.md +208 -0
- ntc/templates/nextjs/knowledge/knowledge_design_pattern.md +460 -0
- ntc/templates/nextjs/knowledge/knowledge_structure.md +75 -0
- ntc/templates/nextjs/main.py +224 -0
- ntc/templates/nextjs/package.json +25 -0
- ntc/templates/nextjs/src/app/globals.css +26 -0
- ntc/templates/nextjs/src/app/layout.tsx +59 -0
- ntc/templates/nextjs/src/app/page.tsx +21 -0
- ntc/templates/nextjs/src/components/Header.tsx +16 -0
- ntc/templates/nextjs/src/services/api.ts +81 -0
- ntc/templates/nextjs/src/utils/constants.ts +1 -0
- ntc/templates/nextjs/structure.json +32 -0
- ntc/templates/nextjs/tsconfig.json +27 -0
- ntc/templates/react_admin/.env.example +1 -0
- ntc/templates/react_admin/.gitignore +10 -0
- ntc/templates/react_admin/CLAUDE.md +210 -0
- ntc/templates/react_admin/README.md +72 -0
- ntc/templates/react_admin/__init__.py +0 -0
- ntc/templates/react_admin/index.html +13 -0
- ntc/templates/react_admin/knowledge/knowledge_description.md +202 -0
- ntc/templates/react_admin/knowledge/knowledge_design_pattern.md +448 -0
- ntc/templates/react_admin/knowledge/knowledge_structure.md +97 -0
- ntc/templates/react_admin/main.py +208 -0
- ntc/templates/react_admin/package.json +35 -0
- ntc/templates/react_admin/src/App.tsx +60 -0
- ntc/templates/react_admin/src/components/ProtectedRoute.tsx +16 -0
- ntc/templates/react_admin/src/components/Sidebar.tsx +90 -0
- ntc/templates/react_admin/src/components/TopNav.tsx +44 -0
- ntc/templates/react_admin/src/context/AuthContext.tsx +107 -0
- ntc/templates/react_admin/src/context/ThemeContext.tsx +34 -0
- ntc/templates/react_admin/src/context/WorkspaceContext.tsx +28 -0
- ntc/templates/react_admin/src/hooks/useApi.ts +39 -0
- ntc/templates/react_admin/src/index.css +34 -0
- ntc/templates/react_admin/src/layouts/AuthLayout.tsx +23 -0
- ntc/templates/react_admin/src/layouts/DashboardLayout.tsx +24 -0
- ntc/templates/react_admin/src/main.tsx +5 -0
- ntc/templates/react_admin/src/pages/DashboardPage.tsx +10 -0
- ntc/templates/react_admin/src/pages/auth/LoginPage.tsx +79 -0
- ntc/templates/react_admin/src/pages/auth/RegisterPage.tsx +93 -0
- ntc/templates/react_admin/src/services/api.service.ts +44 -0
- ntc/templates/react_admin/src/services/base.service.ts +193 -0
- ntc/templates/react_admin/structure.json +52 -0
- ntc/templates/react_admin/tsconfig.app.json +25 -0
- ntc/templates/react_admin/tsconfig.json +7 -0
- ntc/templates/react_admin/tsconfig.node.json +19 -0
- ntc/templates/react_admin/vite.config.ts +13 -0
- ntc/templates/react_structure/CLAUDE.md +176 -0
- ntc/templates/react_structure/__init__.py +1 -0
- ntc/templates/react_structure/index.css +21 -0
- ntc/templates/react_structure/index.html +13 -0
- ntc/templates/react_structure/knowledge/knowledge_description.md +170 -0
- ntc/templates/react_structure/knowledge/knowledge_design_pattern.md +411 -0
- ntc/templates/react_structure/knowledge/knowledge_structure.md +74 -0
- ntc/templates/react_structure/main.py +217 -0
- ntc/templates/react_structure/package.json +32 -0
- ntc/templates/react_structure/src/App.tsx +63 -0
- ntc/templates/react_structure/src/components/Header.tsx +51 -0
- ntc/templates/react_structure/src/main.tsx +5 -0
- ntc/templates/react_structure/src/services/api.ts +90 -0
- ntc/templates/react_structure/src/utils/constants.ts +1 -0
- ntc/templates/react_structure/structure.json +65 -0
- ntc/templates/react_structure/tsconfig.app.json +25 -0
- ntc/templates/react_structure/tsconfig.json +7 -0
- ntc/templates/react_structure/tsconfig.node.json +19 -0
- ntc/templates/react_structure/vite.config.ts +12 -0
- ntc/utils/__init__.py +1 -0
- ntc/utils/environment.py +67 -0
- ntc/utils/output.py +41 -0
- ntc_dev_kit-1.0.0.dist-info/METADATA +188 -0
- ntc_dev_kit-1.0.0.dist-info/RECORD +148 -0
- ntc_dev_kit-1.0.0.dist-info/WHEEL +5 -0
- ntc_dev_kit-1.0.0.dist-info/entry_points.txt +2 -0
- ntc_dev_kit-1.0.0.dist-info/licenses/LICENSE +21 -0
- ntc_dev_kit-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
# Design Patterns and Coding Conventions - {project_name}
|
|
2
|
+
|
|
3
|
+
This document describes the design patterns and coding conventions used in this NTC-generated React + TypeScript + Vite project.
|
|
4
|
+
|
|
5
|
+
## 🎨 Component Design Patterns
|
|
6
|
+
|
|
7
|
+
### Functional Component Pattern
|
|
8
|
+
```typescript
|
|
9
|
+
interface ComponentProps {
|
|
10
|
+
title: string
|
|
11
|
+
onAction: () => void
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const Component: React.FC<ComponentProps> = ({ title, onAction }) => {
|
|
15
|
+
// Component logic
|
|
16
|
+
return <div>{title}</div>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default Component
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Custom Hook Pattern
|
|
23
|
+
```typescript
|
|
24
|
+
const useCustomHook = (param: string) => {
|
|
25
|
+
const [state, setState] = useState<string>('')
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
// Effect logic
|
|
29
|
+
}, [param])
|
|
30
|
+
|
|
31
|
+
return state
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Container/Presenter Pattern
|
|
36
|
+
```typescript
|
|
37
|
+
// Container component (handles logic)
|
|
38
|
+
const DataContainer: React.FC = () => {
|
|
39
|
+
const { data, loading, error } = useData()
|
|
40
|
+
|
|
41
|
+
if (loading) return <LoadingSpinner />
|
|
42
|
+
if (error) return <ErrorMessage />
|
|
43
|
+
|
|
44
|
+
return <DataPresenter data={data} />
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Presenter component (handles UI)
|
|
48
|
+
const DataPresenter: React.FC<{ data: DataType }> = ({ data }) => {
|
|
49
|
+
return <div>{data.name}</div>
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 🔧 Service Layer Pattern
|
|
54
|
+
|
|
55
|
+
### API Service Pattern
|
|
56
|
+
```typescript
|
|
57
|
+
// Centralized API service
|
|
58
|
+
export const apiService = {
|
|
59
|
+
get: (url: string, config = {}) => apiClient.get(url, config),
|
|
60
|
+
post: (url: string, data = {}, config = {}) => apiClient.post(url, data, config),
|
|
61
|
+
// ... other methods
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Auth Service Pattern
|
|
66
|
+
```typescript
|
|
67
|
+
export const authService = {
|
|
68
|
+
login: async (credentials: LoginCredentials) => {
|
|
69
|
+
const response = await apiService.post('/auth/login/', credentials)
|
|
70
|
+
if (response.data.token) {
|
|
71
|
+
localStorage.setItem('authToken', response.data.token)
|
|
72
|
+
}
|
|
73
|
+
return response
|
|
74
|
+
},
|
|
75
|
+
logout: () => {
|
|
76
|
+
localStorage.removeItem('authToken')
|
|
77
|
+
},
|
|
78
|
+
// ... other methods
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## 📝 TypeScript Patterns
|
|
83
|
+
|
|
84
|
+
### Interface for Props
|
|
85
|
+
```typescript
|
|
86
|
+
interface ButtonProps {
|
|
87
|
+
label: string
|
|
88
|
+
onClick: () => void
|
|
89
|
+
disabled?: boolean
|
|
90
|
+
variant?: 'primary' | 'secondary'
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const Button: React.FC<ButtonProps> = ({ label, onClick, disabled = false, variant = 'primary' }) => {
|
|
94
|
+
return <button onClick={onClick} disabled={disabled}>{label}</button>
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Type Guards
|
|
99
|
+
```typescript
|
|
100
|
+
const isString = (value: unknown): value is string => {
|
|
101
|
+
return typeof value === 'string'
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const processValue = (value: unknown) => {
|
|
105
|
+
if (isString(value)) {
|
|
106
|
+
return value.toUpperCase()
|
|
107
|
+
}
|
|
108
|
+
return 'DEFAULT'
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Generic Types
|
|
113
|
+
```typescript
|
|
114
|
+
interface ApiResponse<T> {
|
|
115
|
+
data: T
|
|
116
|
+
status: number
|
|
117
|
+
message: string
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const fetchData = async <T>(url: string): Promise<ApiResponse<T>> => {
|
|
121
|
+
const response = await apiService.get<ApiResponse<T>>(url)
|
|
122
|
+
return response.data
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## 🎯 State Management Patterns
|
|
127
|
+
|
|
128
|
+
### Local State Pattern
|
|
129
|
+
```typescript
|
|
130
|
+
const Component: React.FC = () => {
|
|
131
|
+
const [count, setCount] = useState(0)
|
|
132
|
+
const [loading, setLoading] = useState(false)
|
|
133
|
+
|
|
134
|
+
const increment = () => setCount(prev => prev + 1)
|
|
135
|
+
|
|
136
|
+
return <div>{count}</div>
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Context Pattern
|
|
141
|
+
```typescript
|
|
142
|
+
interface AppContextType {
|
|
143
|
+
user: User | null
|
|
144
|
+
setUser: (user: User | null) => void
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const AppContext = createContext<AppContextType | undefined>(undefined)
|
|
148
|
+
|
|
149
|
+
export const AppProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
150
|
+
const [user, setUser] = useState<User | null>(null)
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<AppContext.Provider value={{ user, setUser }}>
|
|
154
|
+
{children}
|
|
155
|
+
</AppContext.Provider>
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export const useApp = () => {
|
|
160
|
+
const context = useContext(AppContext)
|
|
161
|
+
if (!context) {
|
|
162
|
+
throw new Error('useApp must be used within AppProvider')
|
|
163
|
+
}
|
|
164
|
+
return context
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## 🔄 Async Pattern
|
|
169
|
+
|
|
170
|
+
### Async/Await with Error Handling
|
|
171
|
+
```typescript
|
|
172
|
+
const Component: React.FC = () => {
|
|
173
|
+
const [data, setData] = useState<Data | null>(null)
|
|
174
|
+
const [error, setError] = useState<string | null>(null)
|
|
175
|
+
const [loading, setLoading] = useState(false)
|
|
176
|
+
|
|
177
|
+
const fetchData = async () => {
|
|
178
|
+
setLoading(true)
|
|
179
|
+
setError(null)
|
|
180
|
+
try {
|
|
181
|
+
const response = await apiService.get('/endpoint')
|
|
182
|
+
setData(response.data)
|
|
183
|
+
} catch (err) {
|
|
184
|
+
setError('Failed to fetch data')
|
|
185
|
+
} finally {
|
|
186
|
+
setLoading(false)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
useEffect(() => {
|
|
191
|
+
fetchData()
|
|
192
|
+
}, [])
|
|
193
|
+
|
|
194
|
+
if (loading) return <Loading />
|
|
195
|
+
if (error) return <Error message={error} />
|
|
196
|
+
|
|
197
|
+
return <div>{data?.name}</div>
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## 🎨 Styling Patterns
|
|
202
|
+
|
|
203
|
+
### CSS-in-JS (if using)
|
|
204
|
+
```typescript
|
|
205
|
+
const styledComponent = styled.div`
|
|
206
|
+
padding: 1rem;
|
|
207
|
+
background: ${props => props.theme.background};
|
|
208
|
+
`
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### CSS Modules Pattern
|
|
212
|
+
```typescript
|
|
213
|
+
import styles from './Component.module.css'
|
|
214
|
+
|
|
215
|
+
const Component: React.FC = () => {
|
|
216
|
+
return <div className={styles.container}>Content</div>
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Tailwind CSS Pattern
|
|
221
|
+
```typescript
|
|
222
|
+
const Component: React.FC = () => {
|
|
223
|
+
return (
|
|
224
|
+
<div className="p-4 bg-white rounded-lg shadow">
|
|
225
|
+
Content
|
|
226
|
+
</div>
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## 📦 File Organization Patterns
|
|
232
|
+
|
|
233
|
+
### Feature-Based Structure
|
|
234
|
+
```
|
|
235
|
+
src/
|
|
236
|
+
├── features/
|
|
237
|
+
│ ├── auth/
|
|
238
|
+
│ │ ├── components/
|
|
239
|
+
│ │ ├── services/
|
|
240
|
+
│ │ └── types/
|
|
241
|
+
│ └── dashboard/
|
|
242
|
+
│ ├── components/
|
|
243
|
+
│ ├── services/
|
|
244
|
+
│ └── types/
|
|
245
|
+
├── components/ (shared)
|
|
246
|
+
├── services/ (shared)
|
|
247
|
+
└── utils/ (shared)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Type-Based Structure
|
|
251
|
+
```
|
|
252
|
+
src/
|
|
253
|
+
├── components/
|
|
254
|
+
├── services/
|
|
255
|
+
├── hooks/
|
|
256
|
+
├── types/
|
|
257
|
+
├── utils/
|
|
258
|
+
└── pages/
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## 🔒 Error Handling Patterns
|
|
262
|
+
|
|
263
|
+
### Error Boundary Pattern
|
|
264
|
+
```typescript
|
|
265
|
+
class ErrorBoundary extends React.Component<{ children: React.ReactNode }, { hasError: boolean }> {
|
|
266
|
+
constructor(props: { children: React.ReactNode }) {
|
|
267
|
+
super(props)
|
|
268
|
+
this.state = { hasError: false }
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
static getDerivedStateFromError() {
|
|
272
|
+
return { hasError: true }
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
render() {
|
|
276
|
+
if (this.state.hasError) {
|
|
277
|
+
return <div>Something went wrong</div>
|
|
278
|
+
}
|
|
279
|
+
return this.props.children
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Try-Catch in Async Operations
|
|
285
|
+
```typescript
|
|
286
|
+
const safeAsyncOperation = async () => {
|
|
287
|
+
try {
|
|
288
|
+
const result = await apiService.get('/endpoint')
|
|
289
|
+
return result
|
|
290
|
+
} catch (error) {
|
|
291
|
+
console.error('Operation failed:', error)
|
|
292
|
+
throw error
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## 🚀 Performance Patterns
|
|
298
|
+
|
|
299
|
+
### Memo Pattern
|
|
300
|
+
```typescript
|
|
301
|
+
const ExpensiveComponent: React.FC<{ data: DataType }> = React.memo(({ data }) => {
|
|
302
|
+
return <div>{data.name}</div>
|
|
303
|
+
})
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### useCallback Pattern
|
|
307
|
+
```typescript
|
|
308
|
+
const Component: React.FC = () => {
|
|
309
|
+
const handleClick = useCallback(() => {
|
|
310
|
+
console.log('Clicked')
|
|
311
|
+
}, [])
|
|
312
|
+
|
|
313
|
+
return <button onClick={handleClick}>Click</button>
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### useMemo Pattern
|
|
318
|
+
```typescript
|
|
319
|
+
const Component: React.FC<{ items: Item[] }> = ({ items }) => {
|
|
320
|
+
const sortedItems = useMemo(() => {
|
|
321
|
+
return items.sort((a, b) => a.name.localeCompare(b.name))
|
|
322
|
+
}, [items])
|
|
323
|
+
|
|
324
|
+
return <div>{sortedItems.map(item => <div key={item.id}>{item.name}</div>)}</div>
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## 🧪 Testing Patterns
|
|
329
|
+
|
|
330
|
+
### Component Testing Pattern
|
|
331
|
+
```typescript
|
|
332
|
+
describe('Component', () => {
|
|
333
|
+
it('renders correctly', () => {
|
|
334
|
+
render(<Component title="Test" />)
|
|
335
|
+
expect(screen.getByText('Test')).toBeInTheDocument()
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
it('calls callback on click', () => {
|
|
339
|
+
const handleClick = jest.fn()
|
|
340
|
+
render(<Button onClick={handleClick} label="Click" />)
|
|
341
|
+
fireEvent.click(screen.getByText('Click'))
|
|
342
|
+
expect(handleClick).toHaveBeenCalled()
|
|
343
|
+
})
|
|
344
|
+
})
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Hook Testing Pattern
|
|
348
|
+
```typescript
|
|
349
|
+
import { renderHook, act } from '@testing-library/react-hooks'
|
|
350
|
+
|
|
351
|
+
describe('useCustomHook', () => {
|
|
352
|
+
it('returns initial state', () => {
|
|
353
|
+
const { result } = renderHook(() => useCustomHook())
|
|
354
|
+
expect(result.current).toBe('initial')
|
|
355
|
+
})
|
|
356
|
+
})
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
## 📐 Naming Conventions
|
|
360
|
+
|
|
361
|
+
### Components
|
|
362
|
+
- **File**: `PascalCase.tsx` (e.g., `UserProfile.tsx`)
|
|
363
|
+
- **Component**: `PascalCase` (e.g., `UserProfile`)
|
|
364
|
+
- **Props Interface**: `ComponentProps` (e.g., `UserProfileProps`)
|
|
365
|
+
|
|
366
|
+
### Hooks
|
|
367
|
+
- **File**: `camelCase.ts` (e.g., `useUserProfile.ts`)
|
|
368
|
+
- **Hook**: `camelCase` starting with `use` (e.g., `useUserProfile`)
|
|
369
|
+
|
|
370
|
+
### Services
|
|
371
|
+
- **File**: `camelCase.ts` (e.g., `apiService.ts`)
|
|
372
|
+
- **Service**: `camelCase` (e.g., `apiService`)
|
|
373
|
+
|
|
374
|
+
### Types
|
|
375
|
+
- **File**: `PascalCase.types.ts` (e.g., `User.types.ts`)
|
|
376
|
+
- **Interface**: `PascalCase` (e.g., `UserProfile`)
|
|
377
|
+
- **Type**: `PascalCase` (e.g., `ApiResponse`)
|
|
378
|
+
|
|
379
|
+
### Constants
|
|
380
|
+
- **File**: `camelCase.ts` (e.g., `constants.ts`)
|
|
381
|
+
- **Constant**: `UPPER_SNAKE_CASE` (e.g., `API_BASE_URL`)
|
|
382
|
+
|
|
383
|
+
## 🎯 Best Practices
|
|
384
|
+
|
|
385
|
+
### Code Organization
|
|
386
|
+
1. Keep components small and focused
|
|
387
|
+
2. Extract reusable logic into custom hooks
|
|
388
|
+
3. Use TypeScript interfaces for all props
|
|
389
|
+
4. Organize imports (external, internal, types)
|
|
390
|
+
5. Use path aliases (`@/`) for internal imports
|
|
391
|
+
|
|
392
|
+
### Performance
|
|
393
|
+
1. Use React.memo for expensive components
|
|
394
|
+
2. Implement code splitting with React.lazy
|
|
395
|
+
3. Optimize images and assets
|
|
396
|
+
4. Avoid unnecessary re-renders
|
|
397
|
+
5. Use virtual scrolling for long lists
|
|
398
|
+
|
|
399
|
+
### Accessibility
|
|
400
|
+
1. Use semantic HTML elements
|
|
401
|
+
2. Provide alt text for images
|
|
402
|
+
3. Ensure keyboard navigation
|
|
403
|
+
4. Use ARIA labels where needed
|
|
404
|
+
5. Test with screen readers
|
|
405
|
+
|
|
406
|
+
### Security
|
|
407
|
+
1. Sanitize user input
|
|
408
|
+
2. Use HTTPS in production
|
|
409
|
+
3. Validate data on client and server
|
|
410
|
+
4. Never expose sensitive data
|
|
411
|
+
5. Use environment variables for secrets
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Project Structure - {project_name}
|
|
2
|
+
|
|
3
|
+
This document describes the complete project structure and organization for AI assistants to understand how to work with this codebase.
|
|
4
|
+
|
|
5
|
+
## 📁 Root Directory Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
{project_name}/
|
|
9
|
+
├── .env.example # Environment variables example
|
|
10
|
+
├── .gitignore # Git ignore patterns
|
|
11
|
+
├── .knowledge/ # AI documentation (this directory)
|
|
12
|
+
├── index.html # HTML entry point
|
|
13
|
+
├── package.json # Node.js dependencies
|
|
14
|
+
├── tsconfig.json # TypeScript configuration
|
|
15
|
+
├── tsconfig.app.json # TypeScript app configuration
|
|
16
|
+
├── tsconfig.node.json # TypeScript node configuration
|
|
17
|
+
├── vite.config.ts # Vite build configuration
|
|
18
|
+
├── README.md # Project documentation
|
|
19
|
+
└── src/ # Source code
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 🏗️ Source Directory Structure (`src/`)
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
src/
|
|
26
|
+
├── main.tsx # Application entry point
|
|
27
|
+
├── App.tsx # Root component
|
|
28
|
+
├── index.css # Global styles
|
|
29
|
+
├── components/ # Reusable UI components
|
|
30
|
+
│ └── Header.tsx # Header component
|
|
31
|
+
├── services/ # API and external services
|
|
32
|
+
│ └── api.ts # API service with axios
|
|
33
|
+
└── utils/ # Utility functions
|
|
34
|
+
└── constants.ts # Constants and configuration
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 🎯 Key Conventions
|
|
38
|
+
|
|
39
|
+
### File Naming
|
|
40
|
+
- **Components**: `PascalCase` with `.tsx` extension (e.g., `UserProfile.tsx`)
|
|
41
|
+
- **Utilities**: `camelCase` with `.ts` extension (e.g., `formatDate.ts`)
|
|
42
|
+
- **Services**: `camelCase` with `.ts` extension (e.g., `api.ts`)
|
|
43
|
+
- **Types**: `PascalCase` with `.ts` extension (e.g., `UserTypes.ts`)
|
|
44
|
+
|
|
45
|
+
### Import Patterns
|
|
46
|
+
- **Components**: `import Component from '@/components/Component'`
|
|
47
|
+
- **Services**: `import { apiService } from '@/services/api'`
|
|
48
|
+
- **Utils**: `import { CONSTANT } from '@/utils/constants'`
|
|
49
|
+
- **Types**: `import { UserType } from '@/types'`
|
|
50
|
+
|
|
51
|
+
### TypeScript Patterns
|
|
52
|
+
- **Props Interface**: Always define interfaces for component props
|
|
53
|
+
- **Generic Types**: Use generics when appropriate for reusable components
|
|
54
|
+
- **Strict Mode**: TypeScript strict mode is enabled
|
|
55
|
+
- **No Any**: Avoid using `any` type, use `unknown` or proper types instead
|
|
56
|
+
|
|
57
|
+
## 🔄 Development Workflow
|
|
58
|
+
|
|
59
|
+
1. **Install dependencies**: `npm install`
|
|
60
|
+
2. **Run development server**: `npm run dev`
|
|
61
|
+
3. **Build for production**: `npm run build`
|
|
62
|
+
4. **Preview production build**: `npm run preview`
|
|
63
|
+
5. **Run linter**: `npm run lint`
|
|
64
|
+
|
|
65
|
+
## 📋 Important Notes for AI
|
|
66
|
+
|
|
67
|
+
- **Always use TypeScript** with proper type definitions
|
|
68
|
+
- **Follow the component structure** when creating new components
|
|
69
|
+
- **Use the apiService** for all API calls
|
|
70
|
+
- **Handle loading and error states** in async operations
|
|
71
|
+
- **Use environment variables** via `import.meta.env.VITE_*`
|
|
72
|
+
- **Path aliases**: Use `@` for `src/` directory imports
|
|
73
|
+
- **Vite**: This project uses Vite as the build tool (not Create React App)
|
|
74
|
+
- **Port**: Development server runs on http://localhost:5173
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
import json
|
|
5
|
+
import shutil
|
|
6
|
+
from ntc.utils.output import print_success, print_error, print_warning, print_info, print_progress, print_package, print_download
|
|
7
|
+
|
|
8
|
+
def replace_variables(content, variables):
|
|
9
|
+
"""Replace placeholders with actual variables."""
|
|
10
|
+
for key, value in variables.items():
|
|
11
|
+
content = content.replace(f"{{{key}}}", value)
|
|
12
|
+
return content
|
|
13
|
+
|
|
14
|
+
def get_template_content(template_name, variables):
|
|
15
|
+
"""Load template content and replace variables."""
|
|
16
|
+
# Remove 'react_structure/' prefix if present since we're already in that directory
|
|
17
|
+
if template_name.startswith("react_structure/"):
|
|
18
|
+
template_name = template_name[len("react_structure/"):]
|
|
19
|
+
|
|
20
|
+
template_path = os.path.join(os.path.dirname(__file__), template_name)
|
|
21
|
+
if not os.path.exists(template_path):
|
|
22
|
+
return ""
|
|
23
|
+
with open(template_path, "r", encoding="utf-8") as f:
|
|
24
|
+
content = f.read()
|
|
25
|
+
return replace_variables(content, variables)
|
|
26
|
+
|
|
27
|
+
def create_from_json(base_path, structure, variables):
|
|
28
|
+
"""Recursively create directories and files from JSON."""
|
|
29
|
+
for name, content in structure.items():
|
|
30
|
+
name = replace_variables(name, variables)
|
|
31
|
+
full_path = os.path.join(base_path, name)
|
|
32
|
+
|
|
33
|
+
if isinstance(content, dict):
|
|
34
|
+
if "template" in content:
|
|
35
|
+
os.makedirs(os.path.dirname(full_path), exist_ok=True)
|
|
36
|
+
with open(full_path, "w", encoding="utf-8") as f:
|
|
37
|
+
f.write(get_template_content(content["template"], variables))
|
|
38
|
+
else:
|
|
39
|
+
os.makedirs(full_path, exist_ok=True)
|
|
40
|
+
create_from_json(full_path, content, variables)
|
|
41
|
+
else:
|
|
42
|
+
os.makedirs(os.path.dirname(full_path), exist_ok=True)
|
|
43
|
+
with open(full_path, "w", encoding="utf-8") as f:
|
|
44
|
+
f.write(replace_variables(content or "", variables))
|
|
45
|
+
|
|
46
|
+
def install_npm_dependencies(project_root):
|
|
47
|
+
"""Install npm dependencies for React project."""
|
|
48
|
+
print_package("Installing npm dependencies...")
|
|
49
|
+
|
|
50
|
+
# Check if npm is available
|
|
51
|
+
if not shutil.which("npm"):
|
|
52
|
+
print_error("npm not found. Please install Node.js and npm first.")
|
|
53
|
+
print_info("Visit https://nodejs.org/ to download Node.js")
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
# Change to project directory and install dependencies
|
|
58
|
+
result = subprocess.run(
|
|
59
|
+
["npm", "install"],
|
|
60
|
+
cwd=project_root,
|
|
61
|
+
check=True,
|
|
62
|
+
capture_output=True,
|
|
63
|
+
text=True
|
|
64
|
+
)
|
|
65
|
+
print_success("npm dependencies installed successfully!")
|
|
66
|
+
return True
|
|
67
|
+
except subprocess.CalledProcessError as e:
|
|
68
|
+
print_error(f"Failed to install npm dependencies: {e}")
|
|
69
|
+
print_info("You can manually run 'npm install' in the project directory later.")
|
|
70
|
+
return False
|
|
71
|
+
|
|
72
|
+
def init_react_project(base_path, project_name):
|
|
73
|
+
"""Initialize React project structure and setup environment."""
|
|
74
|
+
structure_file = os.path.join(os.path.dirname(__file__), "structure.json")
|
|
75
|
+
|
|
76
|
+
if not os.path.exists(structure_file):
|
|
77
|
+
print_error(f"Structure file not found: {structure_file}")
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
with open(structure_file, "r", encoding="utf-8") as f:
|
|
81
|
+
structure = json.load(f)
|
|
82
|
+
|
|
83
|
+
project_root = os.path.join(base_path, project_name)
|
|
84
|
+
os.makedirs(project_root, exist_ok=True)
|
|
85
|
+
|
|
86
|
+
variables = {
|
|
87
|
+
"project_name": project_name,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
create_from_json(project_root, structure["Project"], variables)
|
|
91
|
+
print_success(f"React project '{project_name}' initialized at: {project_root}")
|
|
92
|
+
|
|
93
|
+
# Ask user for dependency installation
|
|
94
|
+
print(f"\n{Fore.CYAN}{Style.BRIGHT}📦 NODE.JS DEPENDENCY SETUP{Style.RESET_ALL}")
|
|
95
|
+
print("─" * 40)
|
|
96
|
+
print(f"{Fore.YELLOW}{Style.BRIGHT}[1]{Style.RESET_ALL} {Fore.WHITE}Install dependencies now (npm install){Style.RESET_ALL}")
|
|
97
|
+
print(f"{Fore.YELLOW}{Style.BRIGHT}[2]{Style.RESET_ALL} {Fore.WHITE}Use existing node_modules (if available){Style.RESET_ALL}")
|
|
98
|
+
print(f"{Fore.YELLOW}{Style.BRIGHT}[3]{Style.RESET_ALL} {Fore.WHITE}Skip dependency installation{Style.RESET_ALL}")
|
|
99
|
+
|
|
100
|
+
while True:
|
|
101
|
+
deps_choice = input(f"{Fore.WHITE}{Style.BRIGHT}❓ Select dependency option (1-3): {Style.RESET_ALL}").strip()
|
|
102
|
+
|
|
103
|
+
if deps_choice == "1":
|
|
104
|
+
# Install dependencies now
|
|
105
|
+
install_npm_dependencies(project_root)
|
|
106
|
+
break
|
|
107
|
+
|
|
108
|
+
elif deps_choice == "2":
|
|
109
|
+
# Check for existing node_modules
|
|
110
|
+
node_modules_path = os.path.join(project_root, "node_modules")
|
|
111
|
+
if os.path.exists(node_modules_path):
|
|
112
|
+
print_success("Found existing node_modules directory!")
|
|
113
|
+
print_info("Using existing dependencies. Run 'npm install' if you need to update them.")
|
|
114
|
+
else:
|
|
115
|
+
print_warning("No existing node_modules found in this location.")
|
|
116
|
+
print_info("You may need to run 'npm install' manually later.")
|
|
117
|
+
break
|
|
118
|
+
|
|
119
|
+
elif deps_choice == "3":
|
|
120
|
+
# Skip installation
|
|
121
|
+
print_info("Skipping dependency installation.")
|
|
122
|
+
print_info("Remember to run 'npm install' before starting development.")
|
|
123
|
+
break
|
|
124
|
+
|
|
125
|
+
else:
|
|
126
|
+
print_error("Invalid choice. Please select 1, 2, or 3.")
|
|
127
|
+
|
|
128
|
+
# Show next steps
|
|
129
|
+
print(f"\n{Fore.GREEN}{Style.BRIGHT}🎉 PROJECT READY!{Style.RESET_ALL}")
|
|
130
|
+
print("─" * 30)
|
|
131
|
+
print_info("Next steps:")
|
|
132
|
+
print(f" 1. cd {project_name}")
|
|
133
|
+
if deps_choice != "1":
|
|
134
|
+
print(" 2. npm install")
|
|
135
|
+
print(" 3. npm run dev")
|
|
136
|
+
else:
|
|
137
|
+
print(" 2. npm run dev")
|
|
138
|
+
print_info("Your React app will be available at http://localhost:5173")
|
|
139
|
+
print_info("Make sure your backend API is running on http://localhost:8000")
|
|
140
|
+
|
|
141
|
+
def init_react_project_in_current_dir(project_name):
|
|
142
|
+
"""Initialize React project structure in the current directory."""
|
|
143
|
+
structure_file = os.path.join(os.path.dirname(__file__), "structure.json")
|
|
144
|
+
|
|
145
|
+
if not os.path.exists(structure_file):
|
|
146
|
+
print_error(f"Structure file not found: {structure_file}")
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
with open(structure_file, "r", encoding="utf-8") as f:
|
|
150
|
+
structure = json.load(f)
|
|
151
|
+
|
|
152
|
+
project_root = os.getcwd() # Use current directory
|
|
153
|
+
|
|
154
|
+
variables = {
|
|
155
|
+
"project_name": project_name,
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
# Check if current directory is empty or ask for confirmation
|
|
159
|
+
existing_files = [f for f in os.listdir(project_root) if not f.startswith('.')]
|
|
160
|
+
if existing_files:
|
|
161
|
+
print_warning(f"Current directory is not empty. Found {len(existing_files)} files/folders.")
|
|
162
|
+
print_info("Existing items: " + ", ".join(existing_files[:5]) + ("..." if len(existing_files) > 5 else ""))
|
|
163
|
+
|
|
164
|
+
confirm = input(f"{Fore.WHITE}{Style.BRIGHT}❓ Continue anyway? (y/N): {Style.RESET_ALL}").strip().lower()
|
|
165
|
+
if confirm != 'y':
|
|
166
|
+
print_info("Project initialization cancelled.")
|
|
167
|
+
return
|
|
168
|
+
|
|
169
|
+
create_from_json(project_root, structure["Project"], variables)
|
|
170
|
+
print_success(f"React project '{project_name}' initialized in current directory: {project_root}")
|
|
171
|
+
|
|
172
|
+
# Ask user for dependency installation
|
|
173
|
+
print(f"\n{Fore.CYAN}{Style.BRIGHT}📦 NODE.JS DEPENDENCY SETUP{Style.RESET_ALL}")
|
|
174
|
+
print("─" * 40)
|
|
175
|
+
print(f"{Fore.YELLOW}{Style.BRIGHT}[1]{Style.RESET_ALL} {Fore.WHITE}Install dependencies now (npm install){Style.RESET_ALL}")
|
|
176
|
+
print(f"{Fore.YELLOW}{Style.BRIGHT}[2]{Style.RESET_ALL} {Fore.WHITE}Use existing node_modules (if available){Style.RESET_ALL}")
|
|
177
|
+
print(f"{Fore.YELLOW}{Style.BRIGHT}[3]{Style.RESET_ALL} {Fore.WHITE}Skip dependency installation{Style.RESET_ALL}")
|
|
178
|
+
|
|
179
|
+
while True:
|
|
180
|
+
deps_choice = input(f"{Fore.WHITE}{Style.BRIGHT}❓ Select dependency option (1-3): {Style.RESET_ALL}").strip()
|
|
181
|
+
|
|
182
|
+
if deps_choice == "1":
|
|
183
|
+
# Install dependencies now
|
|
184
|
+
install_npm_dependencies(project_root)
|
|
185
|
+
break
|
|
186
|
+
|
|
187
|
+
elif deps_choice == "2":
|
|
188
|
+
# Check for existing node_modules
|
|
189
|
+
node_modules_path = os.path.join(project_root, "node_modules")
|
|
190
|
+
if os.path.exists(node_modules_path):
|
|
191
|
+
print_success("Found existing node_modules directory!")
|
|
192
|
+
print_info("Using existing dependencies. Run 'npm install' if you need to update them.")
|
|
193
|
+
else:
|
|
194
|
+
print_warning("No existing node_modules found in this location.")
|
|
195
|
+
print_info("You may need to run 'npm install' manually later.")
|
|
196
|
+
break
|
|
197
|
+
|
|
198
|
+
elif deps_choice == "3":
|
|
199
|
+
# Skip installation
|
|
200
|
+
print_info("Skipping dependency installation.")
|
|
201
|
+
print_info("Remember to run 'npm install' before starting development.")
|
|
202
|
+
break
|
|
203
|
+
|
|
204
|
+
else:
|
|
205
|
+
print_error("Invalid choice. Please select 1, 2, or 3.")
|
|
206
|
+
|
|
207
|
+
# Show next steps
|
|
208
|
+
print(f"\n{Fore.GREEN}{Style.BRIGHT}🎉 PROJECT READY!{Style.RESET_ALL}")
|
|
209
|
+
print("─" * 30)
|
|
210
|
+
print_info("Next steps:")
|
|
211
|
+
if deps_choice != "1":
|
|
212
|
+
print(" 1. npm install")
|
|
213
|
+
print(" 2. npm run dev")
|
|
214
|
+
else:
|
|
215
|
+
print(" 1. npm run dev")
|
|
216
|
+
print_info("Your React app will be available at http://localhost:5173")
|
|
217
|
+
print_info("Make sure your backend API is running on http://localhost:8000")
|