@addsign/moje-agenda-shared-lib 1.0.60 → 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.
- package/dist/Dialog-DZMfjbGC.js +421 -0
- package/dist/Dialog-DZMfjbGC.js.map +1 -0
- package/dist/assets/style.css +4369 -0
- package/dist/assets/tailwind.css +1365 -1193
- package/dist/check-B7dJm08z.js +12 -0
- package/dist/check-B7dJm08z.js.map +1 -0
- package/dist/components/Button.js +1 -1
- package/dist/components/datatable/DataTable.js +6 -2
- package/dist/components/datatable/DataTable.js.map +1 -1
- package/dist/components/datatable/DataTableServer.js +6 -2
- package/dist/components/datatable/DataTableServer.js.map +1 -1
- package/dist/components/form/AutocompleteSearchBar.js +6 -2
- package/dist/components/form/AutocompleteSearchBar.js.map +1 -1
- package/dist/components/form/AutocompleteSearchBarServer.js +6 -2
- package/dist/components/form/AutocompleteSearchBarServer.js.map +1 -1
- package/dist/components/form/FileInput.js +7 -3
- package/dist/components/form/FileInput.js.map +1 -1
- package/dist/components/form/FileInputMultiple.js +7 -3
- package/dist/components/form/FileInputMultiple.js.map +1 -1
- package/dist/components/form/FormField.js +6 -2
- package/dist/components/form/FormField.js.map +1 -1
- package/dist/components/form/PositionsSelectorSingle.js +6 -2
- package/dist/components/form/PositionsSelectorSingle.js.map +1 -1
- package/dist/components/form/SelectField.js +6 -2
- package/dist/components/form/SelectField.js.map +1 -1
- package/dist/components/profiles/ProfileOverview.js +6 -2
- package/dist/components/profiles/ProfileOverview.js.map +1 -1
- package/dist/components/ui/Combobox.d.ts +1 -0
- package/dist/components/ui/Combobox.js +138 -0
- package/dist/components/ui/Combobox.js.map +1 -0
- package/dist/components/ui/DateTimePicker.js +19 -5
- package/dist/components/ui/DateTimePicker.js.map +1 -1
- package/dist/components/ui/Dialog.js +15 -413
- package/dist/components/ui/Dialog.js.map +1 -1
- package/dist/components/ui/ScrollArea.js +2 -1
- package/dist/components/ui/ScrollArea.js.map +1 -1
- package/dist/components/ui/command.d.ts +80 -0
- package/dist/components/ui/command.js +643 -0
- package/dist/components/ui/command.js.map +1 -0
- package/dist/components/ui/datepicker.js +14 -1
- package/dist/components/ui/datepicker.js.map +1 -1
- package/dist/components/ui/popover.js +3 -2
- package/dist/components/ui/popover.js.map +1 -1
- package/dist/components/ui/radioGroup.d.ts +5 -0
- package/dist/components/ui/radioGroup.js +586 -0
- package/dist/components/ui/radioGroup.js.map +1 -0
- package/dist/components/ui/select.js +9 -144
- package/dist/components/ui/select.js.map +1 -1
- package/dist/index-BXrwe-_7.js +1395 -0
- package/dist/index-BXrwe-_7.js.map +1 -0
- package/dist/index-Bk8dRTPE.js +11 -0
- package/dist/index-Bk8dRTPE.js.map +1 -0
- package/dist/index-BlzC-wss.js +140 -0
- package/dist/index-BlzC-wss.js.map +1 -0
- package/dist/index-Bp9GiUkg.js +2266 -0
- package/dist/index-Bp9GiUkg.js.map +1 -0
- package/dist/index-BzVVosDl.js +57 -0
- package/dist/index-BzVVosDl.js.map +1 -0
- package/dist/index-CbAQSs_C.js +40 -0
- package/dist/index-CbAQSs_C.js.map +1 -0
- package/dist/index-Dz_fWpFA.js +2203 -0
- package/dist/index-Dz_fWpFA.js.map +1 -0
- package/dist/index-IXOTxK3N.js +7 -0
- package/dist/index-IXOTxK3N.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.js +29 -12
- package/dist/main.js.map +1 -1
- package/dist/parse-D2yb8751.js +1727 -0
- package/dist/parse-D2yb8751.js.map +1 -0
- package/dist/tailwind-l0sNRNKZ.js +2 -0
- package/dist/tailwind-l0sNRNKZ.js.map +1 -0
- package/lib/components/Button.tsx +57 -0
- package/lib/components/Calendar.tsx +242 -0
- package/lib/components/ConfirmationModalDialog.tsx +115 -0
- package/lib/components/Modal.tsx +73 -0
- package/lib/components/ModalDialog.tsx +63 -0
- package/lib/components/Spinner.tsx +25 -0
- package/lib/components/SpinnerIcon.tsx +12 -0
- package/lib/components/datatable/DataTable.tsx +442 -0
- package/lib/components/datatable/DataTableServer.tsx +939 -0
- package/lib/components/datatable/DatatableSettings.tsx +48 -0
- package/lib/components/datatable/Resizable.tsx +99 -0
- package/lib/components/datatable/types.ts +33 -0
- package/lib/components/form/AutocompleteSearchBar.tsx +424 -0
- package/lib/components/form/AutocompleteSearchBarServer.tsx +257 -0
- package/lib/components/form/DateField.tsx +124 -0
- package/lib/components/form/DateRangeField.tsx +116 -0
- package/lib/components/form/FileInput.tsx +188 -0
- package/lib/components/form/FileInputMultiple.tsx +186 -0
- package/lib/components/form/FormField.tsx +371 -0
- package/lib/components/form/InputField.tsx +230 -0
- package/lib/components/form/PositionsSelectorSingle.tsx +266 -0
- package/lib/components/form/RadioGroup.tsx +64 -0
- package/lib/components/form/SelectField.tsx +267 -0
- package/lib/components/layout/IconInCircle.tsx +29 -0
- package/lib/components/layout/PageTitle.tsx +19 -0
- package/lib/components/layout/SectionTitle.tsx +22 -0
- package/lib/components/profiles/ProfileOverview.tsx +212 -0
- package/lib/components/ui/Calendar.tsx +68 -0
- package/lib/components/ui/Combobox.tsx +122 -0
- package/lib/components/ui/DatePicker.tsx +124 -0
- package/lib/components/ui/DateTimePicker.tsx +187 -0
- package/lib/components/ui/Dialog.tsx +118 -0
- package/lib/components/ui/ScrollArea.tsx +45 -0
- package/lib/components/ui/button.tsx +56 -0
- package/lib/components/ui/command.tsx +153 -0
- package/lib/components/ui/form.tsx +177 -0
- package/lib/components/ui/input.tsx +22 -0
- package/lib/components/ui/label.tsx +24 -0
- package/lib/components/ui/popover.tsx +31 -0
- package/lib/components/ui/radioGroup.tsx +44 -0
- package/lib/components/ui/select.tsx +158 -0
- package/lib/contexts/FederationContext.tsx +28 -0
- package/lib/contexts/useFederationContext.ts +4 -0
- package/lib/css/tailwind.css +10 -0
- package/lib/fonts/arial.ts +3 -0
- package/lib/fonts/arialBold.ts +4 -0
- package/lib/main.ts +64 -0
- package/lib/types.ts +492 -0
- package/lib/utils/PdfManager.ts +224 -0
- package/lib/utils/getFullName.tsx +83 -0
- package/lib/utils/getIntersectingDays.ts +28 -0
- package/lib/utils/handleErrors.ts +28 -0
- package/lib/utils/hasRightInModule.ts +17 -0
- package/lib/utils/hasRole.ts +12 -0
- package/lib/utils/utils.ts +6 -0
- package/lib/vite-env.d.ts +1 -0
- package/package.json +5 -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;
|