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.
Files changed (52) hide show
  1. package/.vscode/launch.json +10 -0
  2. package/dist/components/components/FormField.d.ts +2 -1
  3. package/dist/components/components/InnerForm.d.ts +2 -2
  4. package/dist/components/components/list/Datagrid.d.ts +9 -0
  5. package/dist/components/components/list/EmptyList.d.ts +2 -0
  6. package/dist/components/components/list/FilterPopup.d.ts +11 -0
  7. package/dist/components/components/list/ListPage.d.ts +20 -0
  8. package/dist/components/components/list/Pagination.d.ts +11 -0
  9. package/dist/components/components/list/index.d.ts +0 -0
  10. package/dist/components/list/Datagrid.d.ts +8 -4
  11. package/dist/components/list/EmptyList.d.ts +2 -0
  12. package/dist/components/list/FilterPopup.d.ts +10 -0
  13. package/dist/components/pages/FormPage.d.ts +3 -2
  14. package/dist/components/pages/ListPage.d.ts +2 -1
  15. package/dist/decorators/form/Input.d.ts +7 -3
  16. package/dist/decorators/list/Cell.d.ts +14 -2
  17. package/dist/decorators/list/List.d.ts +26 -4
  18. package/dist/decorators/list/ListData.d.ts +4 -4
  19. package/dist/decorators/list/getListFields.d.ts +2 -2
  20. package/dist/index.cjs.js +1 -1
  21. package/dist/index.d.ts +4 -3
  22. package/dist/index.esm.js +1 -1
  23. package/dist/types/ScreenCreatorData.d.ts +5 -5
  24. package/package.json +9 -3
  25. package/src/assets/icons/svg/create.svg +9 -0
  26. package/src/assets/icons/svg/filter.svg +3 -0
  27. package/src/assets/icons/svg/pencil.svg +8 -0
  28. package/src/assets/icons/svg/search.svg +8 -0
  29. package/src/assets/icons/svg/trash.svg +8 -0
  30. package/src/components/components/FormField.tsx +41 -7
  31. package/src/components/components/InnerForm.tsx +8 -9
  32. package/src/components/components/list/Datagrid.tsx +127 -0
  33. package/src/components/components/list/EmptyList.tsx +26 -0
  34. package/src/components/components/list/FilterPopup.tsx +202 -0
  35. package/src/components/components/list/ListPage.tsx +178 -0
  36. package/src/components/pages/FormPage.tsx +4 -2
  37. package/src/decorators/form/Input.ts +4 -3
  38. package/src/decorators/list/Cell.ts +24 -14
  39. package/src/decorators/list/List.ts +26 -11
  40. package/src/decorators/list/ListData.ts +5 -5
  41. package/src/decorators/list/getListFields.ts +8 -8
  42. package/src/index.ts +8 -3
  43. package/src/styles/filter-popup.scss +134 -0
  44. package/src/styles/index.scss +8 -13
  45. package/src/styles/list.scss +149 -8
  46. package/src/types/ScreenCreatorData.ts +12 -12
  47. package/src/types/svg.d.ts +5 -0
  48. package/src/components/list/Datagrid.tsx +0 -101
  49. package/src/components/pages/ListPage.tsx +0 -85
  50. /package/src/components/{list → components/list}/Pagination.tsx +0 -0
  51. /package/src/components/{list → components/list}/index.ts +0 -0
  52. /package/src/styles/{_scrollbar.scss → utils/scrollbar.scss} +0 -0
@@ -1,7 +1,7 @@
1
- import { ListOptions } from "./List";
2
- import { CellOptions } from "./Cell";
1
+ import { ListOptions } from './List';
2
+ import { CellOptions } from './Cell';
3
3
 
4
- export interface ListData {
5
- list?: ListOptions;
6
- cells: CellOptions[];
4
+ export interface ListData<T> {
5
+ list?: ListOptions<T>;
6
+ cells: CellOptions[];
7
7
  }
@@ -1,10 +1,10 @@
1
- import { getClassListData } from "./List";
2
- import { getCellFields } from "./GetCellFields";
3
- import { ListData } from "./ListData";
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
- return {
7
- list: getClassListData(entityClass),
8
- cells: getCellFields(entityClass),
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/pages/ListPage';
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
+ }
@@ -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
- // Imported styles will be scoped under .layout
36
- @import './sidebar';
37
- @import './form';
38
- @import './list';
39
- @import './login';
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
  }
@@ -1,12 +1,9 @@
1
- @import './scrollbar';
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
- font-size: 24px;
20
- font-weight: bold;
21
- text-align: center;
22
- color: #ffffff;
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 "../decorators/list/Cell";
2
- import { CrudOptions } from "../decorators/Crud";
3
- import { InputOptions } from "../decorators/form/Input";
4
- import { ListOptions } from "../decorators/list/List";
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
- resolver: any;
8
- fields: string[];
9
- inputs: InputOptions[];
10
- crud?: CrudOptions;
11
- path: string;
12
- list?: ListOptions;
13
- cells: CellOptions[];
7
+ resolver: any;
8
+ fields: string[];
9
+ inputs: InputOptions[];
10
+ crud?: CrudOptions;
11
+ path: string;
12
+ list?: ListOptions<any>;
13
+ cells: CellOptions[];
14
14
  }
@@ -0,0 +1,5 @@
1
+ declare module '*.svg' {
2
+ import React from 'react';
3
+ const SVGComponent: React.FC<React.SVGProps<SVGSVGElement>>;
4
+ export default SVGComponent;
5
+ }
@@ -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
- }