proje-react-panel 1.0.15 → 1.0.17-test1
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/.vscode/launch.json +10 -0
- package/dist/components/components/FormField.d.ts +2 -1
- package/dist/components/components/InnerForm.d.ts +2 -2
- package/dist/components/components/list/Datagrid.d.ts +9 -0
- package/dist/components/components/list/EmptyList.d.ts +2 -0
- package/dist/components/components/list/FilterPopup.d.ts +11 -0
- package/dist/components/components/list/ListPage.d.ts +20 -0
- package/dist/components/components/list/Pagination.d.ts +11 -0
- package/dist/components/components/list/index.d.ts +0 -0
- package/dist/components/list/Datagrid.d.ts +8 -4
- package/dist/components/list/EmptyList.d.ts +2 -0
- package/dist/components/list/FilterPopup.d.ts +10 -0
- package/dist/components/pages/FormPage.d.ts +3 -2
- package/dist/components/pages/ListPage.d.ts +2 -1
- package/dist/decorators/form/Input.d.ts +7 -3
- package/dist/decorators/list/Cell.d.ts +14 -2
- package/dist/decorators/list/List.d.ts +26 -4
- package/dist/decorators/list/ListData.d.ts +4 -4
- package/dist/decorators/list/getListFields.d.ts +2 -2
- package/dist/index.cjs.js +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.esm.js +1 -1
- package/dist/types/ScreenCreatorData.d.ts +5 -5
- package/package.json +9 -3
- package/src/assets/icons/svg/create.svg +9 -0
- package/src/assets/icons/svg/filter.svg +3 -0
- package/src/assets/icons/svg/pencil.svg +8 -0
- package/src/assets/icons/svg/search.svg +8 -0
- package/src/assets/icons/svg/trash.svg +8 -0
- package/src/components/components/FormField.tsx +41 -7
- package/src/components/components/InnerForm.tsx +8 -9
- package/src/components/components/list/Datagrid.tsx +127 -0
- package/src/components/components/list/EmptyList.tsx +26 -0
- package/src/components/components/list/FilterPopup.tsx +202 -0
- package/src/components/components/list/ListPage.tsx +178 -0
- package/src/components/pages/FormPage.tsx +4 -2
- package/src/decorators/form/Input.ts +4 -3
- package/src/decorators/list/Cell.ts +24 -14
- package/src/decorators/list/List.ts +26 -11
- package/src/decorators/list/ListData.ts +5 -5
- package/src/decorators/list/getListFields.ts +8 -8
- package/src/index.ts +8 -3
- package/src/styles/filter-popup.scss +134 -0
- package/src/styles/index.scss +8 -13
- package/src/styles/list.scss +149 -8
- package/src/types/ScreenCreatorData.ts +12 -12
- package/src/types/svg.d.ts +5 -0
- package/src/components/list/Datagrid.tsx +0 -101
- package/src/components/pages/ListPage.tsx +0 -85
- /package/src/components/{list → components/list}/Pagination.tsx +0 -0
- /package/src/components/{list → components/list}/index.ts +0 -0
- /package/src/styles/{_scrollbar.scss → utils/scrollbar.scss} +0 -0
@@ -1,7 +1,7 @@
|
|
1
|
-
import { ListOptions } from
|
2
|
-
import { CellOptions } from
|
1
|
+
import { ListOptions } from './List';
|
2
|
+
import { CellOptions } from './Cell';
|
3
3
|
|
4
|
-
export interface ListData {
|
5
|
-
|
6
|
-
|
4
|
+
export interface ListData<T> {
|
5
|
+
list?: ListOptions<T>;
|
6
|
+
cells: CellOptions[];
|
7
7
|
}
|
@@ -1,10 +1,10 @@
|
|
1
|
-
import { getClassListData } from
|
2
|
-
import { getCellFields } from
|
3
|
-
import { ListData } from
|
1
|
+
import { getClassListData } from './List';
|
2
|
+
import { getCellFields } from './GetCellFields';
|
3
|
+
import { ListData } from './ListData';
|
4
4
|
|
5
|
-
export function getListFields<T>(entityClass: T): ListData {
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
export function getListFields<T>(entityClass: T): ListData<T> {
|
6
|
+
return {
|
7
|
+
list: getClassListData(entityClass),
|
8
|
+
cells: getCellFields(entityClass),
|
9
|
+
};
|
10
10
|
}
|
package/src/index.ts
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
export type { OnSubmitFN, GetDetailsDataFN } from './components/pages/FormPage';
|
2
|
-
export type { GetDataForList } from './components/pages/ListPage';
|
3
|
-
export type { PaginatedResponse, PaginationParams } from './components/pages/ListPage';
|
4
2
|
export type { InitPanelOptions } from './types/initPanelOptions';
|
5
3
|
export type { ScreenCreatorData } from './types/ScreenCreatorData';
|
6
4
|
export type { OnLogin } from './components/pages/Login';
|
7
5
|
export type { AnyClass } from './types/AnyClass';
|
6
|
+
export type {
|
7
|
+
GetDataForList,
|
8
|
+
PaginatedResponse,
|
9
|
+
GetDataParams,
|
10
|
+
} from './components/components/list/ListPage';
|
8
11
|
|
9
|
-
export { ListPage } from './components/
|
12
|
+
export { ListPage } from './components/components/list/ListPage';
|
10
13
|
export { FormPage } from './components/pages/FormPage';
|
11
14
|
export type { FormPageProps } from './components/pages/FormPage';
|
12
15
|
export { Login } from './components/pages/Login';
|
@@ -21,3 +24,5 @@ export { Crud } from './decorators/Crud';
|
|
21
24
|
export { Cell } from './decorators/list/Cell';
|
22
25
|
export { Input } from './decorators/form/Input';
|
23
26
|
// Export page components
|
27
|
+
export { getFormFields } from './decorators/form/getFormFields';
|
28
|
+
export { getInputFields } from './decorators/form/Input';
|
@@ -0,0 +1,134 @@
|
|
1
|
+
.filter-popup {
|
2
|
+
position: absolute;
|
3
|
+
top: 60px; //TODO: make this variable $list-header-height
|
4
|
+
//TODO: make this variable
|
5
|
+
left: 260px;
|
6
|
+
right: 10px;
|
7
|
+
background: #1e1e1e;
|
8
|
+
padding: 20px;
|
9
|
+
border-radius: 8px;
|
10
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
11
|
+
border: 1px solid #333;
|
12
|
+
z-index: 10;
|
13
|
+
&-header {
|
14
|
+
display: flex;
|
15
|
+
justify-content: space-between;
|
16
|
+
align-items: center;
|
17
|
+
margin-bottom: 20px;
|
18
|
+
|
19
|
+
h3 {
|
20
|
+
margin: 0;
|
21
|
+
font-size: 1.2rem;
|
22
|
+
color: #ffffff;
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
&-content {
|
27
|
+
max-height: 400px;
|
28
|
+
overflow-y: auto;
|
29
|
+
|
30
|
+
&::-webkit-scrollbar {
|
31
|
+
width: 8px;
|
32
|
+
}
|
33
|
+
|
34
|
+
&::-webkit-scrollbar-track {
|
35
|
+
background: #2d2d2d;
|
36
|
+
border-radius: 4px;
|
37
|
+
}
|
38
|
+
|
39
|
+
&::-webkit-scrollbar-thumb {
|
40
|
+
background: #444;
|
41
|
+
border-radius: 4px;
|
42
|
+
|
43
|
+
&:hover {
|
44
|
+
background: #555;
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
&-footer {
|
50
|
+
display: flex;
|
51
|
+
justify-content: flex-end;
|
52
|
+
gap: 10px;
|
53
|
+
margin-top: 20px;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
.close-button {
|
58
|
+
background: none;
|
59
|
+
border: none;
|
60
|
+
font-size: 1.5rem;
|
61
|
+
cursor: pointer;
|
62
|
+
padding: 0;
|
63
|
+
color: #999;
|
64
|
+
transition: color 0.2s ease;
|
65
|
+
|
66
|
+
&:hover {
|
67
|
+
color: #ffffff;
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
.filter-field {
|
72
|
+
margin-bottom: 15px;
|
73
|
+
|
74
|
+
label {
|
75
|
+
display: block;
|
76
|
+
margin-bottom: 5px;
|
77
|
+
font-size: 0.9rem;
|
78
|
+
color: #bbb;
|
79
|
+
}
|
80
|
+
|
81
|
+
input {
|
82
|
+
width: 100%;
|
83
|
+
padding: 10px;
|
84
|
+
border: 1px solid #444;
|
85
|
+
border-radius: 6px;
|
86
|
+
font-size: 0.9rem;
|
87
|
+
background: #2d2d2d;
|
88
|
+
color: #ffffff;
|
89
|
+
transition: all 0.2s ease;
|
90
|
+
|
91
|
+
&:focus {
|
92
|
+
border-color: #0066cc;
|
93
|
+
outline: none;
|
94
|
+
box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.2);
|
95
|
+
}
|
96
|
+
|
97
|
+
&::placeholder {
|
98
|
+
color: #666;
|
99
|
+
}
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
.cancel-button,
|
104
|
+
.apply-button {
|
105
|
+
padding: 10px 20px;
|
106
|
+
border-radius: 6px;
|
107
|
+
cursor: pointer;
|
108
|
+
font-size: 0.9rem;
|
109
|
+
transition: all 0.2s ease;
|
110
|
+
}
|
111
|
+
|
112
|
+
.cancel-button {
|
113
|
+
background: #2d2d2d;
|
114
|
+
border: 1px solid #444;
|
115
|
+
color: #bbb;
|
116
|
+
|
117
|
+
&:hover {
|
118
|
+
background: #333;
|
119
|
+
border-color: #555;
|
120
|
+
color: #fff;
|
121
|
+
}
|
122
|
+
}
|
123
|
+
|
124
|
+
.apply-button {
|
125
|
+
background: #0066cc;
|
126
|
+
border: 1px solid #0056b3;
|
127
|
+
color: white;
|
128
|
+
|
129
|
+
&:hover {
|
130
|
+
background: #0056b3;
|
131
|
+
transform: translateY(-1px);
|
132
|
+
box-shadow: 0 2px 8px rgba(0, 102, 204, 0.3);
|
133
|
+
}
|
134
|
+
}
|
package/src/styles/index.scss
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
+
@forward './layout';
|
1
2
|
@forward './sidebar';
|
2
3
|
@forward './form';
|
3
|
-
@forward './layout';
|
4
4
|
@forward './list';
|
5
5
|
@forward './login';
|
6
6
|
@forward './error-boundary';
|
@@ -8,6 +8,7 @@
|
|
8
8
|
@forward './counter';
|
9
9
|
@forward './loading-screen';
|
10
10
|
@forward './pagination';
|
11
|
+
@forward './filter-popup';
|
11
12
|
|
12
13
|
.layout {
|
13
14
|
display: flex;
|
@@ -31,16 +32,10 @@
|
|
31
32
|
*::after {
|
32
33
|
box-sizing: border-box;
|
33
34
|
}
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
@import './error-boundary';
|
41
|
-
@import './image-uploader';
|
42
|
-
@import './counter';
|
43
|
-
@import './loading-screen';
|
44
|
-
@import './pagination';
|
45
|
-
@import './scrollbar';
|
35
|
+
}
|
36
|
+
.icon {
|
37
|
+
width: 14px;
|
38
|
+
height: 14px;
|
39
|
+
stroke: currentColor;
|
40
|
+
fill: currentColor;
|
46
41
|
}
|
package/src/styles/list.scss
CHANGED
@@ -1,12 +1,9 @@
|
|
1
|
-
@import '
|
1
|
+
@import 'utils/scrollbar';
|
2
2
|
$header-height: 60px;
|
3
3
|
$footer-height: 60px;
|
4
4
|
$datagrid-height: calc(100vh - ($header-height + $footer-height));
|
5
5
|
.list {
|
6
6
|
width: 100%;
|
7
|
-
padding: 0 0 0 16px;
|
8
|
-
border-radius: 8px;
|
9
|
-
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
10
7
|
background-color: #2b2b2b;
|
11
8
|
color: #e0e0e0;
|
12
9
|
max-height: 100vh;
|
@@ -16,11 +13,51 @@ $datagrid-height: calc(100vh - ($header-height + $footer-height));
|
|
16
13
|
|
17
14
|
.list-header {
|
18
15
|
height: $header-height;
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
display: flex;
|
17
|
+
align-items: center;
|
18
|
+
justify-content: space-between;
|
19
|
+
padding: 0 24px;
|
20
|
+
background: linear-gradient(90deg, #2b2b2b 0%, #3c3c3c 100%);
|
21
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
22
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
23
|
+
|
24
|
+
.header-title {
|
25
|
+
font-size: 20px;
|
26
|
+
font-weight: 600;
|
27
|
+
color: #ffffff;
|
28
|
+
text-transform: uppercase;
|
29
|
+
letter-spacing: 0.5px;
|
30
|
+
}
|
31
|
+
|
32
|
+
.header-actions {
|
33
|
+
display: flex;
|
34
|
+
gap: 16px;
|
35
|
+
align-items: center;
|
36
|
+
|
37
|
+
.create-button {
|
38
|
+
padding: 8px 16px;
|
39
|
+
background-color: #4caf50;
|
40
|
+
color: white;
|
41
|
+
border-radius: 4px;
|
42
|
+
text-decoration: none;
|
43
|
+
font-size: 14px;
|
44
|
+
font-weight: 500;
|
45
|
+
transition: background-color 0.2s ease;
|
46
|
+
|
47
|
+
&:hover {
|
48
|
+
background-color: #45a049;
|
49
|
+
}
|
50
|
+
.icon-create {
|
51
|
+
fill: currentColor;
|
52
|
+
stroke: none;
|
53
|
+
position: relative;
|
54
|
+
top: 2px;
|
55
|
+
right: 2px;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
23
59
|
}
|
60
|
+
|
24
61
|
.list-footer {
|
25
62
|
display: flex;
|
26
63
|
justify-content: space-between;
|
@@ -32,6 +69,7 @@ $datagrid-height: calc(100vh - ($header-height + $footer-height));
|
|
32
69
|
color: #ffffff;
|
33
70
|
}
|
34
71
|
.datagrid {
|
72
|
+
padding: 0 0 0 8px;
|
35
73
|
height: $datagrid-height;
|
36
74
|
min-height: $datagrid-height;
|
37
75
|
max-height: $datagrid-height;
|
@@ -65,4 +103,107 @@ $datagrid-height: calc(100vh - ($header-height + $footer-height));
|
|
65
103
|
background-color: #444444;
|
66
104
|
}
|
67
105
|
}
|
106
|
+
|
107
|
+
.empty-list {
|
108
|
+
display: flex;
|
109
|
+
align-items: center;
|
110
|
+
justify-content: center;
|
111
|
+
height: 100%;
|
112
|
+
width: 100%;
|
113
|
+
padding: 2rem;
|
114
|
+
|
115
|
+
.empty-list-content {
|
116
|
+
text-align: center;
|
117
|
+
color: #888;
|
118
|
+
|
119
|
+
svg {
|
120
|
+
margin-bottom: 1rem;
|
121
|
+
color: #666;
|
122
|
+
}
|
123
|
+
|
124
|
+
h3 {
|
125
|
+
font-size: 1.5rem;
|
126
|
+
margin-bottom: 0.5rem;
|
127
|
+
font-weight: 500;
|
128
|
+
color: #fff;
|
129
|
+
}
|
130
|
+
|
131
|
+
p {
|
132
|
+
font-size: 1rem;
|
133
|
+
color: #888;
|
134
|
+
}
|
135
|
+
}
|
136
|
+
}
|
137
|
+
}
|
138
|
+
|
139
|
+
.util-cell-link {
|
140
|
+
display: flex;
|
141
|
+
align-items: center;
|
142
|
+
gap: 8px;
|
143
|
+
color: #666;
|
144
|
+
text-decoration: none;
|
145
|
+
transition: color 0.2s ease;
|
146
|
+
|
147
|
+
&:hover {
|
148
|
+
color: #333;
|
149
|
+
}
|
150
|
+
|
151
|
+
.icon {
|
152
|
+
width: 16px;
|
153
|
+
height: 16px;
|
154
|
+
stroke: none;
|
155
|
+
&.icon-trash {
|
156
|
+
fill: #ff0000;
|
157
|
+
stroke: none;
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
.util-cell-label {
|
162
|
+
font-size: 14px;
|
163
|
+
}
|
164
|
+
}
|
165
|
+
|
166
|
+
.filter-button {
|
167
|
+
background-color: #4caf50;
|
168
|
+
color: white;
|
169
|
+
border: none;
|
170
|
+
padding: 8px 16px;
|
171
|
+
border-radius: 4px;
|
172
|
+
cursor: pointer;
|
173
|
+
transition: background-color 0.2s ease;
|
174
|
+
|
175
|
+
&:hover {
|
176
|
+
background-color: #45a049;
|
177
|
+
}
|
178
|
+
.icon-filter {
|
179
|
+
position: relative;
|
180
|
+
top: 2px;
|
181
|
+
right: 2px;
|
182
|
+
width: 15px;
|
183
|
+
height: 15px;
|
184
|
+
stroke: currentColor;
|
185
|
+
stroke-width: 2;
|
186
|
+
fill: none;
|
187
|
+
&.active {
|
188
|
+
fill: currentColor;
|
189
|
+
}
|
190
|
+
}
|
191
|
+
}
|
192
|
+
.header-custom {
|
193
|
+
display: flex;
|
194
|
+
flex: 1;
|
195
|
+
align-items: center;
|
196
|
+
justify-content: flex-start;
|
197
|
+
padding: 0 24px;
|
198
|
+
gap: 16px;
|
199
|
+
a {
|
200
|
+
padding: 8px 16px;
|
201
|
+
background-color: #4caf50;
|
202
|
+
color: white;
|
203
|
+
border-radius: 4px;
|
204
|
+
text-decoration: none;
|
205
|
+
font-size: 14px;
|
206
|
+
font-weight: 500;
|
207
|
+
transition: background-color 0.2s ease;
|
208
|
+
}
|
68
209
|
}
|
@@ -1,14 +1,14 @@
|
|
1
|
-
import { CellOptions } from
|
2
|
-
import { CrudOptions } from
|
3
|
-
import { InputOptions } from
|
4
|
-
import { ListOptions } from
|
5
|
-
|
1
|
+
import { CellOptions } from '../decorators/list/Cell';
|
2
|
+
import { CrudOptions } from '../decorators/Crud';
|
3
|
+
import { InputOptions } from '../decorators/form/Input';
|
4
|
+
import { ListOptions } from '../decorators/list/List';
|
5
|
+
//TODO: remove
|
6
6
|
export interface ScreenCreatorData {
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
resolver: any;
|
8
|
+
fields: string[];
|
9
|
+
inputs: InputOptions[];
|
10
|
+
crud?: CrudOptions;
|
11
|
+
path: string;
|
12
|
+
list?: ListOptions<any>;
|
13
|
+
cells: CellOptions[];
|
14
14
|
}
|
@@ -1,101 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import { CellOptions } from '../../decorators/list/Cell';
|
3
|
-
import { Link } from 'react-router';
|
4
|
-
import { useAppStore } from '../../store/store';
|
5
|
-
import { ImageCellOptions } from '../../decorators/list/ImageCell';
|
6
|
-
|
7
|
-
interface ListProps<T> {
|
8
|
-
data: T[];
|
9
|
-
cells: CellOptions[];
|
10
|
-
}
|
11
|
-
|
12
|
-
export function Datagrid<T>({ data, cells }: ListProps<T>) {
|
13
|
-
if (!data || data.length === 0) {
|
14
|
-
return <div>No items available</div>;
|
15
|
-
}
|
16
|
-
|
17
|
-
return (
|
18
|
-
<div className="datagrid">
|
19
|
-
<table className="datagrid-table">
|
20
|
-
<thead>
|
21
|
-
<tr>
|
22
|
-
{cells.map(cellOptions => (
|
23
|
-
<th key={cellOptions.name}>{cellOptions.title ?? cellOptions.name}</th>
|
24
|
-
))}
|
25
|
-
<th />
|
26
|
-
<th>Delete</th>
|
27
|
-
</tr>
|
28
|
-
</thead>
|
29
|
-
<tbody>
|
30
|
-
{data.map((item, index) => (
|
31
|
-
<tr key={index}>
|
32
|
-
{cells.map(cellOptions => {
|
33
|
-
// @ts-ignore
|
34
|
-
const value = item[cellOptions.name];
|
35
|
-
let render = value ?? '-'; // Default value if the field is undefined or null
|
36
|
-
|
37
|
-
switch (cellOptions.type) {
|
38
|
-
case 'date':
|
39
|
-
if (value) {
|
40
|
-
const date = new Date(value);
|
41
|
-
render = `${date.getDate().toString().padStart(2, '0')}/${(
|
42
|
-
date.getMonth() + 1
|
43
|
-
)
|
44
|
-
.toString()
|
45
|
-
.padStart(
|
46
|
-
2,
|
47
|
-
'0'
|
48
|
-
)}/${date.getFullYear()} ${date.getHours().toString().padStart(2, '0')}:${date
|
49
|
-
.getMinutes()
|
50
|
-
.toString()
|
51
|
-
.padStart(2, '0')}`;
|
52
|
-
}
|
53
|
-
break;
|
54
|
-
|
55
|
-
case 'image': {
|
56
|
-
const imageCellOptions = cellOptions as ImageCellOptions;
|
57
|
-
render = (
|
58
|
-
<img
|
59
|
-
width={100}
|
60
|
-
height={100}
|
61
|
-
src={imageCellOptions.baseUrl + value}
|
62
|
-
style={{ objectFit: 'contain' }}
|
63
|
-
/>
|
64
|
-
);
|
65
|
-
break;
|
66
|
-
}
|
67
|
-
case 'string':
|
68
|
-
default:
|
69
|
-
render = value ? value.toString() : (cellOptions?.placeHolder ?? '-'); // Handles string type or default fallback
|
70
|
-
break;
|
71
|
-
}
|
72
|
-
/*
|
73
|
-
if (cellOptions.linkTo) {
|
74
|
-
render = <Link to={cellOptions.linkTo(item)}>{formattedValue}</Link>;
|
75
|
-
}
|
76
|
-
*/
|
77
|
-
return <td key={cellOptions.name}>{render}</td>;
|
78
|
-
})}
|
79
|
-
<td>
|
80
|
-
{/*@ts-ignore*/}
|
81
|
-
<Link to={'edit/' + (item?.id ?? '-')}>Edit</Link>
|
82
|
-
{/*@ts-ignore*/}
|
83
|
-
<Link to={'details/' + (item?.id ?? '-')}>Details</Link>
|
84
|
-
</td>
|
85
|
-
<td>
|
86
|
-
<button
|
87
|
-
onClick={() => {
|
88
|
-
/*@ts-ignore*/
|
89
|
-
//CrudApi.delete({ ...fetchSettings, token }, screen.controller, item?.id);
|
90
|
-
}}
|
91
|
-
>
|
92
|
-
Delete
|
93
|
-
</button>
|
94
|
-
</td>
|
95
|
-
</tr>
|
96
|
-
))}
|
97
|
-
</tbody>
|
98
|
-
</table>
|
99
|
-
</div>
|
100
|
-
);
|
101
|
-
}
|
@@ -1,85 +0,0 @@
|
|
1
|
-
import React, { useMemo, useCallback } from 'react';
|
2
|
-
import { useEffect, useState } from 'react';
|
3
|
-
import { Link, useParams } from 'react-router';
|
4
|
-
import { Datagrid } from '../list/Datagrid';
|
5
|
-
import { ErrorComponent } from '../components/ErrorComponent';
|
6
|
-
import { LoadingScreen } from '../components/LoadingScreen';
|
7
|
-
import { AnyClass } from '../../types/AnyClass';
|
8
|
-
import { getListFields } from '../../decorators/list/getListFields';
|
9
|
-
import { Pagination } from '../list/Pagination';
|
10
|
-
|
11
|
-
export interface PaginationParams {
|
12
|
-
page?: number;
|
13
|
-
limit?: number;
|
14
|
-
}
|
15
|
-
|
16
|
-
export interface PaginatedResponse<T> {
|
17
|
-
data: T[];
|
18
|
-
total: number;
|
19
|
-
page: number;
|
20
|
-
limit: number;
|
21
|
-
}
|
22
|
-
|
23
|
-
export type GetDataForList<T> = ({
|
24
|
-
page,
|
25
|
-
}: PaginationParams) => PaginatedResponse<T> | Promise<PaginatedResponse<T>>;
|
26
|
-
|
27
|
-
export function ListPage<T extends AnyClass>({
|
28
|
-
model,
|
29
|
-
getData,
|
30
|
-
}: {
|
31
|
-
model: T;
|
32
|
-
getData: GetDataForList<T>;
|
33
|
-
}) {
|
34
|
-
const [loading, setLoading] = useState(true);
|
35
|
-
const [pagination, setPagination] = useState({ total: 0, page: 0, limit: 0 });
|
36
|
-
const [data, setData] = useState<any>(null);
|
37
|
-
const [error, setError] = useState<unknown | null>(null);
|
38
|
-
const listData = useMemo(() => getListFields(model), [model]);
|
39
|
-
const params = useParams();
|
40
|
-
|
41
|
-
const fetchData = useCallback(
|
42
|
-
async (page: number) => {
|
43
|
-
setLoading(true);
|
44
|
-
try {
|
45
|
-
const result = await getData({ page });
|
46
|
-
setData(result.data);
|
47
|
-
setPagination({
|
48
|
-
total: result.total,
|
49
|
-
page: result.page,
|
50
|
-
limit: result.limit,
|
51
|
-
});
|
52
|
-
} catch (e: unknown) {
|
53
|
-
setError(e);
|
54
|
-
console.error(e);
|
55
|
-
} finally {
|
56
|
-
setLoading(false);
|
57
|
-
}
|
58
|
-
},
|
59
|
-
[getData]
|
60
|
-
);
|
61
|
-
|
62
|
-
useEffect(() => {
|
63
|
-
fetchData(parseInt(params.page as string) || 1);
|
64
|
-
}, [fetchData, params.page]);
|
65
|
-
|
66
|
-
if (loading) return <LoadingScreen />;
|
67
|
-
if (error) return <ErrorComponent error={error} />;
|
68
|
-
|
69
|
-
return (
|
70
|
-
<div className="list">
|
71
|
-
<div className="list-header">
|
72
|
-
<Link to={'create'}>Create</Link>
|
73
|
-
</div>
|
74
|
-
<Datagrid cells={listData.cells} data={data} />
|
75
|
-
<div className="list-footer">
|
76
|
-
<Pagination
|
77
|
-
pagination={pagination}
|
78
|
-
onPageChange={(page: number) => {
|
79
|
-
fetchData(page);
|
80
|
-
}}
|
81
|
-
/>
|
82
|
-
</div>
|
83
|
-
</div>
|
84
|
-
);
|
85
|
-
}
|
File without changes
|
File without changes
|
File without changes
|