@addsign/moje-agenda-shared-lib 1.0.61 → 2.0.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.
Files changed (103) hide show
  1. package/dist/Dialog-IPFWYptw.js +421 -0
  2. package/dist/Dialog-IPFWYptw.js.map +1 -0
  3. package/dist/assets/style.css +4374 -0
  4. package/dist/assets/tailwind.css +1249 -1231
  5. package/dist/components/Button.js +1 -1
  6. package/dist/components/datatable/DataTable.js +2 -3
  7. package/dist/components/datatable/DataTable.js.map +1 -1
  8. package/dist/components/datatable/DataTableServer.js +2 -3
  9. package/dist/components/datatable/DataTableServer.js.map +1 -1
  10. package/dist/components/form/AutocompleteSearchBar.js +2 -3
  11. package/dist/components/form/AutocompleteSearchBar.js.map +1 -1
  12. package/dist/components/form/AutocompleteSearchBarServer.js +2 -3
  13. package/dist/components/form/AutocompleteSearchBarServer.js.map +1 -1
  14. package/dist/components/form/FileInput.js +2 -3
  15. package/dist/components/form/FileInput.js.map +1 -1
  16. package/dist/components/form/FileInputMultiple.js +2 -3
  17. package/dist/components/form/FileInputMultiple.js.map +1 -1
  18. package/dist/components/form/FormField.js +2 -3
  19. package/dist/components/form/FormField.js.map +1 -1
  20. package/dist/components/form/PositionsSelectorSingle.js +2 -3
  21. package/dist/components/form/PositionsSelectorSingle.js.map +1 -1
  22. package/dist/components/form/SelectField.js +2 -3
  23. package/dist/components/form/SelectField.js.map +1 -1
  24. package/dist/components/profiles/ProfileOverview.js +2 -3
  25. package/dist/components/profiles/ProfileOverview.js.map +1 -1
  26. package/dist/components/ui/Combobox.d.ts +14 -1
  27. package/dist/components/ui/Combobox.js +122 -98
  28. package/dist/components/ui/Combobox.js.map +1 -1
  29. package/dist/components/ui/command.js +1 -1
  30. package/dist/components/ui/command.js.map +1 -1
  31. package/dist/index-BF3Kkq7m.js +2212 -0
  32. package/dist/index-BF3Kkq7m.js.map +1 -0
  33. package/dist/index-Bm-YZac9.js +28864 -0
  34. package/dist/index-Bm-YZac9.js.map +1 -0
  35. package/dist/index-C4pkvuV-.js +1395 -0
  36. package/dist/index-C4pkvuV-.js.map +1 -0
  37. package/dist/index-M6rMtqtn.js +2203 -0
  38. package/dist/index-M6rMtqtn.js.map +1 -0
  39. package/dist/index.es-XWsfbnWC.js +9628 -0
  40. package/dist/index.es-XWsfbnWC.js.map +1 -0
  41. package/dist/jspdf.plugin.autotable-DXW3wQb-.js +11475 -0
  42. package/dist/jspdf.plugin.autotable-DXW3wQb-.js.map +1 -0
  43. package/dist/main.js +2 -5
  44. package/dist/main.js.map +1 -1
  45. package/dist/tailwind-l0sNRNKZ.js +2 -0
  46. package/dist/tailwind-l0sNRNKZ.js.map +1 -0
  47. package/lib/components/Button.tsx +57 -0
  48. package/lib/components/Calendar.tsx +242 -0
  49. package/lib/components/ConfirmationModalDialog.tsx +115 -0
  50. package/lib/components/Modal.tsx +73 -0
  51. package/lib/components/ModalDialog.tsx +63 -0
  52. package/lib/components/Spinner.tsx +25 -0
  53. package/lib/components/SpinnerIcon.tsx +12 -0
  54. package/lib/components/datatable/DataTable.tsx +442 -0
  55. package/lib/components/datatable/DataTableServer.tsx +939 -0
  56. package/lib/components/datatable/DatatableSettings.tsx +48 -0
  57. package/lib/components/datatable/Resizable.tsx +99 -0
  58. package/lib/components/datatable/types.ts +33 -0
  59. package/lib/components/form/AutocompleteSearchBar.tsx +424 -0
  60. package/lib/components/form/AutocompleteSearchBarServer.tsx +257 -0
  61. package/lib/components/form/DateField.tsx +124 -0
  62. package/lib/components/form/DateRangeField.tsx +116 -0
  63. package/lib/components/form/FileInput.tsx +188 -0
  64. package/lib/components/form/FileInputMultiple.tsx +186 -0
  65. package/lib/components/form/FormField.tsx +371 -0
  66. package/lib/components/form/InputField.tsx +230 -0
  67. package/lib/components/form/PositionsSelectorSingle.tsx +266 -0
  68. package/lib/components/form/RadioGroup.tsx +64 -0
  69. package/lib/components/form/SelectField.tsx +267 -0
  70. package/lib/components/layout/IconInCircle.tsx +29 -0
  71. package/lib/components/layout/PageTitle.tsx +19 -0
  72. package/lib/components/layout/SectionTitle.tsx +22 -0
  73. package/lib/components/profiles/ProfileOverview.tsx +212 -0
  74. package/lib/components/ui/Calendar.tsx +68 -0
  75. package/lib/components/ui/Combobox.tsx +175 -0
  76. package/lib/components/ui/DatePicker.tsx +124 -0
  77. package/lib/components/ui/DateTimePicker.tsx +187 -0
  78. package/lib/components/ui/Dialog.tsx +118 -0
  79. package/lib/components/ui/ScrollArea.tsx +45 -0
  80. package/lib/components/ui/button.tsx +56 -0
  81. package/lib/components/ui/command.tsx +153 -0
  82. package/lib/components/ui/form.tsx +177 -0
  83. package/lib/components/ui/input.tsx +22 -0
  84. package/lib/components/ui/label.tsx +24 -0
  85. package/lib/components/ui/popover.tsx +31 -0
  86. package/lib/components/ui/radioGroup.tsx +44 -0
  87. package/lib/components/ui/select.tsx +158 -0
  88. package/lib/contexts/FederationContext.tsx +28 -0
  89. package/lib/contexts/useFederationContext.ts +4 -0
  90. package/lib/css/tailwind.css +10 -0
  91. package/lib/fonts/arial.ts +3 -0
  92. package/lib/fonts/arialBold.ts +4 -0
  93. package/lib/main.ts +64 -0
  94. package/lib/types.ts +492 -0
  95. package/lib/utils/PdfManager.ts +224 -0
  96. package/lib/utils/getFullName.tsx +83 -0
  97. package/lib/utils/getIntersectingDays.ts +28 -0
  98. package/lib/utils/handleErrors.ts +28 -0
  99. package/lib/utils/hasRightInModule.ts +17 -0
  100. package/lib/utils/hasRole.ts +12 -0
  101. package/lib/utils/utils.ts +6 -0
  102. package/lib/vite-env.d.ts +1 -0
  103. package/package.json +3 -2
@@ -0,0 +1,267 @@
1
+ import * as React from "react";
2
+ import { IFormFieldGlobalProps, IOptionItem } from "../../types";
3
+ import { MdCheck, MdClose, MdExpandLess, MdExpandMore } from "react-icons/md";
4
+ import { useClickAway } from "react-use";
5
+ import { SpinnerIcon, useFederationContext } from "../../main";
6
+ import { FaSpinner } from "react-icons/fa";
7
+
8
+ export interface ISelectFieldProps extends IFormFieldGlobalProps {
9
+ options?: IOptionItem[];
10
+ valueKey?: string;
11
+ labelKey?: string | ((item: any) => string);
12
+ fetchUrl?: string;
13
+ loading?: boolean;
14
+ }
15
+
16
+ export default function SelectField({
17
+ label,
18
+ name,
19
+ value,
20
+ description,
21
+ onInputChange,
22
+ options,
23
+ placeholder,
24
+ className,
25
+ errors = {},
26
+ clearable,
27
+ required,
28
+ disabled,
29
+ rounded = true,
30
+ fetchUrl,
31
+ valueKey,
32
+ labelKey,
33
+ loading,
34
+ }: ISelectFieldProps) {
35
+ const [isFocused, setIsFocused] = React.useState(false);
36
+ const [isLoading, setIsLoading] = React.useState(false);
37
+ const apiClient = useFederationContext()?.apiClient;
38
+ const [localOptions, setLocalOptions] = React.useState(options);
39
+
40
+ const ref = React.useRef(null);
41
+ const handleClear = (e: any) => {
42
+ // value;
43
+ e.stopPropagation(); // Add this line
44
+
45
+ setIsFocused(false);
46
+ onInputChange({
47
+ ...e,
48
+ target: {
49
+ value: "",
50
+ name: name,
51
+ },
52
+ });
53
+ };
54
+
55
+ React.useEffect(() => {
56
+ const fetchOptions = async (fetchUrl: string) => {
57
+ setIsLoading(true);
58
+ const { data } = await apiClient.get(fetchUrl);
59
+
60
+ // Check if the first item in the data array is a number to determine the data type
61
+ const isArrayOfNumbers = typeof data[0] === "number";
62
+ const isInPageable = data && data.hasOwnProperty("content");
63
+
64
+ // Transform data based on its type
65
+ const transformedOptions = (isInPageable ? data.content : data).map(
66
+ (item: any) => {
67
+ if (isArrayOfNumbers) {
68
+ // If it's a number, use the number for both value and label
69
+ return { value: item, label: item.toString() };
70
+ } else {
71
+ // Otherwise, extract using predefined keys or defaults
72
+ return {
73
+ value: item[valueKey || "id"],
74
+ label:
75
+ labelKey instanceof Function && labelKey !== undefined
76
+ ? labelKey(item)
77
+ : item[labelKey as string],
78
+ };
79
+ }
80
+ }
81
+ );
82
+
83
+ setLocalOptions([
84
+ { value: null, label: " " }, // Add an empty option as the first item
85
+ ...transformedOptions,
86
+ ]);
87
+ setIsLoading(false);
88
+ };
89
+
90
+ if (fetchUrl) fetchOptions(fetchUrl);
91
+ if (options) setLocalOptions(options);
92
+ }, [fetchUrl, options, apiClient, valueKey, labelKey]); // ensure valueKey and labelKey are also in the dependency array if they are dynamic
93
+
94
+ const handleToggleFocus = () => {
95
+ setIsFocused((prev) => !prev);
96
+ };
97
+
98
+ useClickAway(ref, () => {
99
+ setIsFocused(false);
100
+ });
101
+
102
+ const hangleChange = (option: IOptionItem) => {
103
+ const tmp: any = {
104
+ target: {
105
+ value: option.value,
106
+ name: name,
107
+ },
108
+ };
109
+
110
+ onInputChange(tmp);
111
+ setIsFocused(false);
112
+ };
113
+ const currentlySelectedOption = React.useMemo(() => {
114
+ if (value === undefined || value === "") return null;
115
+ return localOptions?.find((option) => option.value == value);
116
+ }, [localOptions, value, options]);
117
+
118
+ const listOfOptions = () => {
119
+ if (!localOptions) return [];
120
+ return (
121
+ <div
122
+ id="list"
123
+ className="max-h-[390px] min-w-20 w-auto whitespace-nowrap absolute z-[100000] -top-[1px] mt-[4px] bg-white rounded-lg shadow-xl border
124
+ border-gray-200 justify-start items-start inline-flex overflow-auto cursor-default"
125
+ >
126
+ <div className="grow shrink basis-0 py-1 flex-col justify-start items-start inline-flex">
127
+ {localOptions.map((option, index) => {
128
+ return (
129
+ <div
130
+ className="group self-stretch px-1.5 py-px justify-start items-center inline-flex hover:bg-gray-50 "
131
+ onClick={() => hangleChange(option)}
132
+ key={index}
133
+ >
134
+ <div className="grow shrink basis-0 px-1 py-2 rounded-md flex-col justify-start items-start gap-2 inline-flex">
135
+ <div className="self-stretch justify-start items-center gap-2 inline-flex">
136
+ <div className="text-gray-900 text-sm font-normal leading-normal">
137
+ {option.label}
138
+ </div>
139
+ {option.description && (
140
+ <div className="text-slate-600 sm-base font-normal leading-normal">
141
+ {option.description}
142
+ </div>
143
+ )}
144
+ </div>
145
+ </div>
146
+ <div className="w-5 h-5 relative">
147
+ {option.value === value && value !== "" && (
148
+ <MdCheck size={22} className="text-primary" />
149
+ )}{" "}
150
+ {option.value !== value && (
151
+ <MdCheck
152
+ size={22}
153
+ className="text-transparent group-hover:text-gray-300"
154
+ />
155
+ )}
156
+ </div>
157
+ </div>
158
+ );
159
+ })}
160
+ </div>
161
+ </div>
162
+ );
163
+ };
164
+
165
+ return (
166
+ <>
167
+ <div
168
+ className={
169
+ "w-full min-h-30 flex-col justify-start items-start gap-0inline-flex " +
170
+ className
171
+ }
172
+ id="selectField"
173
+ ref={ref}
174
+ >
175
+ <div className="self-stretch flex-col justify-start items-start gap-1.5 flex">
176
+ {label && (
177
+ <label
178
+ className="text-slate-700 text-sm font-medium leading-tight"
179
+ htmlFor={name}
180
+ >
181
+ {label} {required ? "*" : ""}
182
+ </label>
183
+ )}
184
+ <div
185
+ className={`self-stretch w-full px-3 py-1 bg-white border justify-start items-center gap-0 inline-flex outline-none
186
+ ${isFocused ? "outline-4 outline-indigo-200 outline-offset-0 border-indigo-300 " : ""}
187
+ ${
188
+ isFocused && errors[name]?.message
189
+ ? "outline-4 outline-red-200 outline-offset-0 border-none"
190
+ : ""
191
+ }
192
+ ${rounded ? " rounded-lg " : " rounded-none "}
193
+ ${!isFocused && errors[name]?.message ? "border-red-200" : ""}
194
+ ${disabled || isLoading ? "opacity-80 cursor-not-allowed " : ""}
195
+ `}
196
+ onClick={() =>
197
+ !disabled && !isLoading ? handleToggleFocus() : null
198
+ }
199
+ >
200
+ <div className="grow shrink basis-0 min-h-[32px] justify-start items-center gap-0 flex whitespace-nowrap w-[calc(100%-40px)] ">
201
+ <div
202
+ className="text-gray-900 text-sm font-normal leading-normal text-ellipsis overflow-hidden w-full"
203
+ id={name}
204
+ >
205
+ {currentlySelectedOption?.label}
206
+ {!currentlySelectedOption?.label && placeholder && (
207
+ <span className="text-slate-400 font-normal">
208
+ {" "}
209
+ {placeholder}
210
+ </span>
211
+ )}
212
+ </div>
213
+ <div className="text-slate-600 text-base font-normal leading-normal">
214
+ {currentlySelectedOption?.description}
215
+ </div>
216
+ </div>
217
+ {clearable &&
218
+ value !== "" &&
219
+ value !== null &&
220
+ value !== undefined &&
221
+ !disabled && (
222
+ <div
223
+ className="w-6 h-6 relative cursor-pointer "
224
+ id={name + ":clear"}
225
+ onClick={handleClear}
226
+ >
227
+ <div className="absolute inset-0 flex items-center justify-center hover:bg-gray-100 w-6 rounded-full text-lg">
228
+ <MdClose />
229
+ </div>{" "}
230
+ </div>
231
+ )}
232
+ {isLoading === true ||
233
+ (loading && (
234
+ <div className="w-6 h-6 relative flex items-center justify-center align-middle">
235
+ <SpinnerIcon icon={<FaSpinner />} />
236
+ </div>
237
+ ))}
238
+
239
+ <div className="w-6 h-6 relative cursor-pointer ">
240
+ <div className="absolute inset-0 flex items-center justify-center hover:bg-gray-100 w-6 rounded-full text-lg">
241
+ {isFocused && !disabled && <MdExpandLess />}
242
+ {!isFocused && !disabled && <MdExpandMore />}
243
+ </div>
244
+ </div>
245
+ </div>
246
+ </div>{" "}
247
+ <div className="w-full relative">{isFocused && listOfOptions()}</div>
248
+ {description && !isFocused && (
249
+ <div
250
+ className="self-stretch text-slate-600 text-sm font-normal leading-tight"
251
+ id={name + ":description"}
252
+ >
253
+ {description}
254
+ </div>
255
+ )}
256
+ {errors[name] && (
257
+ <div
258
+ className="HintText self-stretch text-red-600 text-sm font-normal leading-tight"
259
+ id={name + ":error"}
260
+ >
261
+ {errors[name]?.message}
262
+ </div>
263
+ )}
264
+ </div>
265
+ </>
266
+ );
267
+ }
@@ -0,0 +1,29 @@
1
+ import React from "react";
2
+ import { MdCheck, MdOutlinePending } from "react-icons/md";
3
+
4
+ interface IconInCircleProps {
5
+ isComplete: boolean;
6
+ isPending?: boolean;
7
+ size?: number;
8
+ title?: string;
9
+ }
10
+
11
+ const IconInCircle: React.FC<IconInCircleProps> = ({
12
+ isComplete,
13
+ size = 20,
14
+ title,
15
+ isPending,
16
+ }) => {
17
+ if (!isComplete && !isPending) return "";
18
+ return (
19
+ <div
20
+ className="p-2 rounded-full border text-success flex items-center justify-center text-2xl "
21
+ style={{ width: 2 * size, height: 2 * size }}
22
+ title={title || ""}
23
+ >
24
+ {isPending ? <MdOutlinePending size={size} /> : <MdCheck size={size} />}
25
+ </div>
26
+ );
27
+ };
28
+
29
+ export default IconInCircle;
@@ -0,0 +1,19 @@
1
+ interface IPageTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {
2
+ textSize?: string;
3
+ children?: React.ReactNode;
4
+ }
5
+
6
+ export default function PageTitle({
7
+ children,
8
+ textSize = "text-2xl",
9
+ ...props
10
+ }: IPageTitleProps) {
11
+ // Combine textSize with the other fixed class names
12
+ const className = ` text-gray-900 ${textSize} font-medium font-['Inter'] leading-7 mb-5`;
13
+
14
+ return (
15
+ <h1 {...props} className={className}>
16
+ {children}
17
+ </h1>
18
+ );
19
+ }
@@ -0,0 +1,22 @@
1
+ import * as React from "react";
2
+
3
+ interface ISectionTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {
4
+ textSize?: string;
5
+ children?: React.ReactNode;
6
+ }
7
+ export default function SectionTitle({
8
+ children,
9
+ textSize = "text-lg",
10
+ ...props
11
+ }: ISectionTitleProps) {
12
+ // Combine textSize with the other fixed class names
13
+ const className = ` text-gray-900 ${textSize} font-semibold font-['Inter'] leading-7 mb-3`;
14
+
15
+ return (
16
+ <div className="border-b border-gray-300 pt-2 mb-5 ">
17
+ <h2 {...props} className={className}>
18
+ {children}
19
+ </h2>
20
+ </div>
21
+ );
22
+ }
@@ -0,0 +1,212 @@
1
+ import { useEffect, useMemo, useState } from "react";
2
+ import SectionTitle from "../layout/SectionTitle";
3
+ import {
4
+ EApproverRoles,
5
+ IProfileApprover,
6
+ IStep,
7
+ getFullName,
8
+ handleErrors,
9
+ useFederationContext,
10
+ } from "../../main";
11
+ import { AxiosResponse } from "axios";
12
+ import IconInCircle from "../layout/IconInCircle";
13
+
14
+ interface ProfileOverviewProps {
15
+ employeeId?: number;
16
+ onProfileApproversChange?: (data: IProfileApprover[]) => void;
17
+ steps: IStep[];
18
+ }
19
+
20
+ interface IApproverRoleGroup {
21
+ name: string;
22
+ roles: EApproverRoles[];
23
+ all: boolean;
24
+ parallel: boolean;
25
+ stepType: number;
26
+ }
27
+
28
+ const approverRoleGroups: IApproverRoleGroup[] = [
29
+ {
30
+ name: "Předschvalovatelé",
31
+ roles: [EApproverRoles.predschvalovatel],
32
+ all: true,
33
+ parallel: true,
34
+ stepType: 1,
35
+ },
36
+ {
37
+ name: "Schvalovatelé",
38
+ roles: [
39
+ EApproverRoles.vedouci,
40
+ EApproverRoles.zastupce,
41
+ EApproverRoles.zastupce9,
42
+ EApproverRoles.vyssiSchvalovatel,
43
+ EApproverRoles.vyssiSchvalovatelZastupce,
44
+ EApproverRoles.vyssiSchvalovatelZastupce9,
45
+ ],
46
+ all: false,
47
+ parallel: true,
48
+ stepType: 2,
49
+ },
50
+ {
51
+ name: "Docházkový vedoucí",
52
+ roles: [EApproverRoles.dochazkovyVedouci],
53
+ all: true,
54
+ parallel: false,
55
+ stepType: 3,
56
+ },
57
+ ];
58
+
59
+ export default function ProfileOverview({
60
+ employeeId,
61
+ onProfileApproversChange,
62
+ steps,
63
+ }: ProfileOverviewProps) {
64
+ const [profileApprovers, setProfileApprovers] = useState<
65
+ Array<IProfileApprover>
66
+ >([]);
67
+ const context = useFederationContext();
68
+
69
+ const currentApprovers = useMemo(() => {
70
+ return approverRoleGroups.map((group) => {
71
+ const groupApprovers = profileApprovers.filter((approver) =>
72
+ group.roles.includes(approver.role)
73
+ );
74
+
75
+ const groupApprovedSteps = steps.filter(
76
+ (step) =>
77
+ step.type == group.stepType &&
78
+ groupApprovers.some(
79
+ (approver) =>
80
+ step.employee.employeeId == approver?.approver?.employeeId
81
+ )
82
+ );
83
+
84
+ return {
85
+ ...group,
86
+ numberOfApprovers: groupApprovers.length,
87
+ numberOfApprovedSteps: groupApprovedSteps.length,
88
+ isComplete:
89
+ (group.all && groupApprovers.length == groupApprovedSteps.length) ||
90
+ (!group.all && groupApprovedSteps.length > 0),
91
+ };
92
+ });
93
+ }, [profileApprovers, steps]);
94
+
95
+ useEffect(() => {
96
+ context.apiClient
97
+ .get<IProfileApprover[]>(
98
+ "/timeoff/approve-profile" + (employeeId ? "/" + employeeId : "")
99
+ )
100
+ .then((response: AxiosResponse) => {
101
+ setProfileApprovers(response.data);
102
+ })
103
+ .catch((error) => {
104
+ handleErrors(
105
+ error,
106
+ context.emitter,
107
+ "Nepodařilo se dohledat schvalovací profil pro uživatele " +
108
+ employeeId
109
+ );
110
+ console.error("Error submitting form:", error);
111
+ });
112
+ }, [employeeId, context.emitter, context.apiClient]);
113
+
114
+ const decisionsTranslations = (decision: string): string | null => {
115
+ const translations: { [key: string]: string } = {
116
+ approved: "Schváleno",
117
+ rejected: "Zamítnuto",
118
+ cancelled: "Stornováno",
119
+ evided: "Zaevidováno",
120
+ };
121
+
122
+ return translations[decision] || null;
123
+ };
124
+ // pri nacteni profileApprovers se muze dat vedet parent componente inbfo o aktualnich schvalovatelich
125
+ useEffect(() => {
126
+ if (onProfileApproversChange) onProfileApproversChange(profileApprovers);
127
+ }, [profileApprovers, onProfileApproversChange]);
128
+
129
+ const findStep = (
130
+ steps: IStep[],
131
+ group: IApproverRoleGroup,
132
+ row: IProfileApprover
133
+ ) => {
134
+ return steps.find(
135
+ (step) =>
136
+ step.type == group.stepType &&
137
+ step.employee.employeeId == row.position?.employee?.employeeId
138
+ );
139
+ };
140
+
141
+ return (
142
+ <>
143
+ <SectionTitle>Postup schvalování</SectionTitle>
144
+ <div className="flex gap-4">
145
+ {currentApprovers.map((group, index) => (
146
+ <div
147
+ key={group.name}
148
+ className={
149
+ "w-full lg:w-1/3 rounded-lg shadow-xl p-5 border gap-4 flex flex-col "
150
+ }
151
+ >
152
+ <div className="flex justify-between">
153
+ <h3 className="text-xl font-bold">
154
+ {group.name} - {index}
155
+ </h3>
156
+
157
+ <IconInCircle
158
+ isComplete={group.isComplete}
159
+ title={
160
+ group.numberOfApprovedSteps + " / " + group.numberOfApprovers
161
+ }
162
+ isPending={!group.isComplete}
163
+ ></IconInCircle>
164
+ </div>
165
+ {profileApprovers
166
+ .filter((it) => group.roles.includes(it.role))
167
+ .map((row, index) => (
168
+ <div
169
+ key={index}
170
+ className=" border rounded-lg p-4 flex justify-between gap-5"
171
+ >
172
+ <div>
173
+ <h3 className="">
174
+ {getFullName(row.position?.employee, true)}
175
+ </h3>
176
+ <p className="text-sm ">{row.roleTxt}</p>
177
+ {findStep(steps, group, row) && (
178
+ <>
179
+ <p className="text-sm "></p>{" "}
180
+ {findStep(steps, group, row)?.decision && (
181
+ <p className="text-sm ">
182
+ {decisionsTranslations(
183
+ findStep(steps, group, row)?.decision || ""
184
+ )}{" "}
185
+ (
186
+ {new Date(
187
+ findStep(steps, group, row)?.date || 0
188
+ ).toLocaleString()}
189
+ )
190
+ </p>
191
+ )}
192
+ {findStep(steps, group, row)?.comment && (
193
+ <p className="text-sm ">
194
+ <b>Komentář</b>:{" "}
195
+ {findStep(steps, group, row)?.comment}{" "}
196
+ </p>
197
+ )}
198
+ </>
199
+ )}
200
+ </div>
201
+ <IconInCircle
202
+ isComplete={findStep(steps, group, row) != null}
203
+ isPending={!group.isComplete}
204
+ />
205
+ </div>
206
+ ))}
207
+ </div>
208
+ ))}
209
+ </div>
210
+ </>
211
+ );
212
+ }
@@ -0,0 +1,68 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { ChevronLeft, ChevronRight } from "lucide-react";
5
+ import { DayPicker } from "react-day-picker";
6
+
7
+ import { cs } from "date-fns/locale";
8
+ import { cn } from "../../utils/utils";
9
+ import { buttonVariants } from "./button";
10
+
11
+ export type CalendarProps = React.ComponentProps<typeof DayPicker>;
12
+
13
+ function Calendar({
14
+ className,
15
+ classNames,
16
+ showOutsideDays = true,
17
+ ...props
18
+ }: CalendarProps) {
19
+ return (
20
+ <DayPicker
21
+ locale={cs}
22
+ showOutsideDays={showOutsideDays}
23
+ className={cn("p-3", className)}
24
+ classNames={{
25
+ months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
26
+ month: "space-y-4",
27
+ caption: "flex justify-center pt-1 relative items-center",
28
+ caption_label: "text-sm font-medium",
29
+ nav: "space-x-1 flex items-center",
30
+ nav_button: cn(
31
+ buttonVariants({ variant: "outline" }),
32
+ "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
33
+ ),
34
+ nav_button_previous: "absolute left-1",
35
+ nav_button_next: "absolute right-1",
36
+ table: "w-full border-collapse space-y-1",
37
+ head_row: "flex",
38
+ head_cell:
39
+ "text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]",
40
+ row: "flex w-full mt-2",
41
+ cell: "h-9 w-9 text-center text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20",
42
+ day: cn(
43
+ buttonVariants({ variant: "ghost" }),
44
+ "h-9 w-9 p-0 font-normal aria-selected:opacity-100"
45
+ ),
46
+ day_range_end: "day-range-end",
47
+ day_selected:
48
+ "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
49
+ day_today: "bg-accent text-accent-foreground",
50
+ day_outside:
51
+ "day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground",
52
+ day_disabled: "text-muted-foreground opacity-50",
53
+ day_range_middle:
54
+ "aria-selected:bg-accent aria-selected:text-accent-foreground",
55
+ day_hidden: "invisible",
56
+ ...classNames,
57
+ }}
58
+ components={{
59
+ IconLeft: () => <ChevronLeft className="h-4 w-4" />,
60
+ IconRight: () => <ChevronRight className="h-4 w-4" />,
61
+ }}
62
+ {...props}
63
+ />
64
+ );
65
+ }
66
+ Calendar.displayName = "Calendar";
67
+
68
+ export { Calendar };