@addsign/moje-agenda-shared-lib 2.0.72 → 2.0.73
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/Dialog-BmQoVu5C.js.map +1 -1
- package/dist/assets/style.css +1772 -1758
- package/dist/components/Attachments.js +2 -2
- package/dist/components/datatable/DataTable.js +3 -3
- package/dist/components/datatable/DataTableServer.js +3 -3
- package/dist/components/form/AutocompleteSearchBar.js +2 -2
- package/dist/components/form/AutocompleteSearchBarServer.js +2 -2
- package/dist/components/form/FileInput.js +3 -3
- package/dist/components/form/FileInputForm.d.ts +1 -0
- package/dist/components/form/FileInputForm.js +201 -93
- package/dist/components/form/FileInputForm.js.map +1 -1
- package/dist/components/form/FileInputFormMultiple.d.ts +1 -0
- package/dist/components/form/FileInputFormMultiple.js +203 -82
- package/dist/components/form/FileInputFormMultiple.js.map +1 -1
- package/dist/components/form/FileInputMultiple.js +3 -3
- package/dist/components/form/FormField.js +2 -2
- package/dist/components/form/PositionsSelectorSingle.js +3 -3
- package/dist/components/form/SelectField.js +2 -2
- package/dist/components/layout/Neoptimizovano.js +2 -2
- package/dist/components/profiles/ProfileOverview.js +2 -2
- package/dist/components/ui/Combobox.js +1 -1
- package/dist/components/ui/DatePicker.js +2 -2
- package/dist/components/ui/DateTimePicker.js +2 -2
- package/dist/components/ui/Dialog.js +1 -1
- package/dist/components/ui/ScrollArea.js +2 -2
- package/dist/components/ui/checkbox.js +4 -4
- package/dist/components/ui/command.d.ts +6 -6
- package/dist/components/ui/command.js +2 -2
- package/dist/components/ui/input.js +8 -107
- package/dist/components/ui/input.js.map +1 -1
- package/dist/components/ui/multi-select.js +1 -1
- package/dist/components/ui/popover.js +1 -1
- package/dist/components/ui/radioGroup.js +5 -5
- package/dist/components/ui/select.js +7 -7
- package/dist/components/ui/toast.js +5 -5
- package/dist/components/ui/tooltip.js +6 -6
- package/dist/input-Cm_FjJOF.js +111 -0
- package/dist/input-Cm_FjJOF.js.map +1 -0
- package/dist/main.js +3 -3
- package/dist/popover-DpJhfyvx.js.map +1 -1
- package/lib/components/form/FileInputForm.tsx +245 -99
- package/lib/components/form/FileInputFormMultiple.tsx +233 -65
- package/lib/css/tailwind.css +9 -9
- package/package.json +1 -1
- package/tailwind.config.js +97 -97
|
@@ -2,9 +2,19 @@ import React, { useState, useEffect, useCallback } from "react";
|
|
|
2
2
|
import { useDropzone } from "react-dropzone";
|
|
3
3
|
import { MdDeleteOutline, MdInsertDriveFile } from "react-icons/md";
|
|
4
4
|
import { AxiosError } from "axios";
|
|
5
|
+
import { LoaderCircleIcon } from "lucide-react";
|
|
5
6
|
import { cn } from "../../utils/utils";
|
|
6
7
|
import { IAttachment } from "../../types";
|
|
7
8
|
import { handleErrors, useFederationContext, useFormField } from "../../main";
|
|
9
|
+
import {
|
|
10
|
+
Dialog,
|
|
11
|
+
DialogContent,
|
|
12
|
+
DialogHeader,
|
|
13
|
+
DialogTitle,
|
|
14
|
+
DialogFooter,
|
|
15
|
+
} from "../ui/Dialog";
|
|
16
|
+
import { Input } from "../ui/input";
|
|
17
|
+
import { Button } from "../ui/button";
|
|
8
18
|
|
|
9
19
|
export interface FileInputFormProps {
|
|
10
20
|
name: string;
|
|
@@ -14,6 +24,7 @@ export interface FileInputFormProps {
|
|
|
14
24
|
disabled?: boolean;
|
|
15
25
|
attachmentName?: string;
|
|
16
26
|
attachmentType?: string;
|
|
27
|
+
askForAttachmentName?: boolean;
|
|
17
28
|
}
|
|
18
29
|
|
|
19
30
|
const MAX_FILE_SIZE = 1024 * 1024; // 1MB
|
|
@@ -26,9 +37,14 @@ const FileInputForm: React.FC<FileInputFormProps> = ({
|
|
|
26
37
|
disabled,
|
|
27
38
|
attachmentName,
|
|
28
39
|
attachmentType,
|
|
40
|
+
askForAttachmentName = false,
|
|
29
41
|
}) => {
|
|
30
42
|
const [fileData, setFileData] = useState<IAttachment | null>(value || null);
|
|
31
43
|
const [isFocused, setIsFocused] = useState(false);
|
|
44
|
+
const [pendingFile, setPendingFile] = useState<File | null>(null);
|
|
45
|
+
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
|
46
|
+
const [fileName, setFileName] = useState<string>("");
|
|
47
|
+
const [isUploading, setIsUploading] = useState(false);
|
|
32
48
|
|
|
33
49
|
const federationContext = useFederationContext();
|
|
34
50
|
const { error } = useFormField();
|
|
@@ -36,6 +52,67 @@ const FileInputForm: React.FC<FileInputFormProps> = ({
|
|
|
36
52
|
useEffect(() => {
|
|
37
53
|
setFileData(value || null);
|
|
38
54
|
}, [value]);
|
|
55
|
+
const uploadFile = useCallback(
|
|
56
|
+
async (file: File, customName?: string) => {
|
|
57
|
+
if (file.size > MAX_FILE_SIZE) {
|
|
58
|
+
// Handle the case when the file size is exceeded
|
|
59
|
+
federationContext.emitter.emit("message", {
|
|
60
|
+
title: "Velikost souboru byla překročena",
|
|
61
|
+
message: `Maximální povolená velikost je ${MAX_FILE_SIZE / (1024 * 1024)} MB.`,
|
|
62
|
+
classes: "bg-danger ",
|
|
63
|
+
timeout: 0,
|
|
64
|
+
type: "error",
|
|
65
|
+
});
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
setIsUploading(true);
|
|
70
|
+
const formData = new FormData();
|
|
71
|
+
|
|
72
|
+
// Add the JSON data object
|
|
73
|
+
const dataObject = {
|
|
74
|
+
...(customName
|
|
75
|
+
? { attachmentName: customName }
|
|
76
|
+
: attachmentName && { attachmentName }),
|
|
77
|
+
...(attachmentType && { attachmentType }),
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
formData.append(
|
|
81
|
+
"data",
|
|
82
|
+
new Blob([JSON.stringify(dataObject)], { type: "application/json" })
|
|
83
|
+
);
|
|
84
|
+
formData.append("file", file);
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const response = await federationContext.apiClient.post<IAttachment>(
|
|
88
|
+
"/files/upload",
|
|
89
|
+
formData,
|
|
90
|
+
{
|
|
91
|
+
headers: {
|
|
92
|
+
"Content-Type": "multipart/form-data",
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
const uploadedFile = response.data;
|
|
97
|
+
setFileData(uploadedFile);
|
|
98
|
+
onFileChanged(uploadedFile);
|
|
99
|
+
setIsFocused(false);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
handleErrors(error as AxiosError, federationContext.emitter);
|
|
102
|
+
console.error("There was an error!", error);
|
|
103
|
+
} finally {
|
|
104
|
+
setIsUploading(false);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
[
|
|
108
|
+
federationContext.apiClient,
|
|
109
|
+
federationContext.emitter,
|
|
110
|
+
onFileChanged,
|
|
111
|
+
attachmentName,
|
|
112
|
+
attachmentType,
|
|
113
|
+
]
|
|
114
|
+
);
|
|
115
|
+
|
|
39
116
|
const onDrop = useCallback(
|
|
40
117
|
async (acceptedFiles: File[]) => {
|
|
41
118
|
if (acceptedFiles.length > 0) {
|
|
@@ -53,52 +130,44 @@ const FileInputForm: React.FC<FileInputFormProps> = ({
|
|
|
53
130
|
return;
|
|
54
131
|
}
|
|
55
132
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
formData.append(
|
|
65
|
-
"data",
|
|
66
|
-
new Blob([JSON.stringify(dataObject)], { type: "application/json" })
|
|
67
|
-
);
|
|
68
|
-
formData.append("file", file);
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
const response = await federationContext.apiClient.post<IAttachment>(
|
|
72
|
-
"/files/upload",
|
|
73
|
-
formData,
|
|
74
|
-
{
|
|
75
|
-
headers: {
|
|
76
|
-
"Content-Type": "multipart/form-data",
|
|
77
|
-
},
|
|
78
|
-
}
|
|
79
|
-
);
|
|
80
|
-
const uploadedFile = response.data;
|
|
81
|
-
setFileData(uploadedFile);
|
|
82
|
-
onFileChanged(uploadedFile);
|
|
83
|
-
setIsFocused(false);
|
|
84
|
-
} catch (error) {
|
|
85
|
-
handleErrors(error as AxiosError, federationContext.emitter);
|
|
86
|
-
console.error("There was an error!", error);
|
|
133
|
+
if (askForAttachmentName) {
|
|
134
|
+
setPendingFile(file);
|
|
135
|
+
setFileName(file.name);
|
|
136
|
+
setIsDialogOpen(true);
|
|
137
|
+
} else {
|
|
138
|
+
await uploadFile(file);
|
|
87
139
|
}
|
|
88
140
|
}
|
|
89
141
|
},
|
|
90
|
-
[
|
|
91
|
-
federationContext.apiClient,
|
|
92
|
-
federationContext.emitter,
|
|
93
|
-
onFileChanged,
|
|
94
|
-
attachmentName,
|
|
95
|
-
attachmentType,
|
|
96
|
-
]
|
|
142
|
+
[askForAttachmentName, uploadFile, federationContext]
|
|
97
143
|
);
|
|
98
144
|
|
|
145
|
+
const handleDialogSubmit = async () => {
|
|
146
|
+
if (pendingFile) {
|
|
147
|
+
setIsDialogOpen(false);
|
|
148
|
+
await uploadFile(pendingFile, fileName);
|
|
149
|
+
setPendingFile(null);
|
|
150
|
+
setFileName("");
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const handleDialogCancel = () => {
|
|
155
|
+
setIsDialogOpen(false);
|
|
156
|
+
setPendingFile(null);
|
|
157
|
+
setFileName("");
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const formatFileSize = (bytes: number): string => {
|
|
161
|
+
if (bytes === 0) return "0 Bytes";
|
|
162
|
+
const k = 1024;
|
|
163
|
+
const sizes = ["Bytes", "KB", "MB", "GB"];
|
|
164
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
165
|
+
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
|
|
166
|
+
};
|
|
167
|
+
|
|
99
168
|
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
|
100
169
|
onDrop,
|
|
101
|
-
disabled,
|
|
170
|
+
disabled: disabled || isUploading,
|
|
102
171
|
multiple: false,
|
|
103
172
|
});
|
|
104
173
|
|
|
@@ -108,76 +177,153 @@ const FileInputForm: React.FC<FileInputFormProps> = ({
|
|
|
108
177
|
};
|
|
109
178
|
|
|
110
179
|
return (
|
|
111
|
-
|
|
112
|
-
<div className="
|
|
113
|
-
<div
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
180
|
+
<>
|
|
181
|
+
<div className="w-full min-h-30 flex-col justify-start items-start gap-1.5 inline-flex sharedLibrary">
|
|
182
|
+
<div className="self-stretch flex-col justify-start items-start gap-1.5 flex">
|
|
183
|
+
<div
|
|
184
|
+
className={cn(
|
|
185
|
+
`self-stretch px-2 py-2 rounded-lg justify-start items-center gap-2 inline-flex outline-none border relative`,
|
|
186
|
+
isFocused &&
|
|
117
187
|
!hasError &&
|
|
118
188
|
"outline-4 outline-indigo-200 outline-offset-0 border-indigo-300",
|
|
119
189
|
|
|
120
|
-
|
|
190
|
+
hasError &&
|
|
121
191
|
"outline-4 outline-red-200 outline-offset-0 border-none",
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
isDragActive
|
|
134
|
-
? "border-indigo-300 bg-indigo-50"
|
|
135
|
-
: "border-gray-300"
|
|
136
|
-
}`}
|
|
137
|
-
>
|
|
138
|
-
<input {...getInputProps()} id={name} />
|
|
139
|
-
<p className="text-gray-500">
|
|
140
|
-
{isDragActive
|
|
141
|
-
? "Sem přetáhněte soubor"
|
|
142
|
-
: "Klikněte pro nahrání, nebo nahrajte přetažením souboru"}
|
|
143
|
-
</p>
|
|
144
|
-
</div>
|
|
145
|
-
) : (
|
|
146
|
-
<div className="w-full flex items-center justify-between">
|
|
147
|
-
<div className=" flex">
|
|
148
|
-
<MdInsertDriveFile style={{ fontSize: "2rem" }} />
|
|
149
|
-
<a
|
|
150
|
-
href={`/api/files/download/${fileData.id}`}
|
|
151
|
-
className="pl-2 text-left underline text-primary"
|
|
152
|
-
target="_blank"
|
|
153
|
-
>
|
|
154
|
-
{fileData.filename}
|
|
155
|
-
</a>
|
|
192
|
+
!isFocused && hasError && "border-red-200 ",
|
|
193
|
+
disabled ? "bg-gray-100" : "bg-transparent"
|
|
194
|
+
)}
|
|
195
|
+
onFocus={() => setIsFocused(true)}
|
|
196
|
+
onBlur={() => setIsFocused(false)}
|
|
197
|
+
>
|
|
198
|
+
{isUploading && (
|
|
199
|
+
<div className="absolute inset-0 bg-white/80 backdrop-blur-sm rounded-lg z-10 flex items-center justify-center">
|
|
200
|
+
<div className="flex flex-col items-center gap-2">
|
|
201
|
+
<LoaderCircleIcon className="h-8 w-8 animate-spin text-primary" />
|
|
202
|
+
<p className="text-sm text-gray-600">Nahrávání...</p>
|
|
156
203
|
</div>
|
|
157
|
-
{!disabled && (
|
|
158
|
-
<div
|
|
159
|
-
onClick={handleRemove}
|
|
160
|
-
className="text-gray-600 cursor-pointer hover:text-primary hover:bg-gray-200 rounded-full ml-4"
|
|
161
|
-
>
|
|
162
|
-
<MdDeleteOutline
|
|
163
|
-
style={{ fontSize: "1.5rem", margin: "15px" }}
|
|
164
|
-
/>
|
|
165
|
-
</div>
|
|
166
|
-
)}
|
|
167
204
|
</div>
|
|
168
205
|
)}
|
|
206
|
+
<div className="flex relative grow shrink basis-0 min-h-5 lg:min-h-[32px] justify-start items-stretch gap-2 max-w-full ">
|
|
207
|
+
{!fileData ? (
|
|
208
|
+
<div
|
|
209
|
+
{...getRootProps()}
|
|
210
|
+
className={cn(
|
|
211
|
+
`w-full p-4 border-dashed border-2 rounded-lg text-center`,
|
|
212
|
+
isUploading
|
|
213
|
+
? "cursor-not-allowed opacity-50"
|
|
214
|
+
: "cursor-pointer hover:bg-gray-100",
|
|
215
|
+
isDragActive ? "border-indigo-300 bg-indigo-50" : "border-gray-300"
|
|
216
|
+
)}
|
|
217
|
+
>
|
|
218
|
+
<input {...getInputProps()} id={name} />
|
|
219
|
+
<p className="text-gray-500">
|
|
220
|
+
{isDragActive
|
|
221
|
+
? "Sem přetáhněte soubor"
|
|
222
|
+
: "Klikněte pro nahrání, nebo nahrajte přetažením souboru"}
|
|
223
|
+
</p>
|
|
224
|
+
</div>
|
|
225
|
+
) : (
|
|
226
|
+
<div className="w-full flex items-center justify-between py-2 border-b">
|
|
227
|
+
<div className="flex flex-col flex-1">
|
|
228
|
+
<div className="flex items-center gap-2">
|
|
229
|
+
<MdInsertDriveFile style={{ fontSize: "2rem" }} />
|
|
230
|
+
<div className="flex flex-col flex-1">
|
|
231
|
+
<a
|
|
232
|
+
href={`/api/files/download/${fileData.id}`}
|
|
233
|
+
className="text-left underline text-primary text-sm cursor-pointer"
|
|
234
|
+
target="_blank"
|
|
235
|
+
>
|
|
236
|
+
{fileData.filename}
|
|
237
|
+
</a>
|
|
238
|
+
{fileData.attachmentName && (
|
|
239
|
+
<div className="text-sm text-gray-600">
|
|
240
|
+
{fileData.attachmentName}
|
|
241
|
+
</div>
|
|
242
|
+
)}
|
|
243
|
+
</div>
|
|
244
|
+
</div>
|
|
245
|
+
</div>
|
|
246
|
+
{!disabled && !isUploading && (
|
|
247
|
+
<div
|
|
248
|
+
onClick={handleRemove}
|
|
249
|
+
className="text-gray-600 cursor-pointer hover:text-primary hover:bg-gray-200 rounded-full ml-4"
|
|
250
|
+
>
|
|
251
|
+
<MdDeleteOutline
|
|
252
|
+
style={{ fontSize: "1.5rem", margin: "15px" }}
|
|
253
|
+
/>
|
|
254
|
+
</div>
|
|
255
|
+
)}
|
|
256
|
+
</div>
|
|
257
|
+
)}
|
|
258
|
+
</div>
|
|
169
259
|
</div>
|
|
170
260
|
</div>
|
|
261
|
+
{description && (
|
|
262
|
+
<div
|
|
263
|
+
className="HintText self-stretch text-slate-600 text-sm font-normal leading-tight"
|
|
264
|
+
id={name + ":description"}
|
|
265
|
+
>
|
|
266
|
+
{description}
|
|
267
|
+
</div>
|
|
268
|
+
)}
|
|
171
269
|
</div>
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
{
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
270
|
+
|
|
271
|
+
<Dialog
|
|
272
|
+
open={isDialogOpen}
|
|
273
|
+
onOpenChange={(open) => {
|
|
274
|
+
setIsDialogOpen(open);
|
|
275
|
+
if (!open) {
|
|
276
|
+
handleDialogCancel();
|
|
277
|
+
}
|
|
278
|
+
}}
|
|
279
|
+
>
|
|
280
|
+
<DialogContent className="max-w-lg">
|
|
281
|
+
<DialogHeader>
|
|
282
|
+
<DialogTitle>Zadejte popis přílohy</DialogTitle>
|
|
283
|
+
</DialogHeader>
|
|
284
|
+
{pendingFile && (
|
|
285
|
+
<div className="space-y-4 py-4">
|
|
286
|
+
<div className="flex flex-col gap-2 p-4 border rounded-lg">
|
|
287
|
+
<div className="flex items-center gap-2">
|
|
288
|
+
<MdInsertDriveFile style={{ fontSize: "1.5rem" }} />
|
|
289
|
+
<div className="flex-1">
|
|
290
|
+
<div className="font-medium text-sm">{pendingFile.name}</div>
|
|
291
|
+
<div className="text-xs text-gray-500">
|
|
292
|
+
{formatFileSize(pendingFile.size)}
|
|
293
|
+
</div>
|
|
294
|
+
</div>
|
|
295
|
+
</div>
|
|
296
|
+
<div className="mt-2">
|
|
297
|
+
<label className="text-sm font-medium mb-1 block">
|
|
298
|
+
Popis přílohy:
|
|
299
|
+
</label>
|
|
300
|
+
<Input
|
|
301
|
+
value={fileName}
|
|
302
|
+
onChange={(e) => setFileName(e.target.value)}
|
|
303
|
+
placeholder="Zadejte popis přílohy"
|
|
304
|
+
/>
|
|
305
|
+
</div>
|
|
306
|
+
</div>
|
|
307
|
+
</div>
|
|
308
|
+
)}
|
|
309
|
+
<DialogFooter>
|
|
310
|
+
<Button variant="outline" onClick={handleDialogCancel} disabled={isUploading}>
|
|
311
|
+
Zrušit
|
|
312
|
+
</Button>
|
|
313
|
+
<Button onClick={handleDialogSubmit} disabled={isUploading}>
|
|
314
|
+
{isUploading ? (
|
|
315
|
+
<>
|
|
316
|
+
<LoaderCircleIcon className="h-4 w-4 animate-spin mr-2" />
|
|
317
|
+
Nahrávání...
|
|
318
|
+
</>
|
|
319
|
+
) : (
|
|
320
|
+
"Nahrát"
|
|
321
|
+
)}
|
|
322
|
+
</Button>
|
|
323
|
+
</DialogFooter>
|
|
324
|
+
</DialogContent>
|
|
325
|
+
</Dialog>
|
|
326
|
+
</>
|
|
181
327
|
);
|
|
182
328
|
};
|
|
183
329
|
|