@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,230 @@
1
+ import * as React from "react";
2
+ import { IFormFieldGlobalProps } from "../../types";
3
+ import { MdClose } from "react-icons/md";
4
+ import { useClickAway } from "react-use";
5
+ import { FaSpinner } from "react-icons/fa";
6
+ import SpinnerIcon from "../SpinnerIcon";
7
+
8
+ export interface IInputFieldProps extends IFormFieldGlobalProps {
9
+ maxLength?: number;
10
+ debounceTimeout?: number;
11
+ manualRef?: React.RefObject<HTMLInputElement>;
12
+ disableAutocomplete?: boolean;
13
+ }
14
+
15
+ export default function InputField({
16
+ label,
17
+ name,
18
+ value,
19
+ description,
20
+ onInputChange,
21
+ placeholder,
22
+ className,
23
+ register,
24
+ type,
25
+ disabled,
26
+ maxLength,
27
+ errors = {},
28
+ rounded = true,
29
+ debounceTimeout,
30
+ required,
31
+ onFocus = () => {},
32
+ onBlur = () => {},
33
+ children,
34
+ disableAutocomplete,
35
+ }: IInputFieldProps) {
36
+ const wrapperRef = React.useRef(null);
37
+ const [isFocused, setIsFocused] = React.useState(false);
38
+ const [inputValue, setInputValue] = React.useState(value || ""); // Local state for input value
39
+ const debounceTimeoutRef = React.useRef<NodeJS.Timeout | null>(null); // Ref to hold the debounce timeout
40
+ const [inputIsChanging, setInputIsChanging] = React.useState(false);
41
+
42
+ React.useEffect(() => {
43
+ setInputValue(value || "");
44
+ }, [value]);
45
+ const handleClear = (e: any) => {
46
+ setInputValue(""); // Clear local state
47
+ onInputChange({
48
+ ...e,
49
+ target: {
50
+ value: "",
51
+ name: name,
52
+ },
53
+ });
54
+ };
55
+ const fallbackRef = React.useRef(null);
56
+ const {
57
+ ref: registeredRef = fallbackRef,
58
+ onBlur: formOnBlur = () => {} /* default function */,
59
+ ...rest
60
+ } = register ? register(name) : {};
61
+
62
+ const ref = registeredRef || fallbackRef;
63
+ const handleSetFocus = () => {
64
+ setIsFocused(true);
65
+ ref?.current?.focus();
66
+ };
67
+ useClickAway(wrapperRef, () => {
68
+ if (isFocused) {
69
+ setIsFocused(false);
70
+ }
71
+ });
72
+
73
+ const handleDebouncedChange = React.useCallback(
74
+ (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
75
+ const { value } = e.target;
76
+ setInputValue(value); // Update local state
77
+ setInputIsChanging(true);
78
+
79
+ if (debounceTimeout) {
80
+ // Clear any previous debounce timeout
81
+ if (debounceTimeoutRef.current)
82
+ clearTimeout(debounceTimeoutRef.current);
83
+
84
+ // Set a new debounce timeout
85
+ debounceTimeoutRef.current = setTimeout(() => {
86
+ onInputChange(e);
87
+ setInputIsChanging(false);
88
+ }, debounceTimeout);
89
+ } else {
90
+ onInputChange(e);
91
+ setInputIsChanging(false);
92
+ }
93
+ },
94
+ [debounceTimeout, debounceTimeoutRef, onInputChange, setInputValue]
95
+ );
96
+
97
+ const handleBlur = (
98
+ e: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>
99
+ ) => {
100
+ formOnBlur(e); // Call React Hook Form's onBlur
101
+ setIsFocused(false); // Then call your custom onBlur logic
102
+
103
+ if (debounceTimeoutRef.current && inputIsChanging) {
104
+ clearTimeout(debounceTimeoutRef.current);
105
+ onInputChange(e);
106
+ setInputIsChanging(false);
107
+ }
108
+ };
109
+
110
+ const renderInput = () => {
111
+ switch (type) {
112
+ case "textarea":
113
+ return (
114
+ <textarea
115
+ id={name}
116
+ name={name}
117
+ className="grow shrink basis-0 text-gray-900 text-sm font-normal leading-tight focus:border-none
118
+ bg-white "
119
+ disabled={disabled}
120
+ value={inputValue}
121
+ rows={3}
122
+ {...rest} // Spread the rest of register's return value
123
+ // ref={ref}
124
+ onChange={(e) => handleDebouncedChange(e)}
125
+ onFocus={() => {
126
+ setIsFocused(true);
127
+ onFocus();
128
+ }}
129
+ onBlur={(e) => {
130
+ handleBlur(e);
131
+ onBlur();
132
+ }}
133
+ maxLength={maxLength || 4000}
134
+ />
135
+ );
136
+ default:
137
+ return (
138
+ <>
139
+ <input
140
+ className={`text-gray-900 text-sm font-normal leading-normal
141
+ text-ellipsis overflow-hidden w-full disabled:cursor-not-allowed bg-white`}
142
+ id={name}
143
+ name={name}
144
+ disabled={disabled}
145
+ type={type}
146
+ {...rest}
147
+ // ref={ref}
148
+ value={inputValue} // Controlled value
149
+ placeholder={placeholder}
150
+ onChange={(e) => handleDebouncedChange(e)}
151
+ onFocus={() => {
152
+ setIsFocused(true);
153
+ onFocus();
154
+ }}
155
+ onBlur={(e) => {
156
+ handleBlur(e);
157
+ onBlur();
158
+ }}
159
+ autoComplete={disableAutocomplete ? "off" : "on"}
160
+ />
161
+ </>
162
+ );
163
+ }
164
+ };
165
+
166
+ return (
167
+ <>
168
+ <div
169
+ className={
170
+ "min-h-30 flex-col justify-start items-start gap-1.5 w-full relative " +
171
+ className
172
+ }
173
+ ref={wrapperRef}
174
+ id="component"
175
+ >
176
+ <div className="self-stretch flex-col justify-start items-start gap-1.5 flex">
177
+ {label && (
178
+ <div className="text-slate-700 text-sm font-medium leading-tight">
179
+ {label} {required ? "*" : ""}
180
+ </div>
181
+ )}
182
+ <div
183
+ className={`self-stretch w-full pl-3 pr-2 py-1 border justify-between items-center gap-0 inline-flex outline-none bg-white
184
+ ${isFocused ? "outline-4 outline-indigo-200 outline-offset-0 border-indigo-300 " : ""}
185
+ ${rounded ? " rounded-lg " : " rounded-none "}
186
+ ${disabled ? " !opacity-80 cursor-not-allowed " : ""}`}
187
+ onClick={handleSetFocus}
188
+ >
189
+ <div className="flex-grow basis-0 min-h-[32px] justify-start items-center gap-0 flex whitespace-nowrap w-[calc(100%-40px)] ">
190
+ {renderInput()}
191
+ </div>
192
+ <div className="items-center content-center flex-shrink flex">
193
+ {inputIsChanging === true && (
194
+ <div className="w-6 h-6 relative flex items-center justify-center align-middle">
195
+ <SpinnerIcon icon={<FaSpinner />} />
196
+ </div>
197
+ )}
198
+ {value && !disabled && (
199
+ <div
200
+ className="w-6 h-6 relative flex cursor-pointer items-center justify-center align-middle hover:bg-gray-100 rounded-full text-lg"
201
+ onClick={handleClear}
202
+ id={name + ":clear"}
203
+ >
204
+ <MdClose />
205
+ </div>
206
+ )}
207
+ {children}
208
+ </div>
209
+ </div>
210
+ </div>
211
+ {description && !isFocused && (
212
+ <div
213
+ className="self-stretch text-slate-600 text-sm font-normal leading-tight"
214
+ id={name + ":description"}
215
+ >
216
+ {description}
217
+ </div>
218
+ )}{" "}
219
+ {errors[name] && (
220
+ <div
221
+ className="HintText self-stretch text-red-600 text-sm font-normal leading-tight mt-1"
222
+ id={name + ":error"}
223
+ >
224
+ {errors[name]?.message}
225
+ </div>
226
+ )}
227
+ </div>
228
+ </>
229
+ );
230
+ }
@@ -0,0 +1,266 @@
1
+ import { useEffect, useState } from "react";
2
+ import {
3
+ AutocompleteSearchBar,
4
+ Button,
5
+ FormField,
6
+ IPositionEmployee,
7
+ SectionTitle,
8
+ getFullName,
9
+ } from "../../main";
10
+ import { AxiosResponse } from "axios";
11
+ import { CiEdit } from "react-icons/ci";
12
+
13
+ import { useFederationContext } from "../../main";
14
+ import { MdCheck } from "react-icons/md";
15
+
16
+ interface IPositionsSelectorSingleProps {
17
+ label: string;
18
+ name: string;
19
+ onInputChange: (e: any) => void;
20
+ initPositionEmployee?: IPositionEmployee;
21
+ disabled?: boolean;
22
+ initDepartmentId?: string;
23
+ placeholder?: string;
24
+ }
25
+ function PositionsSelectorSingle({
26
+ label,
27
+ name,
28
+ onInputChange,
29
+ initPositionEmployee,
30
+ disabled,
31
+ initDepartmentId,
32
+ placeholder,
33
+ }: IPositionsSelectorSingleProps) {
34
+ const [isModalOpen, setModalOpen] = useState(false);
35
+ const [department, setDepartment] = useState("");
36
+ const [positions, setPositions] = useState<IPositionEmployee[]>([]); // State to hold the original data
37
+ const [selectedPositionEmployee, setSelectedPositionEmployee] =
38
+ useState<IPositionEmployee>(); // State to hold the original data
39
+ const [storedPositionEmployee, setStoredPositionEmployee] =
40
+ useState<IPositionEmployee>(); // State to hold the original data
41
+
42
+ const apiClient = useFederationContext()?.apiClient;
43
+
44
+ useEffect(() => {
45
+ const handleKeyDown = (event: KeyboardEvent) => {
46
+ if (event.key === "Escape") {
47
+ setModalOpen(false);
48
+ }
49
+ };
50
+ window.addEventListener("keydown", handleKeyDown);
51
+ return () => {
52
+ window.removeEventListener("keydown", handleKeyDown);
53
+ };
54
+ }, []);
55
+
56
+ useEffect(() => {
57
+ setSelectedPositionEmployee(initPositionEmployee);
58
+ setStoredPositionEmployee(initPositionEmployee);
59
+ setDepartment(department);
60
+ }, [initPositionEmployee, department]);
61
+
62
+ //If I will get init department ID and not init position employee, I will use the init department ID as a default departmen
63
+ useEffect(() => {
64
+ if (initDepartmentId && !initPositionEmployee) {
65
+ setDepartment(initDepartmentId);
66
+ }
67
+ }, [initDepartmentId, initPositionEmployee]);
68
+
69
+ const handleSave = (
70
+ selectedPositionEmployee: IPositionEmployee | undefined
71
+ ) => {
72
+ onInputChange({
73
+ target: { name: name, value: selectedPositionEmployee || null },
74
+ });
75
+
76
+ setStoredPositionEmployee(selectedPositionEmployee);
77
+ setModalOpen(false);
78
+ };
79
+
80
+ const handleSelectedPositionEmployee = (position: IPositionEmployee) => {
81
+ console.log(
82
+ "%csrcsharedPositionsSelectorSingle.tsx:54 position",
83
+ "color: #007acc;",
84
+ position,
85
+ selectedPositionEmployee,
86
+ position.id == selectedPositionEmployee?.id
87
+ );
88
+ if (
89
+ !selectedPositionEmployee ||
90
+ position.id !== selectedPositionEmployee?.id
91
+ ) {
92
+ setSelectedPositionEmployee(position);
93
+ } else {
94
+ setSelectedPositionEmployee(undefined);
95
+ }
96
+ };
97
+ const handleDepartmentChange = (e: any) => {
98
+ console.log("%csrcsharedTransferListModal.tsx:109 e", "color: #007acc;", e);
99
+ setDepartment(e);
100
+ };
101
+
102
+ useEffect(() => {
103
+ if (department && isModalOpen) {
104
+ apiClient
105
+ .get(
106
+ "os/positions/employees?maxResults=1000&departmentId=" + department
107
+ )
108
+ .then((response: AxiosResponse) => {
109
+ setPositions(response.data?.content);
110
+ })
111
+ .catch((error: any) => {
112
+ console.error(
113
+ "There was an error fetching positions/employees",
114
+ error
115
+ );
116
+ });
117
+ }
118
+ }, [apiClient, department, isModalOpen]);
119
+
120
+ return (
121
+ <>
122
+ <div className="">
123
+ <p className="text-slate-700 text-sm leading-tight font-medium mb-2">
124
+ {label}:
125
+ </p>
126
+ <div className="w-full flex gap-5 ">
127
+ <FormField
128
+ placeholder={placeholder || "Zatím není určen"}
129
+ name={name}
130
+ onInputChange={() => {}}
131
+ type="text"
132
+ disabled
133
+ value={
134
+ storedPositionEmployee?.employee
135
+ ? getFullName(storedPositionEmployee?.employee, true, false)
136
+ : ""
137
+ }
138
+ ></FormField>
139
+ <Button
140
+ className={"text-xl " + (disabled ? "hidden" : "")}
141
+ variant="primary"
142
+ onClick={() => setModalOpen(true)}
143
+ disabled={disabled}
144
+ >
145
+ <CiEdit></CiEdit>
146
+ </Button>
147
+ </div>
148
+ </div>
149
+ {isModalOpen && (
150
+ <div
151
+ className="fixed inset-0 bg-black bg-opacity-25 flex items-center justify-center p-5"
152
+ style={{ zIndex: 50 }}
153
+ >
154
+ <div
155
+ className="bg-white rounded-lg shadow-xl m-4 overflow-auto max-h-full pointer-events-auto "
156
+ style={{ width: "50vw" }}
157
+ >
158
+ <div className="flex flex-col gap-4 p-5">
159
+ <div className="">
160
+ <SectionTitle>{label} - Výběr pozice</SectionTitle>{" "}
161
+ </div>
162
+ <div className="">
163
+ <AutocompleteSearchBar
164
+ name="departmentId"
165
+ placeholder="Vyhledejte útvar"
166
+ fetchUrl="os/departments?pageSize=5000"
167
+ valueKey="departmentId"
168
+ labelKey="nameLong"
169
+ label="Útvar"
170
+ onChange={handleDepartmentChange}
171
+ onBlur={() => {}}
172
+ onFocus={() => {}}
173
+ value={department}
174
+ showId={true}
175
+ ></AutocompleteSearchBar>
176
+ </div>
177
+ <div className=" max-h-[400px] overflow-auto">
178
+ <div className="pt-4 w-full grid grid-cols-2 gap-x-4">
179
+ <div className="text-slate-700 text-sm leading-tight font-medium my-4 col-span-2">
180
+ Nalezené pozice:
181
+ </div>
182
+ {positions.map((position) => (
183
+ <div
184
+ key={position.id}
185
+ className={`group flex justify-between items-center py-2 cursor-pointer ${
186
+ position.id == selectedPositionEmployee?.id
187
+ ? " bg-gray-100 hover:bg-gray-200 "
188
+ : "hover:bg-gray-50"
189
+ } `}
190
+ onClick={() => handleSelectedPositionEmployee(position)}
191
+ >
192
+ <div className=" ">
193
+ {getFullName(position.employee, true)}
194
+ </div>
195
+ <div
196
+ className={` group-hover:text-gray-300 px-3 ${
197
+ position.id == selectedPositionEmployee?.id
198
+ ? " text-blue-500 group-hover:text-black "
199
+ : "text-transparent"
200
+ }`}
201
+ >
202
+ <MdCheck size={25} />
203
+ </div>
204
+ </div>
205
+ ))}
206
+ </div>
207
+ </div>
208
+ <div className="flex justify-between gap-4">
209
+ <div className=" w-1/2 group flex ">
210
+ <FormField
211
+ placeholder="Zatím není určen"
212
+ name={name}
213
+ label="Zvolená pozice "
214
+ onInputChange={() => {}}
215
+ type="text"
216
+ disabled
217
+ value={
218
+ selectedPositionEmployee
219
+ ? getFullName(
220
+ selectedPositionEmployee?.employee,
221
+ true,
222
+ false
223
+ )
224
+ : ""
225
+ }
226
+ ></FormField>
227
+ {selectedPositionEmployee && (
228
+ <div className="flex justify-end space-x-5 mt-auto ml-5">
229
+ {" "}
230
+ <Button
231
+ variant="secondary"
232
+ onClick={() =>
233
+ handleSelectedPositionEmployee(
234
+ selectedPositionEmployee
235
+ )
236
+ }
237
+ >
238
+ Odebrat přiřazení
239
+ </Button>
240
+ </div>
241
+ )}
242
+ </div>
243
+ <div className="flex justify-end space-x-5 mt-auto ">
244
+ <Button
245
+ variant="secondary"
246
+ onClick={() => setModalOpen(false)}
247
+ >
248
+ Zrušit
249
+ </Button>
250
+ <Button
251
+ variant="primary"
252
+ onClick={() => handleSave(selectedPositionEmployee)}
253
+ >
254
+ Uložit
255
+ </Button>
256
+ </div>
257
+ </div>
258
+ </div>
259
+ </div>
260
+ </div>
261
+ )}
262
+ </>
263
+ );
264
+ }
265
+
266
+ export default PositionsSelectorSingle;
@@ -0,0 +1,64 @@
1
+ import { IFormFieldGlobalProps, IOptionItem } from "../../types";
2
+
3
+ export interface IRadioGroupProps extends IFormFieldGlobalProps {
4
+ options?: IOptionItem[];
5
+ valueKey?: string;
6
+ labelKey?: string;
7
+ }
8
+
9
+ export default function RadioGroup({
10
+ label,
11
+ name,
12
+ value,
13
+ description,
14
+ onInputChange,
15
+ options,
16
+ errors = {},
17
+ required,
18
+ disabled,
19
+ register = () => {},
20
+ }: IRadioGroupProps) {
21
+ return (
22
+ <div className="w-full min-h-30 flex-col justify-start items-start gap-1.5 sharedLibrary">
23
+ <div className="self-stretch flex-col justify-start items-start gap-1.5 flex mb-2">
24
+ {label && (
25
+ <label
26
+ className="text-slate-700 text-sm leading-tight font-medium"
27
+ htmlFor={name}
28
+ >
29
+ {label} {required ? "*" : ""}
30
+ </label>
31
+ )}
32
+ </div>
33
+ <div className="flex flex-col gap-1">
34
+ {options?.map((option, index) => (
35
+ <label key={index} className="inline-flex items-center space-x-2">
36
+ <input
37
+ type="radio"
38
+ name={name}
39
+ value={option.value}
40
+ checked={value === option.value}
41
+ disabled={disabled}
42
+ {...register(name)} // Spread the rest of register's return value
43
+ onChange={onInputChange}
44
+ className="text-indigo-600 border-gray-300 focus:ring-indigo-500 mr-2"
45
+ />
46
+ <span className="text-gray-900 text-sm font-normal leading-tight">
47
+ {option.label}
48
+ </span>
49
+ </label>
50
+ ))}
51
+ </div>
52
+ {description && (
53
+ <div className="HintText self-stretch text-slate-600 text-sm font-normal leading-tight">
54
+ {description}
55
+ </div>
56
+ )}
57
+ {errors[name] && (
58
+ <div className="HintText self-stretch text-red-600 text-sm font-normal leading-tight">
59
+ {errors[name]?.message}
60
+ </div>
61
+ )}
62
+ </div>
63
+ );
64
+ }