@addsign/moje-agenda-shared-lib 1.0.61 → 2.0.0

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 (73) hide show
  1. package/dist/assets/style.css +4369 -0
  2. package/dist/assets/tailwind.css +1249 -1231
  3. package/dist/components/Button.js +1 -1
  4. package/dist/components/datatable/DataTable.js +1 -1
  5. package/dist/components/datatable/DataTableServer.js +1 -1
  6. package/dist/components/form/AutocompleteSearchBar.js +1 -1
  7. package/dist/components/form/AutocompleteSearchBarServer.js +1 -1
  8. package/dist/components/form/FileInput.js +1 -1
  9. package/dist/components/form/FileInputMultiple.js +1 -1
  10. package/dist/components/form/FormField.js +1 -1
  11. package/dist/components/form/PositionsSelectorSingle.js +1 -1
  12. package/dist/components/form/SelectField.js +1 -1
  13. package/dist/components/profiles/ProfileOverview.js +1 -1
  14. package/dist/main.js +1 -1
  15. package/dist/tailwind-l0sNRNKZ.js +2 -0
  16. package/dist/tailwind-l0sNRNKZ.js.map +1 -0
  17. package/lib/components/Button.tsx +57 -0
  18. package/lib/components/Calendar.tsx +242 -0
  19. package/lib/components/ConfirmationModalDialog.tsx +115 -0
  20. package/lib/components/Modal.tsx +73 -0
  21. package/lib/components/ModalDialog.tsx +63 -0
  22. package/lib/components/Spinner.tsx +25 -0
  23. package/lib/components/SpinnerIcon.tsx +12 -0
  24. package/lib/components/datatable/DataTable.tsx +442 -0
  25. package/lib/components/datatable/DataTableServer.tsx +939 -0
  26. package/lib/components/datatable/DatatableSettings.tsx +48 -0
  27. package/lib/components/datatable/Resizable.tsx +99 -0
  28. package/lib/components/datatable/types.ts +33 -0
  29. package/lib/components/form/AutocompleteSearchBar.tsx +424 -0
  30. package/lib/components/form/AutocompleteSearchBarServer.tsx +257 -0
  31. package/lib/components/form/DateField.tsx +124 -0
  32. package/lib/components/form/DateRangeField.tsx +116 -0
  33. package/lib/components/form/FileInput.tsx +188 -0
  34. package/lib/components/form/FileInputMultiple.tsx +186 -0
  35. package/lib/components/form/FormField.tsx +371 -0
  36. package/lib/components/form/InputField.tsx +230 -0
  37. package/lib/components/form/PositionsSelectorSingle.tsx +266 -0
  38. package/lib/components/form/RadioGroup.tsx +64 -0
  39. package/lib/components/form/SelectField.tsx +267 -0
  40. package/lib/components/layout/IconInCircle.tsx +29 -0
  41. package/lib/components/layout/PageTitle.tsx +19 -0
  42. package/lib/components/layout/SectionTitle.tsx +22 -0
  43. package/lib/components/profiles/ProfileOverview.tsx +212 -0
  44. package/lib/components/ui/Calendar.tsx +68 -0
  45. package/lib/components/ui/Combobox.tsx +122 -0
  46. package/lib/components/ui/DatePicker.tsx +124 -0
  47. package/lib/components/ui/DateTimePicker.tsx +187 -0
  48. package/lib/components/ui/Dialog.tsx +118 -0
  49. package/lib/components/ui/ScrollArea.tsx +45 -0
  50. package/lib/components/ui/button.tsx +56 -0
  51. package/lib/components/ui/command.tsx +153 -0
  52. package/lib/components/ui/form.tsx +177 -0
  53. package/lib/components/ui/input.tsx +22 -0
  54. package/lib/components/ui/label.tsx +24 -0
  55. package/lib/components/ui/popover.tsx +31 -0
  56. package/lib/components/ui/radioGroup.tsx +44 -0
  57. package/lib/components/ui/select.tsx +158 -0
  58. package/lib/contexts/FederationContext.tsx +28 -0
  59. package/lib/contexts/useFederationContext.ts +4 -0
  60. package/lib/css/tailwind.css +10 -0
  61. package/lib/fonts/arial.ts +3 -0
  62. package/lib/fonts/arialBold.ts +4 -0
  63. package/lib/main.ts +64 -0
  64. package/lib/types.ts +492 -0
  65. package/lib/utils/PdfManager.ts +224 -0
  66. package/lib/utils/getFullName.tsx +83 -0
  67. package/lib/utils/getIntersectingDays.ts +28 -0
  68. package/lib/utils/handleErrors.ts +28 -0
  69. package/lib/utils/hasRightInModule.ts +17 -0
  70. package/lib/utils/hasRole.ts +12 -0
  71. package/lib/utils/utils.ts +6 -0
  72. package/lib/vite-env.d.ts +1 -0
  73. package/package.json +3 -2
@@ -0,0 +1,257 @@
1
+ import { useState, useEffect, useRef } from "react";
2
+ import { useClickAway } from "react-use";
3
+ import { MdExpandLess, MdExpandMore } from "react-icons/md";
4
+ import { InputField, IOptionItem, useFederationContext } from "../../main";
5
+
6
+ interface OptionListProps {
7
+ options: IOptionItem[];
8
+ selectedOptionIndex: number;
9
+ handleOptionClick: (option: IOptionItem) => void;
10
+ label?: string;
11
+ }
12
+
13
+ const AutocompleteSearchBarServer: React.FC<{
14
+ label: string;
15
+ name: string;
16
+ required?: boolean;
17
+ disabled?: boolean;
18
+ description?: string;
19
+ fetchUrl?: string;
20
+ valueKey: string;
21
+ labelKey: string | ((item: any) => string);
22
+ onChange: (value: any) => void;
23
+ onFocus: () => void;
24
+ onBlur: () => void;
25
+ placeholder?: string;
26
+ value?: string;
27
+ initOptions?: IOptionItem[];
28
+ clearable?: boolean;
29
+ returnObject: boolean;
30
+ }> = ({
31
+ label,
32
+ name,
33
+ disabled,
34
+ description,
35
+ fetchUrl,
36
+ valueKey,
37
+ labelKey,
38
+ onChange,
39
+ onFocus,
40
+ onBlur,
41
+ placeholder,
42
+ returnObject,
43
+ value,
44
+ required,
45
+ }) => {
46
+ const [query, setQuery] = useState("");
47
+ const [selectedOptionIndex, setSelectedOptionIndex] = useState(-1);
48
+ const [selectedOption, setSelectedOption] = useState<IOptionItem | null>(
49
+ null
50
+ );
51
+ const [searchResults, setSearchResults] = useState<IOptionItem[]>([]);
52
+ const inputRef = useRef<HTMLInputElement>(null);
53
+ const ref = useRef(null);
54
+ const federationContext = useFederationContext();
55
+ const [isFocused, setIsFocused] = useState(false);
56
+ const [isLoading, setIsLoading] = useState(false);
57
+ // const [errors] = useState<any>({});
58
+
59
+ useEffect(() => {
60
+ if (!value) {
61
+ setQuery("");
62
+ setSelectedOption(null);
63
+ }
64
+ }, [value]);
65
+
66
+ //fetch options
67
+ useEffect(() => {
68
+ const fetchOptions = async () => {
69
+ setIsLoading(true);
70
+ const { data } = await federationContext.apiClient.get(fetchUrl + query);
71
+ setIsLoading(false);
72
+ const tmpData = data.content || data;
73
+
74
+ setSearchResults(
75
+ tmpData.map((item: any) => ({
76
+ value: item[valueKey],
77
+ label: labelKey instanceof Function ? labelKey(item) : item[labelKey],
78
+ }))
79
+ );
80
+ };
81
+ setSearchResults([]);
82
+
83
+ if (query && valueKey && labelKey && fetchUrl && !selectedOption) {
84
+ fetchOptions();
85
+ }
86
+ }, [query, valueKey, labelKey, fetchUrl, federationContext.apiClient]);
87
+
88
+ useClickAway(ref, () => {
89
+ if (isFocused) {
90
+ setIsLoading(false);
91
+ setSearchResults([]);
92
+ setQuery("");
93
+ if (!selectedOption) {
94
+ onChange(null);
95
+ }
96
+ onBlur();
97
+ setIsFocused(false);
98
+ }
99
+ });
100
+
101
+ const handleQueryChange = (event: React.ChangeEvent<HTMLInputElement>) => {
102
+ if (event.target.value == selectedOption?.label) {
103
+ return;
104
+ } else if (event.target.value) {
105
+ setIsFocused(true);
106
+ setSearchResults([]);
107
+ setQuery(event.target.value);
108
+
109
+ setSelectedOption(null);
110
+ setSelectedOptionIndex(-1);
111
+ } else {
112
+ //clear handler
113
+ onChange(null);
114
+ setQuery("");
115
+ setSelectedOption(null);
116
+ setTimeout(() => {
117
+ setSelectedOptionIndex(-1);
118
+ setSearchResults([]);
119
+ }, 100);
120
+ setIsFocused(false);
121
+ }
122
+ };
123
+ const handleFocus = () => {
124
+ setIsFocused(true);
125
+ setSearchResults([]);
126
+ onFocus();
127
+ inputRef.current?.select(); // Add this line
128
+ };
129
+
130
+ const handleOptionClick = (option: IOptionItem) => {
131
+ setSelectedOption(option);
132
+ setSearchResults([]);
133
+ setSelectedOptionIndex(-1);
134
+ setQuery("");
135
+ onChange(returnObject ? option : option.value);
136
+ setIsFocused(false);
137
+ };
138
+ const handleBlur = () => {
139
+ setSearchResults([]);
140
+ setTimeout(() => {
141
+ if (!selectedOption) {
142
+ setQuery("");
143
+ onChange(null);
144
+ }
145
+ }, 1);
146
+
147
+ onBlur();
148
+ setIsFocused(false);
149
+ };
150
+
151
+ return (
152
+ <div
153
+ className="w-full flex-col justify-start items-start gap-1.5 inline-flex relative"
154
+ ref={ref}
155
+ >
156
+ <InputField
157
+ manualRef={inputRef}
158
+ label={label}
159
+ name={String(name) + "_InputField"}
160
+ onInputChange={handleQueryChange}
161
+ required={required}
162
+ type="text"
163
+ value={selectedOption?.label || query}
164
+ disabled={disabled}
165
+ clearable
166
+ className=" min-w-[100px] px-0 "
167
+ rounded={true}
168
+ placeholder={placeholder}
169
+ debounceTimeout={1000}
170
+ description={description}
171
+ onFocus={handleFocus}
172
+ onBlur={handleBlur}
173
+ disableAutocomplete
174
+ >
175
+ {isFocused && (
176
+ <div className=" h-6 w-6 items-center flex justify-center hover:bg-gray-100 rounded-full text-lg cursor-pointer">
177
+ <MdExpandLess />
178
+ {false && !isFocused && <MdExpandMore />}
179
+ </div>
180
+ )}
181
+ </InputField>
182
+ {searchResults.length == 0 && isFocused && !query && (
183
+ <div
184
+ className={`absolute text-sm py-2 px-4 w-full outline-4 bg-white text-gray-400 border border-gray-200 drop-shadow-xl z-50 ${label ? "top-[68px]" : "top-[40px]"}`}
185
+ >
186
+ Začněte psát...
187
+ </div>
188
+ )}
189
+ {searchResults.length == 0 && isFocused && query && (
190
+ <div
191
+ className={`absolute text-sm py-2 px-4 w-full outline-4 bg-white border text-gray-400 border-gray-200 drop-shadow-xl z-50 ${label ? "top-[68px]" : "top-[40px]"}`}
192
+ >
193
+ {!isLoading && "Nic nenalezeno"}
194
+ </div>
195
+ )}
196
+ {searchResults.length > 0 && isFocused && query && (
197
+ <OptionList
198
+ options={searchResults}
199
+ selectedOptionIndex={selectedOptionIndex}
200
+ handleOptionClick={handleOptionClick}
201
+ label={label}
202
+ />
203
+ )}
204
+ </div>
205
+ );
206
+ };
207
+
208
+ const OptionList: React.FC<OptionListProps> = ({
209
+ options,
210
+ selectedOptionIndex,
211
+ handleOptionClick,
212
+ label,
213
+ }) => {
214
+ const containerRef = useRef<HTMLDivElement>(null);
215
+
216
+ useEffect(() => {
217
+ // Ensure the selected option is scrolled into view
218
+ if (selectedOptionIndex >= 0 && selectedOptionIndex < options.length) {
219
+ const selectedElement = containerRef.current?.children[
220
+ selectedOptionIndex
221
+ ] as HTMLDivElement;
222
+ selectedElement?.scrollIntoView({
223
+ behavior: "instant",
224
+ block: "nearest",
225
+ });
226
+ }
227
+ }, [selectedOptionIndex, options.length]);
228
+ const handleMouseDown = (event: any) => {
229
+ event.preventDefault();
230
+ };
231
+ return (
232
+ <div
233
+ ref={containerRef}
234
+ className={`overflow-y-auto resultOptionContainer w-full max-h-96 outline-indigo-200 absolute outline-4 bg-white border border-gray-200 drop-shadow-xl z-50 ${label ? "top-[68px]" : "top-[40px]"} *:
235
+
236
+ `}
237
+ >
238
+ {options?.map((option, index) => (
239
+ <div
240
+ key={option.value as string}
241
+ className={`py-2 px-4 flex items-center justify-between gap-8 hover:bg-gray-200 cursor-pointer text-sm ${
242
+ selectedOptionIndex === index ? "bg-gray-200" : ""
243
+ }`}
244
+ onClick={(e) => {
245
+ e.preventDefault();
246
+ handleOptionClick(option);
247
+ }}
248
+ onMouseDown={handleMouseDown}
249
+ >
250
+ <p className="font-normal">{option.label}</p>
251
+ </div>
252
+ ))}
253
+ </div>
254
+ );
255
+ };
256
+
257
+ export default AutocompleteSearchBarServer;
@@ -0,0 +1,124 @@
1
+ import Datepicker from "react-tailwindcss-datepicker";
2
+ import { ChangeEvent, useMemo } from "react";
3
+ import { IFormFieldGlobalProps } from "../../types";
4
+
5
+ export interface IDateInputFieldProps extends IFormFieldGlobalProps {
6
+ minDate?: string;
7
+ maxDate?: string;
8
+ [key: string]: any;
9
+ }
10
+
11
+ export default function DateField({
12
+ label,
13
+ name,
14
+ value,
15
+ description,
16
+ required,
17
+ onInputChange,
18
+ placeholder,
19
+ className,
20
+ register,
21
+ disabled,
22
+ minDate,
23
+ maxDate,
24
+ errors = {},
25
+ rounded = true,
26
+ ...rest
27
+ }: IDateInputFieldProps) {
28
+ // Utility function to create a mock ChangeEvent
29
+ const createMockChangeEvent = (
30
+ name: string,
31
+ value: any
32
+ ): ChangeEvent<any> => {
33
+ return {
34
+ target: { name, value },
35
+ currentTarget: { name, value },
36
+ preventDefault: () => {},
37
+ stopPropagation: () => {},
38
+ nativeEvent: new Event("change"),
39
+ bubbles: true,
40
+ cancelable: true,
41
+ defaultPrevented: false,
42
+ eventPhase: 0,
43
+ isTrusted: true,
44
+ timeStamp: Date.now(),
45
+ type: "change",
46
+ persist: () => {},
47
+ } as ChangeEvent<any>;
48
+ };
49
+
50
+ const handleValueChange = (newValue: any) => {
51
+ const startEvent = createMockChangeEvent(name, newValue.startDate);
52
+ onInputChange(startEvent);
53
+ };
54
+
55
+ const localValue = useMemo(() => {
56
+ return {
57
+ startDate: value,
58
+ endDate: value,
59
+ };
60
+ }, [value]);
61
+
62
+ return (
63
+ <>
64
+ <div
65
+ className={
66
+ "min-h-30 flex-col justify-start items-start gap-1.5 w-full relative min-w-[120px] " +
67
+ className
68
+ }
69
+ >
70
+ <div className="self-stretch flex-col justify-start items-start gap-1.5 flex ">
71
+ {label && (
72
+ <div className="text-slate-700 text-sm font-medium leading-tight">
73
+ {label} {required ? "*" : ""}
74
+ </div>
75
+ )}
76
+ <Datepicker
77
+ inputId={name}
78
+ primaryColor={"indigo"}
79
+ placeholder={placeholder}
80
+ startFrom={
81
+ localValue?.startDate ? new Date(localValue.startDate) : undefined
82
+ }
83
+ disabled={disabled}
84
+ startWeekOn="mon"
85
+ minDate={minDate ? new Date(minDate) : null}
86
+ maxDate={maxDate ? new Date(maxDate) : null}
87
+ useRange={false}
88
+ asSingle={true}
89
+ i18n={"cs"}
90
+ value={localValue}
91
+ onChange={handleValueChange}
92
+ displayFormat={"DD.MM.YYYY"}
93
+ inputClassName={(className) => {
94
+ return (
95
+ className +
96
+ ` focus:!ring-indigo-200 focus:!border-indigo-300 focus:ring-4 border !border-gray-200 !pr-8 !pl-[8px] font-normal ${rounded ? " rounded-lg " : " !rounded-none "}`
97
+ );
98
+ }}
99
+ containerClassName={(className) => {
100
+ return className + " ";
101
+ }}
102
+ {...rest}
103
+ />
104
+ </div>
105
+ {description && (
106
+ <div
107
+ className="self-stretch text-slate-600 text-sm font-normal leading-tight"
108
+ id={name + ":description"}
109
+ >
110
+ {description}
111
+ </div>
112
+ )}{" "}
113
+ {errors[name] && (
114
+ <div
115
+ className="HintText self-stretch text-red-600 text-sm font-normal leading-tight"
116
+ id={name + ":error"}
117
+ >
118
+ {errors[name]?.message}
119
+ </div>
120
+ )}
121
+ </div>
122
+ </>
123
+ );
124
+ }
@@ -0,0 +1,116 @@
1
+ import Datepicker from "react-tailwindcss-datepicker";
2
+ import { ChangeEvent } from "react";
3
+ import { IFormFieldGlobalProps } from "../../types";
4
+
5
+ export interface IInputFieldProps extends IFormFieldGlobalProps {
6
+ nameEnd: string;
7
+ [key: string]: any;
8
+ }
9
+
10
+ export default function DateRangeField({
11
+ label,
12
+ name,
13
+ nameEnd,
14
+ value,
15
+ description,
16
+ required,
17
+ onInputChange,
18
+ placeholder,
19
+ className,
20
+ register,
21
+ disabled,
22
+ errors = {},
23
+ rounded = true,
24
+ ...rest
25
+ }: IInputFieldProps) {
26
+ // Utility function to create a mock ChangeEvent
27
+ const createMockChangeEvent = (
28
+ name: string,
29
+ value: any
30
+ ): ChangeEvent<any> => {
31
+ return {
32
+ target: { name, value },
33
+ currentTarget: { name, value },
34
+ preventDefault: () => {},
35
+ stopPropagation: () => {},
36
+ nativeEvent: new Event("change"),
37
+ bubbles: true,
38
+ cancelable: true,
39
+ defaultPrevented: false,
40
+ eventPhase: 0,
41
+ isTrusted: true,
42
+ timeStamp: Date.now(),
43
+ type: "change",
44
+ persist: () => {},
45
+ } as ChangeEvent<any>;
46
+ };
47
+ const handleValueChange = (newValue: any) => {
48
+ console.log("newValue:", newValue);
49
+
50
+ const startEvent = createMockChangeEvent(name, newValue.startDate);
51
+ const endEvent = createMockChangeEvent(nameEnd, newValue.endDate);
52
+
53
+ onInputChange(startEvent);
54
+ onInputChange(endEvent);
55
+ };
56
+
57
+ return (
58
+ <>
59
+ <div
60
+ className={
61
+ "min-h-30 flex-col justify-start items-start gap-1.5 w-full relative min-w-[180px] " +
62
+ className
63
+ }
64
+ >
65
+ <div className="self-stretch flex-col justify-start items-start gap-1.5 flex ">
66
+ {label && (
67
+ <div className="text-slate-700 text-sm font-medium leading-tight">
68
+ {label} {required ? "*" : ""}
69
+ </div>
70
+ )}
71
+ <Datepicker
72
+ inputId={name}
73
+ primaryColor={"indigo"}
74
+ placeholder={placeholder}
75
+ startFrom={value?.startDate ? new Date(value.startDate) : undefined}
76
+ disabled={disabled}
77
+ startWeekOn="mon"
78
+ i18n={"cs"}
79
+ value={value}
80
+ onChange={handleValueChange}
81
+ popoverDirection={"down"}
82
+ displayFormat={"DD.MM.YYYY"}
83
+ inputClassName={(className) => {
84
+ return (
85
+ className +
86
+ ` focus:!ring-indigo-200 focus:!border-indigo-300 focus:ring-4 border !border-gray-200 !pr-8 !pl-[8px] font-normal ${rounded ? " rounded-lg " : " !rounded-none "}`
87
+ );
88
+ }}
89
+ containerClassName={(className) => {
90
+ return className + " ";
91
+ }}
92
+ separator={"-"}
93
+ toggleClassName="absolute rounded-r-lg right-0 h-full px-3 text-gray-400 focus:outline-none disabled:opacity-80 disabled:cursor-not-allowed text-gray-900"
94
+ {...rest}
95
+ />
96
+ </div>
97
+ {description && (
98
+ <div
99
+ className="self-stretch text-slate-600 text-sm font-normal leading-tight"
100
+ id={name + ":description"}
101
+ >
102
+ {description}
103
+ </div>
104
+ )}{" "}
105
+ {errors[name] && (
106
+ <div
107
+ className="HintText self-stretch text-red-600 text-sm font-normal leading-tight"
108
+ id={name + ":error"}
109
+ >
110
+ {errors[name]?.message}
111
+ </div>
112
+ )}
113
+ </div>
114
+ </>
115
+ );
116
+ }
@@ -0,0 +1,188 @@
1
+ import React, { useState, useEffect, useCallback } from "react";
2
+ import { useDropzone } from "react-dropzone";
3
+ import { handleErrors, useFederationContext } from "../../main";
4
+ import { MdDeleteOutline, MdInsertDriveFile } from "react-icons/md";
5
+
6
+ export interface FileData {
7
+ id: number;
8
+ mimeType: string;
9
+ size: number;
10
+ filename: string;
11
+ createdByEmpId: string;
12
+ created: string;
13
+ }
14
+
15
+ interface FileInputProps {
16
+ name: string;
17
+ label?: string;
18
+ initialFile?: FileData;
19
+ onFileChanged: (e: any) => void;
20
+ required?: boolean;
21
+ description?: string;
22
+ disabled?: boolean;
23
+ errors?: { [key: string]: { message: string } };
24
+ }
25
+
26
+ const FileInput: React.FC<FileInputProps> = ({
27
+ initialFile,
28
+ onFileChanged,
29
+ label,
30
+ name,
31
+ required,
32
+ description,
33
+ disabled,
34
+ errors = {},
35
+ }) => {
36
+ const [fileData, setFileData] = useState<FileData | null>(
37
+ initialFile || null
38
+ );
39
+ const [isFocused, setIsFocused] = useState(false);
40
+
41
+ const federationContext = useFederationContext();
42
+ useEffect(() => {
43
+ if (initialFile) {
44
+ setFileData(initialFile);
45
+ onFileChanged({ target: { name, value: initialFile.id.toString() } });
46
+ }
47
+ }, [initialFile, onFileChanged, name]);
48
+
49
+ const onDrop = useCallback(
50
+ async (acceptedFiles: File[]) => {
51
+ if (acceptedFiles.length > 0) {
52
+ const file = acceptedFiles[0];
53
+ const formData = new FormData();
54
+ formData.append("file", file);
55
+
56
+ federationContext.apiClient
57
+ .post<FileData>("/files/upload", formData, {
58
+ headers: {
59
+ "Content-Type": "multipart/form-data",
60
+ },
61
+ })
62
+ .then((response) => {
63
+ setFileData(response.data);
64
+ onFileChanged({
65
+ target: { name, value: response.data.id.toString() },
66
+ });
67
+ setIsFocused(false);
68
+ })
69
+ .catch((error) => {
70
+ handleErrors(error, federationContext.emitter);
71
+ console.error("There was an error!", error);
72
+ });
73
+ }
74
+ },
75
+ [
76
+ federationContext.apiClient,
77
+ onFileChanged,
78
+ name,
79
+ federationContext.emitter,
80
+ ]
81
+ );
82
+
83
+ const { getRootProps, getInputProps, isDragActive } = useDropzone({
84
+ onDrop,
85
+ disabled,
86
+ multiple: false,
87
+ });
88
+
89
+ const handleRemove = () => {
90
+ setFileData(null);
91
+ onFileChanged({ target: { name, value: null } });
92
+ };
93
+
94
+ return (
95
+ <div className="w-full min-h-30 flex-col justify-start items-start gap-1.5 inline-flex sharedLibrary">
96
+ <div className="self-stretch flex-col justify-start items-start gap-1.5 flex">
97
+ {label && (
98
+ <label
99
+ className="text-slate-700 text-sm leading-tight font-medium"
100
+ htmlFor={name}
101
+ >
102
+ {label} {required ? "*" : ""}
103
+ </label>
104
+ )}
105
+ <div
106
+ className={
107
+ `self-stretch px-3 py-2 rounded-lg justify-start items-center gap-2 inline-flex outline-none border` +
108
+ ` ${
109
+ isFocused && !errors[name]?.message
110
+ ? "outline-4 outline-indigo-200 outline-offset-0 border-indigo-300"
111
+ : ""
112
+ }` +
113
+ ` ${
114
+ isFocused && errors[name]?.message
115
+ ? "outline-4 outline-red-200 outline-offset-0 border-none"
116
+ : ""
117
+ } ` +
118
+ ` ${!isFocused && errors[name]?.message ? "border-red-200" : ""} ` +
119
+ ` ${disabled ? "bg-gray-100" : "bg-transparent"}`
120
+ }
121
+ onFocus={() => setIsFocused(true)}
122
+ onBlur={() => setIsFocused(false)}
123
+ >
124
+ <div className="flex relative grow shrink basis-0 min-h-5 lg:min-h-[32px] justify-start items-stretch gap-2 max-w-full ">
125
+ {!fileData ? (
126
+ <div
127
+ {...getRootProps()}
128
+ className={`w-full p-4 border-dashed border-2 rounded-lg text-center ${
129
+ isDragActive
130
+ ? "border-indigo-300 bg-indigo-50"
131
+ : "border-gray-300"
132
+ }`}
133
+ >
134
+ <input {...getInputProps()} id={name} />
135
+ <p className="text-gray-500">
136
+ {isDragActive
137
+ ? "Sem přetáhněte soubor"
138
+ : "Klikněte pro nahrání, nebo nahrajte přetažením souboru"}
139
+ </p>
140
+ </div>
141
+ ) : (
142
+ <div className="w-full flex items-center justify-between p-2">
143
+ <div className=" flex">
144
+ <MdInsertDriveFile style={{ fontSize: "2rem" }} />
145
+ <a
146
+ href={`/api/files/download/${fileData.id}`}
147
+ className="pl-2 text-left underline text-primary"
148
+ target="_blank"
149
+ >
150
+ {fileData.filename}
151
+ </a>
152
+ </div>
153
+ {!disabled && (
154
+ <div
155
+ onClick={handleRemove}
156
+ className="text-gray-600 cursor-pointer hover:text-primary hover:bg-gray-200 rounded-full ml-4"
157
+ >
158
+ <MdDeleteOutline
159
+ style={{ fontSize: "1.5rem", margin: "15px" }}
160
+ />
161
+ </div>
162
+ )}
163
+ </div>
164
+ )}
165
+ </div>
166
+ </div>
167
+ </div>
168
+ {description && (
169
+ <div
170
+ className="HintText self-stretch text-slate-600 text-sm font-normal leading-tight"
171
+ id={name + ":description"}
172
+ >
173
+ {description}
174
+ </div>
175
+ )}
176
+ {errors[name] && (
177
+ <div
178
+ className="HintText self-stretch text-red-600 text-sm font-normal leading-tight"
179
+ id={name + ":error"}
180
+ >
181
+ {errors[name]?.message}
182
+ </div>
183
+ )}
184
+ </div>
185
+ );
186
+ };
187
+
188
+ export default FileInput;