proje-react-panel 1.5.0 → 1.6.0-test-1

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.
Files changed (43) hide show
  1. package/.cursor/rules.md +11 -1
  2. package/AUTH_LAYOUT_EXAMPLE.md +343 -0
  3. package/AUTH_LAYOUT_GUIDE.md +819 -0
  4. package/IMPLEMENTATION_GUIDE.md +899 -0
  5. package/dist/api/ApiConfig.d.ts +11 -0
  6. package/dist/api/AuthApi.d.ts +2 -5
  7. package/dist/api/CrudApi.d.ts +11 -12
  8. package/dist/components/list/cells/BooleanCell.d.ts +5 -2
  9. package/dist/components/list/cells/DateCell.d.ts +5 -2
  10. package/dist/components/list/cells/DefaultCell.d.ts +3 -2
  11. package/dist/components/list/cells/DownloadCell.d.ts +3 -2
  12. package/dist/components/list/cells/ImageCell.d.ts +3 -2
  13. package/dist/components/list/cells/UUIDCell.d.ts +5 -2
  14. package/dist/decorators/auth/DefaultLoginForm.d.ts +4 -0
  15. package/dist/decorators/list/Cell.d.ts +4 -0
  16. package/dist/decorators/list/List.d.ts +7 -2
  17. package/dist/index.cjs.js +15 -1
  18. package/dist/index.d.ts +5 -0
  19. package/dist/index.esm.js +15 -1
  20. package/dist/types/Login.d.ts +8 -0
  21. package/package.json +3 -1
  22. package/src/api/ApiConfig.ts +63 -0
  23. package/src/api/AuthApi.ts +8 -0
  24. package/src/api/CrudApi.ts +96 -60
  25. package/src/components/list/CellField.tsx +19 -10
  26. package/src/components/list/Datagrid.tsx +26 -12
  27. package/src/components/list/cells/BooleanCell.tsx +7 -2
  28. package/src/components/list/cells/DateCell.tsx +6 -2
  29. package/src/components/list/cells/DefaultCell.tsx +4 -4
  30. package/src/components/list/cells/DownloadCell.tsx +4 -2
  31. package/src/components/list/cells/ImageCell.tsx +4 -2
  32. package/src/components/list/cells/LinkCell.tsx +3 -2
  33. package/src/components/list/cells/UUIDCell.tsx +6 -2
  34. package/src/decorators/auth/DefaultLoginForm.ts +32 -0
  35. package/src/decorators/list/Cell.ts +4 -0
  36. package/src/decorators/list/List.ts +3 -2
  37. package/src/index.ts +25 -0
  38. package/src/store/store.ts +1 -1
  39. package/src/styles/components/button.scss +14 -0
  40. package/src/styles/index.scss +1 -1
  41. package/src/styles/list.scss +8 -1
  42. package/src/types/Login.ts +9 -0
  43. package/src/utils/logout.ts +2 -0
@@ -0,0 +1,899 @@
1
+ # Proje React Panel - Implementation Guide
2
+
3
+ A comprehensive guide for implementing the Proje React Panel library in your React applications.
4
+
5
+ ## Table of Contents
6
+
7
+ 1. [Installation & Setup](#installation--setup)
8
+ 2. [Basic Configuration](#basic-configuration)
9
+ 3. [Authentication Setup](#authentication-setup)
10
+ 4. [API Configuration](#api-configuration)
11
+ 5. [Creating Models with Decorators](#creating-models-with-decorators)
12
+ 6. [Form Implementation](#form-implementation)
13
+ 7. [List Implementation](#list-implementation)
14
+ 8. [Details Page Implementation](#details-page-implementation)
15
+ 9. [Routing & Layout](#routing--layout)
16
+ 10. [Advanced Features](#advanced-features)
17
+ 11. [Complete Example](#complete-example)
18
+
19
+ ## Installation & Setup
20
+
21
+ ### Prerequisites
22
+
23
+ - React 19.0.0 or higher
24
+ - TypeScript
25
+ - React Router 7.3.0
26
+ - React Hook Form 7.54.2 or higher
27
+ - Zustand 5.0.3 or higher
28
+
29
+ ### Install Dependencies
30
+
31
+ ```bash
32
+ npm install proje-react-panel react react-dom react-router react-hook-form zustand class-validator class-transformer
33
+ ```
34
+
35
+ ### Required Peer Dependencies
36
+
37
+ ```json
38
+ {
39
+ "react": ">=19.0.0",
40
+ "react-hook-form": ">=7.54.2",
41
+ "react-router": "7.3.0",
42
+ "react-select": "^5.10.1",
43
+ "use-sync-external-store": ">=1.4.0",
44
+ "zustand": ">=5.0.3"
45
+ }
46
+ ```
47
+
48
+ ## Basic Configuration
49
+
50
+ ### 1. Initialize Your App
51
+
52
+ Create your main App component and wrap it with the `Panel` component:
53
+
54
+ ```tsx
55
+ // App.tsx
56
+ import React from 'react';
57
+ import { Panel, Login, ListPage, FormPage, DetailsPage } from 'proje-react-panel';
58
+ import { BrowserRouter as Router, Route, Routes } from 'react-router';
59
+ import { initApi, initAuthToken, setAuthToken } from './api/apiConfig';
60
+
61
+ // Initialize API
62
+ initApi({
63
+ baseUrl: import.meta.env.VITE_API_BASE_URL || 'http://localhost:8080',
64
+ });
65
+ initAuthToken();
66
+
67
+ export function App() {
68
+ return (
69
+ <Panel
70
+ onInit={appData => {
71
+ if (appData.token) {
72
+ setAuthToken(appData.token);
73
+ }
74
+ }}
75
+ >
76
+ <Router>
77
+ <Routes>{/* Your routes here */}</Routes>
78
+ </Router>
79
+ </Panel>
80
+ );
81
+ }
82
+ ```
83
+
84
+ ### 2. Entry Point
85
+
86
+ ```tsx
87
+ // index.tsx
88
+ import React from 'react';
89
+ import ReactDOM from 'react-dom/client';
90
+ import { App } from './App';
91
+ import './index.scss';
92
+
93
+ const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
94
+ root.render(<App />);
95
+ ```
96
+
97
+ ## Authentication Setup
98
+
99
+ ### 1. API Configuration
100
+
101
+ Create an API configuration file to handle authentication and HTTP requests:
102
+
103
+ ```typescript
104
+ // api/apiConfig.ts
105
+ import axios, { AxiosInstance } from 'axios';
106
+
107
+ let axiosInstance: AxiosInstance;
108
+
109
+ export function initApi(config: { baseUrl: string }) {
110
+ axiosInstance = axios.create({
111
+ baseURL: config.baseUrl,
112
+ headers: {
113
+ 'Content-Type': 'application/json',
114
+ },
115
+ });
116
+
117
+ // Handle 401 errors globally
118
+ axiosInstance.interceptors.response.use(
119
+ response => response,
120
+ error => {
121
+ if (error.response && error.response.status === 401) {
122
+ setAuthLogout();
123
+ window.location.href = '/login';
124
+ }
125
+ return Promise.reject(error);
126
+ }
127
+ );
128
+ }
129
+
130
+ export function initAuthToken(): void {
131
+ if (!axiosInstance) {
132
+ throw new Error('API not initialized. Call initApi first.');
133
+ }
134
+ const token = localStorage.getItem('token');
135
+ if (token) {
136
+ axiosInstance.defaults.headers.common['Authorization'] = `Bearer ${token}`;
137
+ }
138
+ }
139
+
140
+ export function setAuthToken(token: string): void {
141
+ if (!axiosInstance) {
142
+ throw new Error('API not initialized. Call initApi first.');
143
+ }
144
+ axiosInstance.defaults.headers.common['Authorization'] = `Bearer ${token}`;
145
+ localStorage.setItem('token', token);
146
+ }
147
+
148
+ export function setAuthLogout(): void {
149
+ if (!axiosInstance) {
150
+ throw new Error('API not initialized. Call initApi first.');
151
+ }
152
+ axiosInstance.defaults.headers.common['Authorization'] = null;
153
+ localStorage.removeItem('token');
154
+ }
155
+
156
+ export function getAxiosInstance(): AxiosInstance {
157
+ if (!axiosInstance) {
158
+ throw new Error('API not initialized. Call initApi first.');
159
+ }
160
+ return axiosInstance;
161
+ }
162
+ ```
163
+
164
+ ### 2. Login Form Implementation
165
+
166
+ ```typescript
167
+ // types/Login.ts
168
+ import { MinLength } from 'class-validator';
169
+ import { Form, Input, login } from 'proje-react-panel';
170
+ import { dataFetchers } from '../api/dataFetchers';
171
+ import { setAuthToken } from '../api/apiConfig';
172
+
173
+ export interface LoginResponse {
174
+ access_token: string;
175
+ admin: AdminDetails; // Your user type
176
+ }
177
+
178
+ @Form<LoginForm, LoginResponse>({
179
+ onSubmit: dataFetchers.auth.login,
180
+ onSubmitSuccess: (data: LoginResponse) => {
181
+ setAuthToken(data.access_token);
182
+ login(data.admin, data.access_token, () => {
183
+ window.location.href = '/';
184
+ });
185
+ },
186
+ type: 'formData',
187
+ })
188
+ export class LoginForm {
189
+ @MinLength(3)
190
+ @Input({
191
+ label: 'Username',
192
+ })
193
+ username: string;
194
+
195
+ @Input({
196
+ label: 'Password',
197
+ inputType: 'password',
198
+ })
199
+ password: string;
200
+ }
201
+ ```
202
+
203
+ ## API Configuration
204
+
205
+ ### 1. CRUD Operations
206
+
207
+ Create a CRUD utility for API operations:
208
+
209
+ ```typescript
210
+ // api/crud.ts
211
+ import { getAxiosInstance } from './apiConfig';
212
+
213
+ export function create<T>(endpoint: string) {
214
+ return async (data: T) => {
215
+ const response = await getAxiosInstance().post(`/${endpoint}`, data);
216
+ return response.data;
217
+ };
218
+ }
219
+
220
+ export function getAll<T>(endpoint: string) {
221
+ return async (params: any = {}) => {
222
+ const response = await getAxiosInstance().get(`/${endpoint}`, { params });
223
+ return response.data;
224
+ };
225
+ }
226
+
227
+ export function getOne<T>(endpoint: string) {
228
+ return async (id: string) => {
229
+ const response = await getAxiosInstance().get(`/${endpoint}/${id}`);
230
+ return response.data;
231
+ };
232
+ }
233
+
234
+ export function update<T>(endpoint: string) {
235
+ return async (data: T) => {
236
+ const response = await getAxiosInstance().put(`/${endpoint}`, data);
237
+ return response.data;
238
+ };
239
+ }
240
+
241
+ export function remove(endpoint: string, idField: string) {
242
+ return async (item: any) => {
243
+ const response = await getAxiosInstance().delete(`/${endpoint}/${item[idField]}`);
244
+ return response.data;
245
+ };
246
+ }
247
+ ```
248
+
249
+ ### 2. Data Fetchers
250
+
251
+ Create a centralized data fetchers object:
252
+
253
+ ```typescript
254
+ // api/dataFetchers.ts
255
+ import { create, getAll, getOne, update, remove } from './crud';
256
+ import { AdminList, CreateAdminForm, EditAdminForm, AdminDetails } from '../types/Admin';
257
+
258
+ export const dataFetchers = Object.freeze({
259
+ admins: {
260
+ getAll: getAll<AdminList>('admins'),
261
+ details: getOne<AdminDetails>('admins'),
262
+ create: create<CreateAdminForm>('admins'),
263
+ update: update<EditAdminForm>('admins'),
264
+ updateDetails: getOne<EditAdminForm>('admins'),
265
+ remove: remove('admins', 'id'),
266
+ },
267
+ auth: {
268
+ login: async (data: any) => {
269
+ const response = await getAxiosInstance().post('/auth/login', data);
270
+ return response.data;
271
+ },
272
+ },
273
+ });
274
+ ```
275
+
276
+ ## Creating Models with Decorators
277
+
278
+ The library uses decorators to define models for forms, lists, and details pages.
279
+
280
+ ### 1. List Model
281
+
282
+ ```typescript
283
+ // types/Admin.ts
284
+ import {
285
+ Cell,
286
+ List,
287
+ Input,
288
+ DetailsItem,
289
+ Details,
290
+ Form,
291
+ SelectInput,
292
+ LinkCell,
293
+ } from 'proje-react-panel';
294
+ import { dataFetchers } from '../api/dataFetchers';
295
+
296
+ @List({
297
+ headers: {
298
+ create: { path: 'create', label: 'Create' },
299
+ },
300
+ actions: (item: AdminList) => ({
301
+ customActions: [
302
+ {
303
+ label: 'Custom Action',
304
+ onClick: () => {
305
+ alert('Custom action clicked');
306
+ },
307
+ },
308
+ ],
309
+ details: { path: '' + item.id, label: 'Details' },
310
+ edit: { path: 'edit/' + item.id, label: 'Edit' },
311
+ delete: { label: 'Delete', onRemoveItem: dataFetchers.admins.remove },
312
+ }),
313
+ getData: dataFetchers.admins.getAll,
314
+ primaryId: 'id',
315
+ })
316
+ export class AdminList {
317
+ @Cell({
318
+ title: 'ID',
319
+ type: 'uuid',
320
+ })
321
+ id: string;
322
+
323
+ @Cell({
324
+ title: 'Username',
325
+ })
326
+ username: string;
327
+
328
+ @Cell({
329
+ title: 'Email',
330
+ })
331
+ email: string;
332
+
333
+ @Cell({
334
+ title: 'Created At',
335
+ type: 'date',
336
+ })
337
+ createdAt: string;
338
+
339
+ @LinkCell({
340
+ path: '/',
341
+ placeHolder: 'Custom Link',
342
+ })
343
+ details: string;
344
+
345
+ @Cell({
346
+ title: 'Updated At',
347
+ type: 'date',
348
+ })
349
+ updatedAt: string;
350
+ }
351
+ ```
352
+
353
+ ### 2. Form Model
354
+
355
+ ```typescript
356
+ // Form base class
357
+ class AdminForm {
358
+ @MinLength(3)
359
+ @Input({
360
+ label: 'Username',
361
+ })
362
+ username: string;
363
+
364
+ @IsEmail()
365
+ @Input({
366
+ label: 'Email',
367
+ inputType: 'email',
368
+ })
369
+ email: string;
370
+
371
+ @ValidateIf(o => !o.__formEdit)
372
+ @IsString()
373
+ @MinLength(6)
374
+ @Input({
375
+ label: 'Password',
376
+ inputType: 'password',
377
+ })
378
+ password: string;
379
+
380
+ @IsEnum(['super-admin', 'admin'])
381
+ @SelectInput({
382
+ label: 'Role',
383
+ defaultOptions: [
384
+ { value: 'super-admin', label: 'Super Admin' },
385
+ { value: 'admin', label: 'Admin' },
386
+ ],
387
+ })
388
+ role: string;
389
+ }
390
+
391
+ // Create form
392
+ @Form({
393
+ onSubmit: dataFetchers.admins.create,
394
+ type: 'formData',
395
+ redirectSuccessUrl: '/admins',
396
+ })
397
+ export class CreateAdminForm extends AdminForm {}
398
+
399
+ // Edit form
400
+ @Form({
401
+ onSubmit: dataFetchers.admins.update,
402
+ getDetailsData: dataFetchers.admins.updateDetails,
403
+ redirectSuccessUrl: '/admins',
404
+ })
405
+ export class EditAdminForm extends AdminForm {}
406
+ ```
407
+
408
+ ### 3. Details Model
409
+
410
+ ```typescript
411
+ @Details({
412
+ getDetailsData: dataFetchers.admins.details,
413
+ primaryId: 'username',
414
+ })
415
+ export class AdminDetails {
416
+ @DetailsItem()
417
+ id: string;
418
+
419
+ @DetailsItem()
420
+ username: string;
421
+
422
+ @DetailsItem()
423
+ email: string;
424
+
425
+ @DetailsItem()
426
+ role: string;
427
+
428
+ @DetailsItem()
429
+ createdAt: string;
430
+
431
+ @DetailsItem()
432
+ updatedAt: string;
433
+ }
434
+ ```
435
+
436
+ ## Form Implementation
437
+
438
+ ### 1. Basic Form
439
+
440
+ ```tsx
441
+ // In your routing
442
+ <Route path="create" element={<FormPage key="admin-create" model={CreateAdminForm} />} />
443
+ <Route path="edit/:id" element={<FormPage key="admin-edit" model={EditAdminForm} />} />
444
+ ```
445
+
446
+ ### 2. Form with Custom Validation
447
+
448
+ ```typescript
449
+ import { IsEmail, IsString, MinLength, ValidateIf } from 'class-validator';
450
+
451
+ class UserForm {
452
+ @IsString()
453
+ @MinLength(3)
454
+ @Input({ label: 'Username' })
455
+ username: string;
456
+
457
+ @IsEmail()
458
+ @Input({ label: 'Email', inputType: 'email' })
459
+ email: string;
460
+
461
+ @ValidateIf(o => !o.__formEdit)
462
+ @IsString()
463
+ @MinLength(6)
464
+ @Input({ label: 'Password', inputType: 'password' })
465
+ password: string;
466
+
467
+ @IsBoolean()
468
+ @Input({ label: 'Is Active', type: 'checkbox' })
469
+ isActive: boolean;
470
+ }
471
+ ```
472
+
473
+ ### 3. Form with Select Input
474
+
475
+ ```typescript
476
+ @SelectInput({
477
+ label: "Role",
478
+ defaultOptions: [
479
+ { value: "admin", label: "Admin" },
480
+ { value: "user", label: "User" },
481
+ ],
482
+ })
483
+ role: string;
484
+
485
+ // Dynamic options
486
+ @SelectInput({
487
+ label: "Asset",
488
+ defaultOptions: [],
489
+ onSelectPreloader: async () => {
490
+ const response = await dataFetchers.assets.getAll({});
491
+ return response.data.map((asset) => ({
492
+ value: asset.id,
493
+ label: asset.filename,
494
+ }));
495
+ },
496
+ })
497
+ assetId: number;
498
+ ```
499
+
500
+ ## List Implementation
501
+
502
+ ### 1. Basic List
503
+
504
+ ```tsx
505
+ // In your routing
506
+ <Route path="" element={<ListPage key="admin-list" model={AdminList} />} />
507
+ ```
508
+
509
+ ### 2. List with Custom Header
510
+
511
+ ```tsx
512
+ <Route
513
+ path=""
514
+ element={<ListPage key="admin-list" customHeader={<AdminListHeader />} model={AdminList} />}
515
+ />
516
+ ```
517
+
518
+ ### 3. List with Custom Actions
519
+
520
+ ```typescript
521
+ @List({
522
+ actions: (item: AdminList) => ({
523
+ customActions: [
524
+ {
525
+ label: "Custom Action",
526
+ onClick: () => {
527
+ // Custom action logic
528
+ },
529
+ },
530
+ ],
531
+ details: { path: "" + item.id, label: "Details" },
532
+ edit: { path: "edit/" + item.id, label: "Edit" },
533
+ delete: { label: "Delete", onRemoveItem: dataFetchers.admins.remove },
534
+ }),
535
+ // ... other options
536
+ })
537
+ ```
538
+
539
+ ### 4. Cell Types
540
+
541
+ ```typescript
542
+ // Different cell types
543
+ @Cell({ title: "ID", type: "uuid" })
544
+ id: string;
545
+
546
+ @Cell({ title: "Created At", type: "date" })
547
+ createdAt: string;
548
+
549
+ @Cell({ title: "Is Active", type: "boolean" })
550
+ isActive: boolean;
551
+
552
+ // Link cells
553
+ @LinkCell({
554
+ path: "/details",
555
+ placeHolder: "View Details",
556
+ })
557
+ details: string;
558
+
559
+ @LinkCell({
560
+ path: "/",
561
+ placeHolder: "Click Me",
562
+ onClick: (data: AdminList) => {
563
+ alert(data.username);
564
+ },
565
+ })
566
+ customLink: string;
567
+ ```
568
+
569
+ ## Details Page Implementation
570
+
571
+ ### 1. Basic Details Page
572
+
573
+ ```tsx
574
+ <Route path=":id" element={<DetailsPage key="admin-details" model={AdminDetails} />} />
575
+ ```
576
+
577
+ ### 2. Details Page with Custom Header
578
+
579
+ ```tsx
580
+ <Route
581
+ path=":id"
582
+ element={
583
+ <DetailsPage CustomHeader={AdminDetailsHeader} key="admin-details" model={AdminDetails} />
584
+ }
585
+ />
586
+ ```
587
+
588
+ ## Routing & Layout
589
+
590
+ ### 1. Layout Component
591
+
592
+ ```tsx
593
+ // AuthLayout.tsx
594
+ import { Outlet } from 'react-router';
595
+ import React from 'react';
596
+ import { Layout, logout } from 'proje-react-panel';
597
+ import { setAuthLogout } from './api/apiConfig';
598
+
599
+ export function AuthLayout() {
600
+ return (
601
+ <Layout
602
+ logout={() => {
603
+ setAuthLogout();
604
+ logout(() => {
605
+ window.location.href = '/login';
606
+ });
607
+ }}
608
+ getIcons={getIcons}
609
+ menu={getMenu}
610
+ >
611
+ <Outlet />
612
+ </Layout>
613
+ );
614
+ }
615
+
616
+ function getMenu() {
617
+ return [
618
+ { name: 'Dashboard', path: '/', iconType: 'dashboard' },
619
+ { name: 'Admins', path: '/admins', iconType: 'admin' },
620
+ { name: 'Users', path: 'users', iconType: 'user' },
621
+ // ... more menu items
622
+ ];
623
+ }
624
+ ```
625
+
626
+ ### 2. Complete Routing Structure
627
+
628
+ ```tsx
629
+ // App.tsx
630
+ export function App() {
631
+ return (
632
+ <Panel
633
+ onInit={appData => {
634
+ if (appData.token) {
635
+ setAuthToken(appData.token);
636
+ }
637
+ }}
638
+ >
639
+ <Router>
640
+ <Routes>
641
+ <Route path="/" element={<AuthLayout />}>
642
+ <Route path={'/'} index element={<Dashboard />} />
643
+
644
+ <Route path={'admins'}>
645
+ <Route path={''} element={<ListPage key="admin-list" model={AdminList} />} />
646
+ <Route
647
+ path={'create'}
648
+ element={<FormPage key="admin-create" model={CreateAdminForm} />}
649
+ />
650
+ <Route
651
+ path={'edit/:id'}
652
+ element={<FormPage key="admin-edit" model={EditAdminForm} />}
653
+ />
654
+ <Route
655
+ path={':id'}
656
+ element={<DetailsPage key="admin-details" model={AdminDetails} />}
657
+ />
658
+ </Route>
659
+
660
+ <Route path={'users'}>
661
+ <Route path={''} element={<ListPage key="user-list" model={UserList} />} />
662
+ <Route
663
+ path={'create'}
664
+ element={<FormPage key="user-create" model={CreateUserForm} />}
665
+ />
666
+ <Route
667
+ path={'edit/:id'}
668
+ element={<FormPage key="user-edit" model={EditUserForm} />}
669
+ />
670
+ <Route
671
+ path={':id'}
672
+ element={<DetailsPage key="user-details" model={UserDetails} />}
673
+ />
674
+ </Route>
675
+ </Route>
676
+
677
+ <Route path="/login" element={<Login key="login" model={LoginForm} />} />
678
+ </Routes>
679
+ </Router>
680
+ </Panel>
681
+ );
682
+ }
683
+ ```
684
+
685
+ ## Advanced Features
686
+
687
+ ### 1. Custom Components
688
+
689
+ ```tsx
690
+ // Custom header component
691
+ export function AdminListHeader() {
692
+ return (
693
+ <div>
694
+ <h2>Admin Management</h2>
695
+ <button
696
+ onClick={() => {
697
+ /* custom action */
698
+ }}
699
+ >
700
+ Custom Action
701
+ </button>
702
+ </div>
703
+ );
704
+ }
705
+
706
+ // Use in routing
707
+ <Route
708
+ path=""
709
+ element={<ListPage key="admin-list" customHeader={<AdminListHeader />} model={AdminList} />}
710
+ />;
711
+ ```
712
+
713
+ ### 2. Dashboard Implementation
714
+
715
+ ```tsx
716
+ // Dashboard.tsx
717
+ import React from 'react';
718
+ import { Counter } from 'proje-react-panel';
719
+
720
+ export function Dashboard() {
721
+ return (
722
+ <div className="dashboard">
723
+ <Counter targetNumber={100} duration={2000} image={''} text={'Products'} />
724
+ <Counter targetNumber={50} duration={2000} image={''} text={'Users'} />
725
+ <Counter targetNumber={25} duration={2000} image={''} text={'Admins'} />
726
+ </div>
727
+ );
728
+ }
729
+ ```
730
+
731
+ ### 3. File Upload Forms
732
+
733
+ ```typescript
734
+ // For file uploads, use type: "formData"
735
+ @Form({
736
+ onSubmit: dataFetchers.assets.create,
737
+ type: 'formData', // Important for file uploads
738
+ redirectSuccessUrl: '/assets',
739
+ })
740
+ export class CreateAssetForm {
741
+ @Input({ label: 'File', type: 'file' })
742
+ file: File;
743
+ }
744
+ ```
745
+
746
+ ### 4. Conditional Fields
747
+
748
+ ```typescript
749
+ class UserForm {
750
+ @ValidateIf(o => !o.__formEdit)
751
+ @IsString()
752
+ @MinLength(6)
753
+ @Input({ label: 'Password', inputType: 'password' })
754
+ password: string;
755
+
756
+ @ValidateIf(o => o.role === 'admin')
757
+ @Input({ label: 'Admin Code' })
758
+ adminCode: string;
759
+ }
760
+ ```
761
+
762
+ ## Complete Example
763
+
764
+ Here's a complete example of a simple CRUD implementation:
765
+
766
+ ### 1. Model Definition
767
+
768
+ ```typescript
769
+ // types/User.ts
770
+ import { IsEmail, IsString, MinLength, IsBoolean } from 'class-validator';
771
+ import { Cell, List, Input, Form, Details, DetailsItem } from 'proje-react-panel';
772
+ import { dataFetchers } from '../api/dataFetchers';
773
+
774
+ @List({
775
+ headers: {
776
+ create: { path: 'create', label: 'Create' },
777
+ },
778
+ actions: (item: UserList) => ({
779
+ details: { path: '' + item.id, label: 'Details' },
780
+ edit: { path: 'edit/' + item.id, label: 'Edit' },
781
+ delete: { label: 'Delete', onRemoveItem: dataFetchers.users.remove },
782
+ }),
783
+ getData: dataFetchers.users.getAll,
784
+ primaryId: 'id',
785
+ })
786
+ export class UserList {
787
+ @Cell({ title: 'ID', type: 'uuid' })
788
+ id: string;
789
+
790
+ @Cell({ title: 'Username' })
791
+ username: string;
792
+
793
+ @Cell({ title: 'Email' })
794
+ email: string;
795
+
796
+ @Cell({ title: 'Is Active', type: 'boolean' })
797
+ isActive: boolean;
798
+
799
+ @Cell({ title: 'Created At', type: 'date' })
800
+ createdAt: Date;
801
+ }
802
+
803
+ class UserForm {
804
+ @IsString()
805
+ @MinLength(3)
806
+ @Input({ label: 'Username' })
807
+ username: string;
808
+
809
+ @IsEmail()
810
+ @Input({ label: 'Email', inputType: 'email' })
811
+ email: string;
812
+
813
+ @IsString()
814
+ @MinLength(6)
815
+ @Input({ label: 'Password', inputType: 'password' })
816
+ password: string;
817
+
818
+ @IsBoolean()
819
+ @Input({ label: 'Is Active', type: 'checkbox' })
820
+ isActive: boolean;
821
+ }
822
+
823
+ @Form({
824
+ onSubmit: dataFetchers.users.create,
825
+ redirectSuccessUrl: '/users',
826
+ })
827
+ export class CreateUserForm extends UserForm {}
828
+
829
+ @Form({
830
+ onSubmit: dataFetchers.users.update,
831
+ getDetailsData: dataFetchers.users.updateDetails,
832
+ redirectSuccessUrl: '/users',
833
+ })
834
+ export class EditUserForm extends UserForm {
835
+ @Input({ label: 'ID', type: 'hidden' })
836
+ id: string;
837
+ }
838
+
839
+ @Details({
840
+ getDetailsData: dataFetchers.users.details,
841
+ primaryId: 'id',
842
+ })
843
+ export class DetailsUserForm {
844
+ @DetailsItem()
845
+ id: string;
846
+
847
+ @DetailsItem()
848
+ username: string;
849
+
850
+ @DetailsItem()
851
+ email: string;
852
+
853
+ @DetailsItem()
854
+ isActive: boolean;
855
+
856
+ @DetailsItem()
857
+ createdAt: Date;
858
+ }
859
+ ```
860
+
861
+ ### 2. Routing Implementation
862
+
863
+ ```tsx
864
+ // App.tsx
865
+ <Route path={'users'}>
866
+ <Route path={''} element={<ListPage key="user-list" model={UserList} />} />
867
+ <Route path={'create'} element={<FormPage key="user-create" model={CreateUserForm} />} />
868
+ <Route path={'edit/:id'} element={<FormPage key="user-edit" model={EditUserForm} />} />
869
+ <Route path={':id'} element={<DetailsPage key="user-details" model={DetailsUserForm} />} />
870
+ </Route>
871
+ ```
872
+
873
+ ## Best Practices
874
+
875
+ 1. **Model Organization**: Keep your models organized in separate files by entity
876
+ 2. **API Configuration**: Centralize your API configuration and data fetchers
877
+ 3. **Error Handling**: Implement proper error handling in your API layer
878
+ 4. **Validation**: Use class-validator decorators for form validation
879
+ 5. **Type Safety**: Leverage TypeScript for better type safety
880
+ 6. **Customization**: Use custom headers and components when needed
881
+ 7. **Performance**: Use proper keys for React components to avoid unnecessary re-renders
882
+
883
+ ## Troubleshooting
884
+
885
+ ### Common Issues
886
+
887
+ 1. **Authentication Issues**: Ensure your API configuration is properly initialized
888
+ 2. **Form Validation**: Check that class-validator decorators are properly applied
889
+ 3. **Routing**: Make sure your route paths match your model configurations
890
+ 4. **API Calls**: Verify your data fetchers are correctly configured
891
+
892
+ ### Debug Tips
893
+
894
+ 1. Check browser console for errors
895
+ 2. Verify API endpoints are working
896
+ 3. Ensure all required dependencies are installed
897
+ 4. Check that decorators are properly imported
898
+
899
+ This guide provides a comprehensive overview of implementing the Proje React Panel library. For more specific use cases, refer to the examples folder in the repository.