@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.
Files changed (128) hide show
  1. package/dist/Dialog-DZMfjbGC.js +421 -0
  2. package/dist/Dialog-DZMfjbGC.js.map +1 -0
  3. package/dist/assets/style.css +4369 -0
  4. package/dist/assets/tailwind.css +1365 -1193
  5. package/dist/check-B7dJm08z.js +12 -0
  6. package/dist/check-B7dJm08z.js.map +1 -0
  7. package/dist/components/Button.js +1 -1
  8. package/dist/components/datatable/DataTable.js +6 -2
  9. package/dist/components/datatable/DataTable.js.map +1 -1
  10. package/dist/components/datatable/DataTableServer.js +6 -2
  11. package/dist/components/datatable/DataTableServer.js.map +1 -1
  12. package/dist/components/form/AutocompleteSearchBar.js +6 -2
  13. package/dist/components/form/AutocompleteSearchBar.js.map +1 -1
  14. package/dist/components/form/AutocompleteSearchBarServer.js +6 -2
  15. package/dist/components/form/AutocompleteSearchBarServer.js.map +1 -1
  16. package/dist/components/form/FileInput.js +7 -3
  17. package/dist/components/form/FileInput.js.map +1 -1
  18. package/dist/components/form/FileInputMultiple.js +7 -3
  19. package/dist/components/form/FileInputMultiple.js.map +1 -1
  20. package/dist/components/form/FormField.js +6 -2
  21. package/dist/components/form/FormField.js.map +1 -1
  22. package/dist/components/form/PositionsSelectorSingle.js +6 -2
  23. package/dist/components/form/PositionsSelectorSingle.js.map +1 -1
  24. package/dist/components/form/SelectField.js +6 -2
  25. package/dist/components/form/SelectField.js.map +1 -1
  26. package/dist/components/profiles/ProfileOverview.js +6 -2
  27. package/dist/components/profiles/ProfileOverview.js.map +1 -1
  28. package/dist/components/ui/Combobox.d.ts +1 -0
  29. package/dist/components/ui/Combobox.js +138 -0
  30. package/dist/components/ui/Combobox.js.map +1 -0
  31. package/dist/components/ui/DateTimePicker.js +19 -5
  32. package/dist/components/ui/DateTimePicker.js.map +1 -1
  33. package/dist/components/ui/Dialog.js +15 -413
  34. package/dist/components/ui/Dialog.js.map +1 -1
  35. package/dist/components/ui/ScrollArea.js +2 -1
  36. package/dist/components/ui/ScrollArea.js.map +1 -1
  37. package/dist/components/ui/command.d.ts +80 -0
  38. package/dist/components/ui/command.js +643 -0
  39. package/dist/components/ui/command.js.map +1 -0
  40. package/dist/components/ui/datepicker.js +14 -1
  41. package/dist/components/ui/datepicker.js.map +1 -1
  42. package/dist/components/ui/popover.js +3 -2
  43. package/dist/components/ui/popover.js.map +1 -1
  44. package/dist/components/ui/radioGroup.d.ts +5 -0
  45. package/dist/components/ui/radioGroup.js +586 -0
  46. package/dist/components/ui/radioGroup.js.map +1 -0
  47. package/dist/components/ui/select.js +9 -144
  48. package/dist/components/ui/select.js.map +1 -1
  49. package/dist/index-BXrwe-_7.js +1395 -0
  50. package/dist/index-BXrwe-_7.js.map +1 -0
  51. package/dist/index-Bk8dRTPE.js +11 -0
  52. package/dist/index-Bk8dRTPE.js.map +1 -0
  53. package/dist/index-BlzC-wss.js +140 -0
  54. package/dist/index-BlzC-wss.js.map +1 -0
  55. package/dist/index-Bp9GiUkg.js +2266 -0
  56. package/dist/index-Bp9GiUkg.js.map +1 -0
  57. package/dist/index-BzVVosDl.js +57 -0
  58. package/dist/index-BzVVosDl.js.map +1 -0
  59. package/dist/index-CbAQSs_C.js +40 -0
  60. package/dist/index-CbAQSs_C.js.map +1 -0
  61. package/dist/index-Dz_fWpFA.js +2203 -0
  62. package/dist/index-Dz_fWpFA.js.map +1 -0
  63. package/dist/index-IXOTxK3N.js +7 -0
  64. package/dist/index-IXOTxK3N.js.map +1 -0
  65. package/dist/main.d.ts +3 -0
  66. package/dist/main.js +29 -12
  67. package/dist/main.js.map +1 -1
  68. package/dist/parse-D2yb8751.js +1727 -0
  69. package/dist/parse-D2yb8751.js.map +1 -0
  70. package/dist/tailwind-l0sNRNKZ.js +2 -0
  71. package/dist/tailwind-l0sNRNKZ.js.map +1 -0
  72. package/lib/components/Button.tsx +57 -0
  73. package/lib/components/Calendar.tsx +242 -0
  74. package/lib/components/ConfirmationModalDialog.tsx +115 -0
  75. package/lib/components/Modal.tsx +73 -0
  76. package/lib/components/ModalDialog.tsx +63 -0
  77. package/lib/components/Spinner.tsx +25 -0
  78. package/lib/components/SpinnerIcon.tsx +12 -0
  79. package/lib/components/datatable/DataTable.tsx +442 -0
  80. package/lib/components/datatable/DataTableServer.tsx +939 -0
  81. package/lib/components/datatable/DatatableSettings.tsx +48 -0
  82. package/lib/components/datatable/Resizable.tsx +99 -0
  83. package/lib/components/datatable/types.ts +33 -0
  84. package/lib/components/form/AutocompleteSearchBar.tsx +424 -0
  85. package/lib/components/form/AutocompleteSearchBarServer.tsx +257 -0
  86. package/lib/components/form/DateField.tsx +124 -0
  87. package/lib/components/form/DateRangeField.tsx +116 -0
  88. package/lib/components/form/FileInput.tsx +188 -0
  89. package/lib/components/form/FileInputMultiple.tsx +186 -0
  90. package/lib/components/form/FormField.tsx +371 -0
  91. package/lib/components/form/InputField.tsx +230 -0
  92. package/lib/components/form/PositionsSelectorSingle.tsx +266 -0
  93. package/lib/components/form/RadioGroup.tsx +64 -0
  94. package/lib/components/form/SelectField.tsx +267 -0
  95. package/lib/components/layout/IconInCircle.tsx +29 -0
  96. package/lib/components/layout/PageTitle.tsx +19 -0
  97. package/lib/components/layout/SectionTitle.tsx +22 -0
  98. package/lib/components/profiles/ProfileOverview.tsx +212 -0
  99. package/lib/components/ui/Calendar.tsx +68 -0
  100. package/lib/components/ui/Combobox.tsx +122 -0
  101. package/lib/components/ui/DatePicker.tsx +124 -0
  102. package/lib/components/ui/DateTimePicker.tsx +187 -0
  103. package/lib/components/ui/Dialog.tsx +118 -0
  104. package/lib/components/ui/ScrollArea.tsx +45 -0
  105. package/lib/components/ui/button.tsx +56 -0
  106. package/lib/components/ui/command.tsx +153 -0
  107. package/lib/components/ui/form.tsx +177 -0
  108. package/lib/components/ui/input.tsx +22 -0
  109. package/lib/components/ui/label.tsx +24 -0
  110. package/lib/components/ui/popover.tsx +31 -0
  111. package/lib/components/ui/radioGroup.tsx +44 -0
  112. package/lib/components/ui/select.tsx +158 -0
  113. package/lib/contexts/FederationContext.tsx +28 -0
  114. package/lib/contexts/useFederationContext.ts +4 -0
  115. package/lib/css/tailwind.css +10 -0
  116. package/lib/fonts/arial.ts +3 -0
  117. package/lib/fonts/arialBold.ts +4 -0
  118. package/lib/main.ts +64 -0
  119. package/lib/types.ts +492 -0
  120. package/lib/utils/PdfManager.ts +224 -0
  121. package/lib/utils/getFullName.tsx +83 -0
  122. package/lib/utils/getIntersectingDays.ts +28 -0
  123. package/lib/utils/handleErrors.ts +28 -0
  124. package/lib/utils/hasRightInModule.ts +17 -0
  125. package/lib/utils/hasRole.ts +12 -0
  126. package/lib/utils/utils.ts +6 -0
  127. package/lib/vite-env.d.ts +1 -0
  128. package/package.json +5 -2
@@ -0,0 +1,224 @@
1
+ import jsPDF from "jspdf";
2
+ import "jspdf-autotable";
3
+ import fontArial from "../fonts/arial";
4
+ import fontArialBold from "../fonts/arialBold";
5
+
6
+ class PDFManager {
7
+ private doc: jsPDF;
8
+ private currentYPosition: number;
9
+ private pageHeight: number;
10
+
11
+ constructor() {
12
+ this.doc = new jsPDF();
13
+ this.pageHeight = this.doc.internal.pageSize.height; // Get page height
14
+ this.currentYPosition = 10; // Start position at the top of the page
15
+ this.loadFonts();
16
+ }
17
+ public getPageHeight(): number {
18
+ return this.pageHeight;
19
+ }
20
+ public getCurrentY(): number {
21
+ return this.currentYPosition;
22
+ }
23
+
24
+ /**
25
+ * Adds a new page and resets the Y position.
26
+ */
27
+ public addNewPage() {
28
+ this.doc.addPage();
29
+ this.currentYPosition = 10; // Reset Y position for the new page
30
+ }
31
+
32
+ private loadFonts() {
33
+ this.doc.addFileToVFS("arial.ttf", fontArial);
34
+ this.doc.addFont("arial.ttf", "Arial", "normal");
35
+ this.doc.addFileToVFS("arialBold.ttf", fontArialBold);
36
+ this.doc.addFont("arialBold.ttf", "Arial", "bold");
37
+ this.doc.setFont("Arial", "normal");
38
+ }
39
+
40
+ /**
41
+ * Checks if a page break is needed and adds a new page if required.
42
+ */
43
+ private checkPageBreak(lineHeight: number) {
44
+ if (this.currentYPosition + lineHeight >= this.pageHeight) {
45
+ this.doc.addPage();
46
+ this.currentYPosition = 10; // Reset Y position for the new page
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Appends text to the current PDF document.
52
+ */
53
+ appendText(
54
+ text: string,
55
+ options: {
56
+ fontSize?: number;
57
+ bold?: boolean;
58
+ marginLeft?: number;
59
+ marginTop?: number;
60
+ marginBottom?: number;
61
+ } = {},
62
+ textOptions: any = {},
63
+ ) {
64
+ const {
65
+ fontSize = 12,
66
+ bold = false,
67
+ marginTop = 0,
68
+ marginLeft = 0,
69
+ marginBottom = 0,
70
+ } = options;
71
+ const lineHeight = fontSize * 0.5; // Approximate line height based on font size
72
+ const maxWidth = textOptions.width || 190 - marginLeft; // Adjust width based on offset
73
+
74
+ this.doc.setFontSize(fontSize);
75
+
76
+ this.doc.setFont("Arial", bold ? "bold" : "normal");
77
+
78
+ // Split the text into multiple lines if it exceeds the maxWidth
79
+ const textLines = this.doc.splitTextToSize(text, maxWidth);
80
+ this.currentYPosition += marginTop;
81
+ // Loop through each line and append it to the document
82
+ textLines.forEach((line: string) => {
83
+ this.checkPageBreak(lineHeight); // Check if new page is needed
84
+ this.doc.text(line, 10 + marginLeft, this.currentYPosition, {
85
+ maxWidth, ...textOptions
86
+ });
87
+
88
+ this.currentYPosition += lineHeight; // Update Y position after each line
89
+ });
90
+ this.currentYPosition += marginBottom;
91
+ }
92
+
93
+ appendSquare(
94
+ options: {
95
+ color?: [number, number, number] | string; // RGB color or string (default is black)
96
+ height?: number; // Square height (default is 10 units)
97
+ width?: number; // Square width (default is 10 units)
98
+ marginLeft?: number; // Left margin before the table
99
+ marginTop?: number; // Top margin before the table
100
+ marginBottom?: number; // Bottom margin after the table
101
+ startX?: number; // X coordinate where the square starts (default is 10)
102
+ fill?: boolean; // Whether the square should be filled or outlined (default is true)
103
+ } = {},
104
+ ) {
105
+ const {
106
+ color = [0, 0, 0], // Default black color
107
+ height = 10, // Default square height
108
+ width = 10, // Default square width
109
+ marginTop = 0, // Default no offset
110
+ marginBottom = 0, // Default no offset
111
+ marginLeft = 0, // Default no offset
112
+ startX = 10, // Default starting X position
113
+ fill = true, // Default to filled square
114
+ } = options;
115
+
116
+ // Adjust currentYPosition for any offset
117
+ this.currentYPosition += marginTop;
118
+
119
+ // Check if a new page is needed
120
+ this.checkPageBreak(height + marginTop + marginBottom);
121
+
122
+ // Set the color for the square
123
+ if (Array.isArray(color)) {
124
+ this.doc.setFillColor(...color); // For RGB color
125
+ this.doc.setDrawColor(...color); // For outline color in case it's not filled
126
+ } else {
127
+ this.doc.setFillColor(color); // For string color (e.g., 'red', '#FF0000')
128
+ this.doc.setDrawColor(color); // For outline color in case it's not filled
129
+ }
130
+
131
+ // Draw the square (filled or outlined)
132
+ if (fill) {
133
+ this.doc.rect(startX + marginLeft, this.currentYPosition, width, height, "F"); // F for filled
134
+ } else {
135
+ this.doc.rect(startX + marginLeft, this.currentYPosition, width, height, "S"); // S for outlined (stroke)
136
+ }
137
+
138
+ // Update Y position after the square (including bottom margin)
139
+ this.currentYPosition += height + marginBottom;
140
+ }
141
+ appendTable(
142
+ headers: string[] | null, // The table headers
143
+ data: any[][], // The table data as an array of arrays
144
+ options: {
145
+ bold?: boolean; // Bold font for headers
146
+ marginLeft?: number; // Left margin before the table
147
+ marginRight?: number; // Left margin before the table
148
+ marginTop?: number; // Top margin before the table
149
+ marginBottom?: number; // Bottom margin after the table
150
+ } = {},
151
+ tableOptions: any = {},
152
+ ) {
153
+ const {
154
+ bold = true,
155
+ marginLeft = 0,
156
+ marginRight = 0,
157
+ marginTop = 0,
158
+ marginBottom = 0,
159
+ } = options;
160
+
161
+ // Adjust the current Y position with the top margin
162
+ this.currentYPosition += marginTop;
163
+
164
+ // Append the table using autoTable
165
+ (this.doc as any).autoTable({
166
+ head: headers ? [headers] : null,
167
+ body: data,
168
+ startY: this.currentYPosition,
169
+ margin: { left: 10 + marginLeft, right: 10 + marginRight }, // Adjust the left margin
170
+ styles: { ...tableOptions.styles },
171
+ headStyles: { fontStyle: bold ? "bold" : "normal" },
172
+ ...tableOptions,
173
+ });
174
+
175
+ // Update the Y position after the table
176
+ this.currentYPosition =
177
+ (this.doc as any).lastAutoTable.finalY + marginBottom;
178
+ }
179
+
180
+ //helper function for appening title with square
181
+ appendPageTitle(title: string) {
182
+ this.appendText(title, { fontSize: 14, bold: true });
183
+
184
+ }
185
+ appendPageTitleWithLine(title: string, fontSize: number = 14) {
186
+ this.appendText(title, { fontSize: fontSize, bold: true });
187
+ this.appendSquare({
188
+ color: "#000",
189
+ height: 0.2,
190
+ width: 190,
191
+ marginTop: -fontSize / 3, // Add space between squares
192
+ marginBottom: (fontSize / 2) + 1, // Add space between squares
193
+ startX: 10,
194
+ fill: true, // Filled green square
195
+ });
196
+ }
197
+
198
+ appendPageTitleWithSquare(title: string) {
199
+ this.appendSquare({
200
+ color: "#000", // Green square
201
+ height: 10,
202
+ width: 10,
203
+ marginTop: 0, // Add space between squares
204
+ marginBottom: 0, // Add space between squares
205
+ startX: 10,
206
+ fill: true, // Filled green square
207
+ });
208
+ this.appendText(title, { fontSize: 14, bold: true, marginLeft: 12, marginTop: -6, marginBottom: 5 });
209
+
210
+ }
211
+
212
+
213
+
214
+
215
+ /**
216
+ * Saves the current PDF document.
217
+ */
218
+ savePDF(fileName: string = "report.pdf") {
219
+ this.doc.save(fileName);
220
+ this.doc.close()
221
+ }
222
+ }
223
+
224
+ export default PDFManager;
@@ -0,0 +1,83 @@
1
+ // userDataFetcher.ts
2
+
3
+ import { IEmployee, IPositionEmployee } from "../types";
4
+
5
+ const getFullName = (
6
+ employee: IEmployee,
7
+ showEmplNumb: boolean = false,
8
+ highlighManagers: boolean = true
9
+ ) => {
10
+ if (!employee) {
11
+ return "Neobsazené systemizované místo";
12
+ }
13
+ const fullName =
14
+ (employee?.degreeBefore ? employee?.degreeBefore + " " : "") +
15
+ (employee?.firstName || "") +
16
+ " " +
17
+ (employee?.lastName || "") +
18
+ (employee?.degreeAfter ? " " + employee?.degreeAfter : "") +
19
+ (showEmplNumb ? " (" + employee?.employeeId + ")" : "");
20
+
21
+ if (highlighManagers) {
22
+ if (employee?.manager == "1") {
23
+ return <span className="font-bold"> {fullName} </span>;
24
+ } else {
25
+ return fullName;
26
+ }
27
+ } else {
28
+ return fullName;
29
+ }
30
+ };
31
+ const getFullNameList = (
32
+ employee: IEmployee,
33
+ showEmplNumb: boolean = false,
34
+ highlighManagers: boolean = true
35
+ ) => {
36
+ if (!employee) {
37
+ return "Neobsazené systemizované místo";
38
+ }
39
+ const fullName =
40
+ (employee?.lastName || "") +
41
+ " " +
42
+ (employee?.firstName || "") +
43
+ (employee?.degreeBefore ? " " + employee?.degreeBefore : "") +
44
+ (employee?.degreeAfter ? " " + employee?.degreeAfter : "") +
45
+ (showEmplNumb ? " (" + employee?.employeeId + ")" : "");
46
+
47
+ if (highlighManagers) {
48
+ if (employee?.manager == "1") {
49
+ return <span className="font-bold"> {fullName} </span>;
50
+ } else {
51
+ return fullName;
52
+ }
53
+ } else {
54
+ return fullName;
55
+ }
56
+ };
57
+
58
+ const getFullNamePositionWithEmployee = (
59
+ position: IPositionEmployee,
60
+ showEmplNumb: boolean = false,
61
+ highlighManagers: boolean = true
62
+ ) => {
63
+ if (!position.employee)
64
+ return "Neobsazená pozice (" + position.positionNumber + ")";
65
+ return getFullName(position.employee, showEmplNumb, highlighManagers);
66
+ };
67
+ const getFullNameListPositionWithEmployee = (
68
+ position: IPositionEmployee,
69
+ showEmplNumb: boolean = false,
70
+ highlighManagers: boolean = true
71
+ ) => {
72
+ if (!position.employee)
73
+ return "Neobsazená pozice (" + position.positionNumber + ")";
74
+ return getFullNameList(position.employee, showEmplNumb, highlighManagers);
75
+ };
76
+ export {};
77
+
78
+ export {
79
+ getFullName,
80
+ getFullNameList,
81
+ getFullNamePositionWithEmployee,
82
+ getFullNameListPositionWithEmployee,
83
+ };
@@ -0,0 +1,28 @@
1
+ import { ICalendarItem } from "../types";
2
+
3
+ const getIntersectingDays = (range: ICalendarItem, ranges: ICalendarItem[]): number => {
4
+ if (!range.from || !range.to || !ranges || ranges.length == 0) return 0
5
+
6
+ const msInDay = 1000 * 60 * 60 * 24
7
+ // Helper function to calculate the number of overlapping days between two ranges
8
+ const calculateOverlap = (start1: Date, end1: Date, start2: Date, end2: Date): number => {
9
+ const start = new Date(Math.max(start1.getTime(), start2.getTime()))
10
+ const end = new Date(Math.min(end1.getTime(), end2.getTime()))
11
+
12
+ if (start > end) {
13
+ return 0
14
+ }
15
+
16
+ // Calculate the number of days between the start and end dates
17
+ return Math.ceil((end.getTime() - start.getTime()) / msInDay) + 1
18
+ }
19
+
20
+ let totalIntersectingDays = 0
21
+
22
+ ranges?.forEach((item) => {
23
+ totalIntersectingDays += calculateOverlap(range.from, range.to, item.from, item.to)
24
+ })
25
+
26
+ return totalIntersectingDays
27
+ }
28
+ export { getIntersectingDays }
@@ -0,0 +1,28 @@
1
+ import { AxiosError } from "axios";
2
+ import { Emitter } from "mitt";
3
+ import { Events } from "../types";
4
+
5
+ const handleErrors = (error: AxiosError, emitter: Emitter<Events>, customMessage?: string) => {
6
+ let errmsgToDisplay = (customMessage || "") + " " + ((error.response?.data as any)?.error?.message || error.message || "")
7
+
8
+ let errorTitle = ""
9
+ if (error.code == 'ERR_NETWORK') {
10
+ errorTitle = "Síťová chyba"
11
+ errmsgToDisplay = errmsgToDisplay + " " + new Date().toLocaleString();
12
+ } else if (error.code == 'ERR_BAD_REQUEST') {
13
+ if (error.response?.status == 403) {
14
+ errorTitle = "Přístup zamítnut"
15
+ } else {
16
+ errorTitle = "Interní chyba aplikace"
17
+ }
18
+ errmsgToDisplay = new Date().toLocaleString() + " (" + error.response?.status + ") \n" + errmsgToDisplay;
19
+ }
20
+ console.error(error);
21
+ emitter.emit("message", {
22
+ title: errorTitle,
23
+ message: errmsgToDisplay,
24
+ classes: "bg-danger ",
25
+ timeout: 0
26
+ });
27
+ }
28
+ export { handleErrors }
@@ -0,0 +1,17 @@
1
+ import { IAuthApps } from "../types";
2
+
3
+ const hasRightInModule = (
4
+ authApps: IAuthApps,
5
+ moduleName: string = "global",
6
+ role?: string,
7
+ ): boolean => {
8
+ if (role === undefined) {
9
+ return authApps.some((app) => app.name === moduleName);
10
+ } else {
11
+ return authApps.some(
12
+ (app) => app.name === moduleName && app.roles?.includes(role),
13
+ );
14
+ }
15
+ };
16
+
17
+ export { hasRightInModule };
@@ -0,0 +1,12 @@
1
+ import { IAuthApps } from "../types";
2
+
3
+ const hasRole = (
4
+ authApps: IAuthApps,
5
+ role: string,
6
+ ): boolean => {
7
+
8
+ return authApps.some((app) => app.roles.includes(role));
9
+
10
+ };
11
+
12
+ export { hasRole };
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@addsign/moje-agenda-shared-lib",
3
3
  "private": false,
4
- "version": "1.0.60",
4
+ "version": "2.0.0",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",
7
7
  "types": "dist/main.d.ts",
8
8
  "files": [
9
- "dist"
9
+ "dist",
10
+ "lib"
10
11
  ],
11
12
  "sideEffects": [
12
13
  "**/*.css"
@@ -65,6 +66,7 @@
65
66
  "@radix-ui/react-dialog": "^1.1.2",
66
67
  "@radix-ui/react-label": "^2.1.0",
67
68
  "@radix-ui/react-popover": "^1.1.2",
69
+ "@radix-ui/react-radio-group": "^1.2.1",
68
70
  "@radix-ui/react-scroll-area": "^1.2.0",
69
71
  "@radix-ui/react-select": "^2.1.2",
70
72
  "@radix-ui/react-slot": "^1.1.0",
@@ -72,6 +74,7 @@
72
74
  "axios": "^1.7.2",
73
75
  "class-variance-authority": "^0.7.0",
74
76
  "clsx": "^2.1.1",
77
+ "cmdk": "^1.0.4",
75
78
  "date-fns": "^3.6.0",
76
79
  "file-saver": "^2.0.5",
77
80
  "jspdf": "^2.5.2",