proje-react-panel 1.0.17 → 1.1.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.
- package/dist/components/Counter.d.ts +9 -0
- package/dist/components/DetailsPage.d.ts +7 -0
- package/dist/components/ErrorBoundary.d.ts +16 -0
- package/dist/components/ErrorComponent.d.ts +4 -0
- package/dist/components/LoadingScreen.d.ts +2 -0
- package/dist/components/Login.d.ts +13 -0
- package/dist/components/Panel.d.ts +1 -3
- package/dist/components/components/Checkbox.d.ts +3 -2
- package/dist/components/components/FormField.d.ts +5 -1
- package/dist/components/components/InnerForm.d.ts +8 -3
- package/dist/components/components/Label.d.ts +3 -2
- package/dist/components/components/Uploader.d.ts +8 -0
- package/dist/components/components/index.d.ts +1 -1
- package/dist/components/components/list/ListPage.d.ts +1 -1
- package/dist/components/form/Checkbox.d.ts +7 -0
- package/dist/components/form/FormField.d.ts +17 -0
- package/dist/components/form/FormPage.d.ts +6 -0
- package/dist/components/form/InnerForm.d.ts +10 -0
- package/dist/components/form/Label.d.ts +9 -0
- package/dist/components/form/Select.d.ts +8 -0
- package/dist/components/form/SelectStyles.d.ts +7 -0
- package/dist/components/form/Uploader.d.ts +8 -0
- package/dist/components/layout/Layout.d.ts +3 -4
- package/dist/components/layout/SideBar.d.ts +2 -3
- package/dist/components/list/CellField.d.ts +9 -0
- package/dist/components/list/Datagrid.d.ts +6 -8
- package/dist/components/list/FilterPopup.d.ts +7 -5
- package/dist/components/list/ListHeader.d.ts +11 -0
- package/dist/components/list/ListPage.d.ts +6 -0
- package/dist/components/pages/FormPage.d.ts +8 -2
- package/dist/decorators/details/Details.d.ts +11 -0
- package/dist/decorators/details/DetailsItem.d.ts +11 -0
- package/dist/decorators/details/getDetailsPageMeta.d.ts +8 -0
- package/dist/decorators/form/Form.d.ts +16 -5
- package/dist/decorators/form/Input.d.ts +14 -9
- package/dist/decorators/form/getFormPageMeta.d.ts +10 -0
- package/dist/decorators/form/inputs/SelectInput.d.ts +23 -0
- package/dist/decorators/list/Cell.d.ts +13 -1
- package/dist/decorators/list/List.d.ts +18 -1
- package/dist/decorators/list/cells/ImageCell.d.ts +9 -0
- package/dist/decorators/list/getListPageMeta.d.ts +8 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.d.ts +13 -17
- package/dist/index.esm.js +1 -1
- package/dist/initPanel.d.ts +1 -1
- package/dist/store/store.d.ts +0 -3
- package/dist/types/AnyClass.d.ts +2 -1
- package/dist/types/getDetailsData.d.ts +1 -0
- package/dist/types/initPanelOptions.d.ts +0 -1
- package/package.json +1 -1
- package/src/assets/icons/svg/check.svg +4 -0
- package/src/assets/icons/svg/cross.svg +4 -0
- package/src/components/DetailsPage.tsx +55 -0
- package/src/components/{components/ErrorComponent.tsx → ErrorComponent.tsx} +1 -1
- package/src/components/{pages/Login.tsx → Login.tsx} +2 -2
- package/src/components/Panel.tsx +4 -5
- package/src/components/form/Checkbox.tsx +21 -0
- package/src/components/{components → form}/FormField.tsx +22 -30
- package/src/components/form/FormPage.tsx +32 -0
- package/src/components/form/InnerForm.tsx +84 -0
- package/src/components/form/Label.tsx +21 -0
- package/src/components/form/Select.tsx +51 -0
- package/src/components/form/SelectStyles.ts +73 -0
- package/src/components/form/Uploader.tsx +66 -0
- package/src/components/layout/Layout.tsx +29 -32
- package/src/components/layout/SideBar.tsx +4 -13
- package/src/components/list/CellField.tsx +63 -0
- package/src/components/list/Datagrid.tsx +106 -0
- package/src/components/{components/list → list}/FilterPopup.tsx +13 -9
- package/src/components/list/ListHeader.tsx +47 -0
- package/src/components/{components/list → list}/ListPage.tsx +20 -82
- package/src/decorators/details/Details.ts +31 -0
- package/src/decorators/details/DetailsItem.ts +40 -0
- package/src/decorators/details/getDetailsPageMeta.ts +15 -0
- package/src/decorators/form/Form.ts +38 -12
- package/src/decorators/form/Input.ts +31 -9
- package/src/decorators/form/getFormPageMeta.ts +21 -0
- package/src/decorators/form/inputs/SelectInput.ts +19 -0
- package/src/decorators/list/Cell.ts +41 -1
- package/src/decorators/list/List.ts +30 -6
- package/src/decorators/list/cells/ImageCell.ts +17 -0
- package/src/decorators/list/getListPageMeta.ts +16 -0
- package/src/index.ts +33 -24
- package/src/initPanel.ts +1 -4
- package/src/store/store.ts +0 -5
- package/src/styles/components/checkbox.scss +42 -0
- package/src/styles/components/uploader.scss +86 -0
- package/src/styles/details.scss +62 -0
- package/src/styles/form.scss +9 -11
- package/src/styles/index.scss +26 -12
- package/src/styles/list.scss +3 -1
- package/src/types/AnyClass.ts +2 -1
- package/src/types/initPanelOptions.ts +1 -3
- package/src/components/components/Checkbox.tsx +0 -9
- package/src/components/components/ImageUploader.tsx +0 -301
- package/src/components/components/InnerForm.tsx +0 -74
- package/src/components/components/Label.tsx +0 -15
- package/src/components/components/index.ts +0 -8
- package/src/components/components/list/Datagrid.tsx +0 -127
- package/src/components/pages/ControllerDetails.tsx +0 -37
- package/src/components/pages/FormPage.tsx +0 -34
- package/src/decorators/Crud.ts +0 -20
- package/src/decorators/form/FormOptions.ts +0 -8
- package/src/decorators/form/getFormFields.ts +0 -13
- package/src/decorators/list/GetCellFields.ts +0 -13
- package/src/decorators/list/ImageCell.ts +0 -13
- package/src/decorators/list/ListData.ts +0 -7
- package/src/decorators/list/getListFields.ts +0 -10
- package/src/styles/image-uploader.scss +0 -94
- package/src/types/Screen.ts +0 -4
- package/src/types/ScreenCreatorData.ts +0 -14
- package/src/utils/createScreens.ts +0 -5
- package/src/utils/getFields.ts +0 -22
- /package/src/components/{components/Counter.tsx → Counter.tsx} +0 -0
- /package/src/components/{components/ErrorBoundary.tsx → ErrorBoundary.tsx} +0 -0
- /package/src/components/{components/LoadingScreen.tsx → LoadingScreen.tsx} +0 -0
- /package/src/components/{components/list → list}/EmptyList.tsx +0 -0
- /package/src/components/{components/list → list}/Pagination.tsx +0 -0
- /package/src/components/{components/list → list}/index.ts +0 -0
@@ -1,301 +0,0 @@
|
|
1
|
-
import React, { useEffect } from "react";
|
2
|
-
import { useFormContext } from "react-hook-form";
|
3
|
-
import { bytesToSize } from "../../utils/format";
|
4
|
-
|
5
|
-
interface ThumbnailImageProps {
|
6
|
-
name: string;
|
7
|
-
src: string;
|
8
|
-
size: number;
|
9
|
-
style?: React.CSSProperties;
|
10
|
-
}
|
11
|
-
|
12
|
-
interface MultipleImageUploaderProps {
|
13
|
-
value?: Array<{ file: File; image: string; remove?: boolean }>;
|
14
|
-
onError?: (error: string | null) => void;
|
15
|
-
onClear?: () => void;
|
16
|
-
reset?: any;
|
17
|
-
onFilesChange?: (files: File[]) => void;
|
18
|
-
}
|
19
|
-
|
20
|
-
interface FileWithPreview {
|
21
|
-
file: File;
|
22
|
-
image: string;
|
23
|
-
}
|
24
|
-
|
25
|
-
const uploadState = Object.freeze({
|
26
|
-
BEFORE: "before",
|
27
|
-
HOVER: "hover",
|
28
|
-
AFTER: "after",
|
29
|
-
} as const);
|
30
|
-
|
31
|
-
type UploadStateType = (typeof uploadState)[keyof typeof uploadState];
|
32
|
-
|
33
|
-
function ThumbnailImage(props: ThumbnailImageProps) {
|
34
|
-
return (
|
35
|
-
<div>
|
36
|
-
<img {...props} style={{ width: 100 }} />
|
37
|
-
<p>
|
38
|
-
{props.name} <span style={{ whiteSpace: "none" }}>({bytesToSize(props.size)})</span>
|
39
|
-
</p>
|
40
|
-
</div>
|
41
|
-
);
|
42
|
-
}
|
43
|
-
|
44
|
-
export function ImageUploader() {
|
45
|
-
const {
|
46
|
-
register,
|
47
|
-
formState: { errors },
|
48
|
-
watch,
|
49
|
-
setValue,
|
50
|
-
clearErrors,
|
51
|
-
setError,
|
52
|
-
} = useFormContext();
|
53
|
-
const up = watch("uploader");
|
54
|
-
|
55
|
-
useEffect(() => {
|
56
|
-
register("uploader", { required: true });
|
57
|
-
}, [register]);
|
58
|
-
|
59
|
-
return (
|
60
|
-
<div>
|
61
|
-
<span className="form-error" style={{ bottom: 2, top: "unset" }}>
|
62
|
-
{errors.uploader?.type === "required" && "At least 1 image is required!"}
|
63
|
-
{errors.uploader?.type === "custom" && errors.uploader.message?.toString()}
|
64
|
-
</span>
|
65
|
-
<MultipleImageUploader
|
66
|
-
reset={up}
|
67
|
-
onError={(data: string | null) => {
|
68
|
-
if (!data) {
|
69
|
-
setValue("uploader", { files: [] });
|
70
|
-
clearErrors("uploader");
|
71
|
-
} else {
|
72
|
-
setError("uploader", {
|
73
|
-
type: "custom",
|
74
|
-
message: data,
|
75
|
-
});
|
76
|
-
}
|
77
|
-
}}
|
78
|
-
onClear={() => {
|
79
|
-
setValue("uploader", { files: [] });
|
80
|
-
clearErrors("uploader");
|
81
|
-
}}
|
82
|
-
onFilesChange={(files) => {
|
83
|
-
setValue("uploader", { files });
|
84
|
-
}}
|
85
|
-
/>
|
86
|
-
</div>
|
87
|
-
);
|
88
|
-
}
|
89
|
-
|
90
|
-
export function MultipleImageUploader(props: MultipleImageUploaderProps) {
|
91
|
-
const [currentUploadState, setUploadState] = React.useState<UploadStateType>(uploadState.BEFORE);
|
92
|
-
const [images, setImages] = React.useState<Array<{ file: File; image: string; remove?: boolean }>>(
|
93
|
-
props.value || []
|
94
|
-
);
|
95
|
-
const [files, setFiles] = React.useState<FileWithPreview[]>([]);
|
96
|
-
const [counter, setCounter] = React.useState(0);
|
97
|
-
console.log("files", files);
|
98
|
-
const dropzoneElement = React.useRef<HTMLDivElement>(null);
|
99
|
-
const imageInputRef = React.useRef<HTMLInputElement>(null);
|
100
|
-
|
101
|
-
React.useEffect(() => {
|
102
|
-
const element = dropzoneElement.current;
|
103
|
-
if (!element) return;
|
104
|
-
|
105
|
-
const handleDragEnter = (e: DragEvent) => {
|
106
|
-
e.preventDefault();
|
107
|
-
e.stopPropagation();
|
108
|
-
dragEnter();
|
109
|
-
};
|
110
|
-
|
111
|
-
const handleDragLeave = (e: DragEvent) => {
|
112
|
-
e.preventDefault();
|
113
|
-
e.stopPropagation();
|
114
|
-
dragLeave();
|
115
|
-
};
|
116
|
-
|
117
|
-
const handleDragOver = (e: DragEvent) => {
|
118
|
-
e.preventDefault();
|
119
|
-
e.stopPropagation();
|
120
|
-
};
|
121
|
-
|
122
|
-
const handleDrop = (e: DragEvent) => {
|
123
|
-
e.preventDefault();
|
124
|
-
e.stopPropagation();
|
125
|
-
setCounter(0);
|
126
|
-
const droppedFiles = e.dataTransfer?.files;
|
127
|
-
if (!droppedFiles) return;
|
128
|
-
|
129
|
-
setFiles([]);
|
130
|
-
setUploadState(uploadState.AFTER);
|
131
|
-
|
132
|
-
const newFiles: File[] = [];
|
133
|
-
for (let i = 0; i < droppedFiles.length; i++) {
|
134
|
-
const reader = new FileReader();
|
135
|
-
reader.onload = (event) => {
|
136
|
-
if (!event.target) return;
|
137
|
-
const check = onFileChange([
|
138
|
-
...files,
|
139
|
-
{ file: droppedFiles[i], image: event.target.result as string },
|
140
|
-
]);
|
141
|
-
if (check) {
|
142
|
-
newFiles.push(droppedFiles[i]);
|
143
|
-
if (imageInputRef.current) {
|
144
|
-
imageInputRef.current.files = droppedFiles;
|
145
|
-
}
|
146
|
-
setFiles([]);
|
147
|
-
}
|
148
|
-
// Notify parent of file changes
|
149
|
-
if (props.onFilesChange) {
|
150
|
-
props.onFilesChange(newFiles);
|
151
|
-
}
|
152
|
-
};
|
153
|
-
reader.readAsDataURL(droppedFiles[i]);
|
154
|
-
}
|
155
|
-
if (imageInputRef.current) {
|
156
|
-
imageInputRef.current.files = droppedFiles;
|
157
|
-
}
|
158
|
-
};
|
159
|
-
|
160
|
-
element.addEventListener("dragenter", handleDragEnter, false);
|
161
|
-
element.addEventListener("dragleave", handleDragLeave, false);
|
162
|
-
element.addEventListener("dragover", handleDragOver, false);
|
163
|
-
element.addEventListener("drop", handleDrop, false);
|
164
|
-
|
165
|
-
return () => {
|
166
|
-
element.removeEventListener("dragenter", handleDragEnter);
|
167
|
-
element.removeEventListener("dragleave", handleDragLeave);
|
168
|
-
element.removeEventListener("dragover", handleDragOver);
|
169
|
-
element.removeEventListener("drop", handleDrop);
|
170
|
-
};
|
171
|
-
}, [files]);
|
172
|
-
|
173
|
-
const dragEnter = () => {
|
174
|
-
setCounter((prev) => prev + 1);
|
175
|
-
setUploadState(uploadState.HOVER);
|
176
|
-
};
|
177
|
-
|
178
|
-
const dragLeave = () => {
|
179
|
-
setCounter((prev) => {
|
180
|
-
if (prev - 1 === 0) {
|
181
|
-
setUploadState(uploadState.BEFORE);
|
182
|
-
return 0;
|
183
|
-
}
|
184
|
-
return prev - 1;
|
185
|
-
});
|
186
|
-
};
|
187
|
-
|
188
|
-
const clickRemoveImage = (i: number) => {
|
189
|
-
const sources = [...images];
|
190
|
-
sources[i].remove = !sources[i].remove;
|
191
|
-
setImages(sources);
|
192
|
-
};
|
193
|
-
|
194
|
-
const clickRemoveFile = () => {
|
195
|
-
setFiles([]);
|
196
|
-
if (imageInputRef.current) {
|
197
|
-
imageInputRef.current.value = "";
|
198
|
-
}
|
199
|
-
props.onClear?.();
|
200
|
-
};
|
201
|
-
|
202
|
-
const checkValid = (filesInner: FileWithPreview[]): string | null => {
|
203
|
-
if (!filesInner) return null;
|
204
|
-
if (filesInner.length >= 10) return "you can't send more than 10 images";
|
205
|
-
for (let i = 0; i < filesInner.length; i++) {
|
206
|
-
const file = filesInner[i].file;
|
207
|
-
const split = file.name.split(".");
|
208
|
-
if (!["png", "jpg", "jpeg"].includes(split[split.length - 1])) {
|
209
|
-
return `Extension of the file can only be "png", "jpg" or "jpeg" `;
|
210
|
-
}
|
211
|
-
if (file) {
|
212
|
-
if (file.size > 1048576) {
|
213
|
-
return `Size of "${file.name}" can't be bigger than 1mb`;
|
214
|
-
}
|
215
|
-
}
|
216
|
-
}
|
217
|
-
return null;
|
218
|
-
};
|
219
|
-
|
220
|
-
const onFileChange = (filesInner: FileWithPreview[]): string | null => {
|
221
|
-
const check = checkValid(filesInner);
|
222
|
-
if (!check) {
|
223
|
-
setFiles(filesInner);
|
224
|
-
}
|
225
|
-
props.onError?.(check);
|
226
|
-
return check;
|
227
|
-
};
|
228
|
-
|
229
|
-
const renderImages = () => {
|
230
|
-
const imageElements = [];
|
231
|
-
if (files) {
|
232
|
-
console.log("---->", files);
|
233
|
-
for (let i = 0; i < files.length; i++) {
|
234
|
-
let imageClassName = "image";
|
235
|
-
imageElements.push(
|
236
|
-
<div key={i} className="image-container">
|
237
|
-
<div className={imageClassName}>
|
238
|
-
<ThumbnailImage name={files[i].file.name} src={files[i].image} size={files[i].file.size} />
|
239
|
-
</div>
|
240
|
-
</div>
|
241
|
-
);
|
242
|
-
}
|
243
|
-
}
|
244
|
-
return imageElements;
|
245
|
-
};
|
246
|
-
|
247
|
-
return (
|
248
|
-
<div ref={dropzoneElement} className={"multi-image form-element dropzone " + currentUploadState}>
|
249
|
-
<input ref={imageInputRef} type="file" style={{ display: "none" }} className="target" name={"file"} />
|
250
|
-
<div className="container">
|
251
|
-
<button className="trash" onClick={clickRemoveFile} type="button">
|
252
|
-
Delete All
|
253
|
-
</button>
|
254
|
-
{renderImages()}
|
255
|
-
<div>
|
256
|
-
<button
|
257
|
-
type={"button"}
|
258
|
-
onClick={() => {
|
259
|
-
const fileInput = document.getElementById("file__") as HTMLInputElement;
|
260
|
-
if (fileInput) {
|
261
|
-
fileInput.click();
|
262
|
-
}
|
263
|
-
}}
|
264
|
-
className="plus">
|
265
|
-
<span>
|
266
|
-
+ Add Image
|
267
|
-
<p>Drag image here or Select Image</p>
|
268
|
-
</span>
|
269
|
-
</button>
|
270
|
-
</div>
|
271
|
-
<input
|
272
|
-
hidden
|
273
|
-
id={"file__"}
|
274
|
-
multiple
|
275
|
-
type={"file"}
|
276
|
-
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
277
|
-
const selectedFiles = event.target.files;
|
278
|
-
if (!selectedFiles) return;
|
279
|
-
|
280
|
-
setFiles([]);
|
281
|
-
setUploadState(uploadState.AFTER);
|
282
|
-
for (let i = 0; i < selectedFiles.length; i++) {
|
283
|
-
const reader = new FileReader();
|
284
|
-
reader.onload = (eventInner) => {
|
285
|
-
if (!eventInner.target) return;
|
286
|
-
onFileChange([
|
287
|
-
...files,
|
288
|
-
{ file: selectedFiles[i], image: eventInner.target.result as string },
|
289
|
-
]);
|
290
|
-
};
|
291
|
-
reader.readAsDataURL(selectedFiles[i]);
|
292
|
-
}
|
293
|
-
if (imageInputRef.current) {
|
294
|
-
imageInputRef.current.files = selectedFiles;
|
295
|
-
}
|
296
|
-
}}
|
297
|
-
/>
|
298
|
-
</div>
|
299
|
-
</div>
|
300
|
-
);
|
301
|
-
}
|
@@ -1,74 +0,0 @@
|
|
1
|
-
import React, { useEffect, useState } from 'react';
|
2
|
-
import { FormProvider, useForm } from 'react-hook-form';
|
3
|
-
import { InputOptions } from '../../decorators/form/Input';
|
4
|
-
import { FormField } from './FormField';
|
5
|
-
import { FormOptions } from '../../decorators/form/FormOptions';
|
6
|
-
import { AnyClass } from '../../types/AnyClass';
|
7
|
-
import { OnSubmitFN, GetDetailsDataFN } from '../pages/FormPage';
|
8
|
-
import { useParams, useNavigate } from 'react-router';
|
9
|
-
|
10
|
-
interface InnerFormProps<T extends AnyClass> {
|
11
|
-
formOptions: FormOptions;
|
12
|
-
onSubmit: OnSubmitFN<T>;
|
13
|
-
getDetailsData?: GetDetailsDataFN<T>;
|
14
|
-
redirectBackOnSuccess?: boolean;
|
15
|
-
}
|
16
|
-
|
17
|
-
export function InnerForm<T extends AnyClass>({
|
18
|
-
formOptions,
|
19
|
-
onSubmit,
|
20
|
-
getDetailsData,
|
21
|
-
redirectBackOnSuccess,
|
22
|
-
}: InnerFormProps<T>) {
|
23
|
-
const params = useParams();
|
24
|
-
const form = useForm<T>({
|
25
|
-
resolver: formOptions.resolver,
|
26
|
-
});
|
27
|
-
const navigate = useNavigate();
|
28
|
-
const inputs = formOptions.inputs;
|
29
|
-
useEffect(() => {
|
30
|
-
if (getDetailsData) {
|
31
|
-
getDetailsData(params as Record<string, string>).then(data => {
|
32
|
-
form.reset({ ...data });
|
33
|
-
});
|
34
|
-
}
|
35
|
-
}, [params, form.reset]);
|
36
|
-
|
37
|
-
return (
|
38
|
-
<div className="form-wrapper">
|
39
|
-
<FormProvider {...form}>
|
40
|
-
<form
|
41
|
-
onSubmit={form.handleSubmit(
|
42
|
-
async dataForm => {
|
43
|
-
await onSubmit(dataForm);
|
44
|
-
if (redirectBackOnSuccess) {
|
45
|
-
navigate(-1);
|
46
|
-
}
|
47
|
-
},
|
48
|
-
(errors, event) => {
|
49
|
-
console.log('error creating creation', errors, event);
|
50
|
-
}
|
51
|
-
)}
|
52
|
-
>
|
53
|
-
<div>
|
54
|
-
{inputs?.map((input: InputOptions) => (
|
55
|
-
<FormField
|
56
|
-
key={input.name || ''}
|
57
|
-
input={input}
|
58
|
-
register={form.register}
|
59
|
-
error={
|
60
|
-
input.name
|
61
|
-
? { message: (form.formState.errors[input.name as keyof T] as any)?.message }
|
62
|
-
: undefined
|
63
|
-
}
|
64
|
-
/>
|
65
|
-
))}
|
66
|
-
<button type="submit" className="submit-button">
|
67
|
-
Submit
|
68
|
-
</button>
|
69
|
-
</div>
|
70
|
-
</form>
|
71
|
-
</FormProvider>
|
72
|
-
</div>
|
73
|
-
);
|
74
|
-
}
|
@@ -1,15 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
|
3
|
-
interface LabelProps {
|
4
|
-
htmlFor: string;
|
5
|
-
label?: string;
|
6
|
-
fieldName: string;
|
7
|
-
}
|
8
|
-
|
9
|
-
export function Label({ htmlFor, label, fieldName }: LabelProps) {
|
10
|
-
return (
|
11
|
-
<label htmlFor={htmlFor}>
|
12
|
-
{label ?? fieldName.charAt(0).toUpperCase() + fieldName.slice(1)}
|
13
|
-
</label>
|
14
|
-
);
|
15
|
-
}
|
@@ -1,8 +0,0 @@
|
|
1
|
-
export { InnerForm } from './InnerForm';
|
2
|
-
export { FormField } from './FormField';
|
3
|
-
export { LoadingScreen } from './LoadingScreen';
|
4
|
-
export { Counter } from './Counter';
|
5
|
-
export { ImageUploader } from './ImageUploader';
|
6
|
-
export { ErrorComponent } from './ErrorComponent';
|
7
|
-
export { Label } from './Label';
|
8
|
-
export { ErrorBoundary } from './ErrorBoundary';
|
@@ -1,127 +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
|
-
import { ListData } from '../../../decorators/list/ListData';
|
7
|
-
import { EmptyList } from './EmptyList';
|
8
|
-
import SearchIcon from '../../../assets/icons/svg/search.svg';
|
9
|
-
import PencilIcon from '../../../assets/icons/svg/pencil.svg';
|
10
|
-
import TrashIcon from '../../../assets/icons/svg/trash.svg';
|
11
|
-
|
12
|
-
interface DatagridProps<T> {
|
13
|
-
data: T[];
|
14
|
-
listData: ListData<T>;
|
15
|
-
onRemoveItem?: (item: T) => Promise<void>;
|
16
|
-
}
|
17
|
-
|
18
|
-
export function Datagrid<T>({ data, listData, onRemoveItem }: DatagridProps<T>) {
|
19
|
-
const cells = listData.cells;
|
20
|
-
const listGeneralCells =
|
21
|
-
typeof listData.list?.cells === 'function'
|
22
|
-
? listData.list?.cells?.(data[0])
|
23
|
-
: listData.list?.cells;
|
24
|
-
|
25
|
-
return (
|
26
|
-
<div className="datagrid">
|
27
|
-
{!data || data.length === 0 ? (
|
28
|
-
<EmptyList />
|
29
|
-
) : (
|
30
|
-
<table className="datagrid-table">
|
31
|
-
<thead>
|
32
|
-
<tr>
|
33
|
-
{cells.map(cellOptions => (
|
34
|
-
<th key={cellOptions.name}>{cellOptions.title ?? cellOptions.name}</th>
|
35
|
-
))}
|
36
|
-
{listGeneralCells?.details && <th>Details</th>}
|
37
|
-
{listGeneralCells?.edit && <th>Edit</th>}
|
38
|
-
{listGeneralCells?.delete && <th>Delete</th>}
|
39
|
-
</tr>
|
40
|
-
</thead>
|
41
|
-
<tbody>
|
42
|
-
{data.map((item, index) => (
|
43
|
-
<tr key={index}>
|
44
|
-
{cells.map(cellOptions => {
|
45
|
-
// @ts-ignore
|
46
|
-
const value = item[cellOptions.name];
|
47
|
-
let render = value ?? '-'; // Default value if the field is undefined or null
|
48
|
-
|
49
|
-
switch (cellOptions.type) {
|
50
|
-
case 'date':
|
51
|
-
if (value) {
|
52
|
-
const date = new Date(value);
|
53
|
-
render = `${date.getDate().toString().padStart(2, '0')}/${(
|
54
|
-
date.getMonth() + 1
|
55
|
-
)
|
56
|
-
.toString()
|
57
|
-
.padStart(
|
58
|
-
2,
|
59
|
-
'0'
|
60
|
-
)}/${date.getFullYear()} ${date.getHours().toString().padStart(2, '0')}:${date
|
61
|
-
.getMinutes()
|
62
|
-
.toString()
|
63
|
-
.padStart(2, '0')}`;
|
64
|
-
}
|
65
|
-
break;
|
66
|
-
|
67
|
-
case 'image': {
|
68
|
-
const imageCellOptions = cellOptions as ImageCellOptions;
|
69
|
-
render = (
|
70
|
-
<img
|
71
|
-
width={100}
|
72
|
-
height={100}
|
73
|
-
src={imageCellOptions.baseUrl + value}
|
74
|
-
style={{ objectFit: 'contain' }}
|
75
|
-
/>
|
76
|
-
);
|
77
|
-
break;
|
78
|
-
}
|
79
|
-
case 'string':
|
80
|
-
default:
|
81
|
-
render = value ? value.toString() : (cellOptions?.placeHolder ?? '-'); // Handles string type or default fallback
|
82
|
-
break;
|
83
|
-
}
|
84
|
-
/*
|
85
|
-
if (cellOptions.linkTo) {
|
86
|
-
render = <Link to={cellOptions.linkTo(item)}>{formattedValue}</Link>;
|
87
|
-
}
|
88
|
-
*/
|
89
|
-
return <td key={cellOptions.name}>{render}</td>;
|
90
|
-
})}
|
91
|
-
{listGeneralCells?.details && (
|
92
|
-
<td>
|
93
|
-
<Link to={listGeneralCells.details.path} className="util-cell-link">
|
94
|
-
<SearchIcon className="icon icon-search" />
|
95
|
-
<span className="util-cell-label">{listGeneralCells.details.label}</span>
|
96
|
-
</Link>
|
97
|
-
</td>
|
98
|
-
)}
|
99
|
-
{listGeneralCells?.edit && (
|
100
|
-
<td>
|
101
|
-
<Link to={listGeneralCells.edit.path} className="util-cell-link">
|
102
|
-
<PencilIcon className="icon icon-pencil" />
|
103
|
-
<span className="util-cell-label">{listGeneralCells.edit.label}</span>
|
104
|
-
</Link>
|
105
|
-
</td>
|
106
|
-
)}
|
107
|
-
{listGeneralCells?.delete && (
|
108
|
-
<td>
|
109
|
-
<a
|
110
|
-
onClick={() => {
|
111
|
-
onRemoveItem?.(item);
|
112
|
-
}}
|
113
|
-
className="util-cell-link"
|
114
|
-
>
|
115
|
-
<TrashIcon className="icon icon-trash" />
|
116
|
-
<span className="util-cell-label">{listGeneralCells.delete.label}</span>
|
117
|
-
</a>
|
118
|
-
</td>
|
119
|
-
)}
|
120
|
-
</tr>
|
121
|
-
))}
|
122
|
-
</tbody>
|
123
|
-
</table>
|
124
|
-
)}
|
125
|
-
</div>
|
126
|
-
);
|
127
|
-
}
|
@@ -1,37 +0,0 @@
|
|
1
|
-
import { useParams } from 'react-router';
|
2
|
-
import React, { useEffect, useState } from 'react';
|
3
|
-
import { Screen } from '../../types/Screen';
|
4
|
-
import { ErrorComponent } from '../components/ErrorComponent';
|
5
|
-
|
6
|
-
export function ControllerDetails({ screen }: { screen: Screen }) {
|
7
|
-
const { id } = useParams();
|
8
|
-
const [data, setData] = useState<any>(null);
|
9
|
-
const [error, setError] = useState(null);
|
10
|
-
|
11
|
-
useEffect(() => {
|
12
|
-
if (screen.controller && id) {
|
13
|
-
/*
|
14
|
-
CrudApi.details({ ...fetchSettings, token }, screen.controller, id)
|
15
|
-
.then(res => {
|
16
|
-
setData(res);
|
17
|
-
})
|
18
|
-
.catch((e: any) => {
|
19
|
-
setError(e);
|
20
|
-
console.error(e);
|
21
|
-
});
|
22
|
-
*/
|
23
|
-
}
|
24
|
-
}, [id, screen]);
|
25
|
-
|
26
|
-
if (error) {
|
27
|
-
return <ErrorComponent error={error} />;
|
28
|
-
}
|
29
|
-
|
30
|
-
return (
|
31
|
-
<p
|
32
|
-
dangerouslySetInnerHTML={{
|
33
|
-
__html: JSON.stringify(data, null, ' ' + '<br/>'),
|
34
|
-
}}
|
35
|
-
/>
|
36
|
-
);
|
37
|
-
}
|
@@ -1,34 +0,0 @@
|
|
1
|
-
import React, { useMemo } from 'react';
|
2
|
-
import { InnerForm } from '../components';
|
3
|
-
import { AnyClass } from '../../types/AnyClass';
|
4
|
-
import { getFormFields } from '../../decorators/form/getFormFields';
|
5
|
-
|
6
|
-
export type GetDetailsDataFN<T> = (param: Record<string, string>) => Promise<T>;
|
7
|
-
export type OnSubmitFN<T> = (data: T) => Promise<T>;
|
8
|
-
|
9
|
-
export interface FormPageProps<T extends AnyClass> {
|
10
|
-
model: T;
|
11
|
-
getDetailsData?: GetDetailsDataFN<T>;
|
12
|
-
redirect?: string;
|
13
|
-
onSubmit: OnSubmitFN<T>;
|
14
|
-
redirectBackOnSuccess?: boolean;
|
15
|
-
}
|
16
|
-
|
17
|
-
export function FormPage<T extends AnyClass>({
|
18
|
-
model,
|
19
|
-
getDetailsData,
|
20
|
-
onSubmit,
|
21
|
-
redirect,
|
22
|
-
redirectBackOnSuccess = true,
|
23
|
-
...rest
|
24
|
-
}: FormPageProps<T>) {
|
25
|
-
const formOptions = useMemo(() => getFormFields(model), [model]);
|
26
|
-
return (
|
27
|
-
<InnerForm
|
28
|
-
getDetailsData={getDetailsData}
|
29
|
-
onSubmit={onSubmit}
|
30
|
-
formOptions={formOptions}
|
31
|
-
redirectBackOnSuccess={redirectBackOnSuccess}
|
32
|
-
/>
|
33
|
-
);
|
34
|
-
}
|
package/src/decorators/Crud.ts
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
import 'reflect-metadata';
|
2
|
-
|
3
|
-
const CRUD_KEY = 'Crud'; // Changed from Symbol to string
|
4
|
-
|
5
|
-
export interface CrudOptions {
|
6
|
-
controller: string;
|
7
|
-
}
|
8
|
-
|
9
|
-
export function Crud(options?: CrudOptions): ClassDecorator {
|
10
|
-
return (target: Function) => {
|
11
|
-
if (options) {
|
12
|
-
Reflect.defineMetadata(CRUD_KEY, options, target);
|
13
|
-
}
|
14
|
-
};
|
15
|
-
}
|
16
|
-
|
17
|
-
|
18
|
-
export function getClassCrudData(entityClass: any): CrudOptions | undefined {
|
19
|
-
return Reflect.getMetadata(CRUD_KEY, entityClass);
|
20
|
-
}
|
@@ -1,13 +0,0 @@
|
|
1
|
-
import { AnyClass } from "../../types/AnyClass";
|
2
|
-
import { FormOptions } from "./FormOptions";
|
3
|
-
import { getInputFields } from "./Input";
|
4
|
-
import { getFormConfiguration } from "./Form";
|
5
|
-
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
|
6
|
-
|
7
|
-
export function getFormFields<T extends AnyClass>(entityClass: T): FormOptions {
|
8
|
-
return {
|
9
|
-
resolver: classValidatorResolver(entityClass as any),
|
10
|
-
form: getFormConfiguration(entityClass),
|
11
|
-
inputs: getInputFields<T>(entityClass),
|
12
|
-
};
|
13
|
-
}
|
@@ -1,13 +0,0 @@
|
|
1
|
-
import { CELL_KEY, CellOptions } from "./Cell";
|
2
|
-
|
3
|
-
export function getCellFields(entityClass: any): CellOptions[] {
|
4
|
-
const prototype = entityClass.prototype;
|
5
|
-
const cellFields: string[] = Reflect.getMetadata(CELL_KEY, prototype) || [];
|
6
|
-
return cellFields.map((field) => {
|
7
|
-
const fields = Reflect.getMetadata(`${CELL_KEY.toString()}:${field}:options`, prototype) || {};
|
8
|
-
return {
|
9
|
-
...fields,
|
10
|
-
name: fields?.name ?? field,
|
11
|
-
};
|
12
|
-
});
|
13
|
-
}
|
@@ -1,13 +0,0 @@
|
|
1
|
-
import "reflect-metadata";
|
2
|
-
import { Cell, CellOptions } from "./Cell";
|
3
|
-
|
4
|
-
export interface ImageCellOptions extends CellOptions {
|
5
|
-
baseUrl: string;
|
6
|
-
}
|
7
|
-
|
8
|
-
export function ImageCell(options?: ImageCellOptions): PropertyDecorator {
|
9
|
-
return Cell({
|
10
|
-
...options,
|
11
|
-
type: "image",
|
12
|
-
});
|
13
|
-
}
|
@@ -1,10 +0,0 @@
|
|
1
|
-
import { getClassListData } from './List';
|
2
|
-
import { getCellFields } from './GetCellFields';
|
3
|
-
import { ListData } from './ListData';
|
4
|
-
|
5
|
-
export function getListFields<T>(entityClass: T): ListData<T> {
|
6
|
-
return {
|
7
|
-
list: getClassListData(entityClass),
|
8
|
-
cells: getCellFields(entityClass),
|
9
|
-
};
|
10
|
-
}
|