nakel-ola commited on
Commit
e146f58
·
1 Parent(s): f6942c9

feat: added export button to the project

Browse files
components/editor/header/index.tsx CHANGED
@@ -1,4 +1,10 @@
1
- import { ArrowRight, HelpCircle, RefreshCcw, Lock } from "lucide-react";
 
 
 
 
 
 
2
  import Image from "next/image";
3
  import Link from "next/link";
4
 
@@ -11,6 +17,8 @@ import { SwitchDevice } from "@/components/editor/switch-devide";
11
  import { SwitchTab } from "./switch-tab";
12
  import { History } from "@/components/editor/history";
13
  import { useEditor } from "@/hooks/useEditor";
 
 
14
  import {
15
  Tooltip,
16
  TooltipContent,
@@ -18,8 +26,22 @@ import {
18
  } from "@/components/ui/tooltip";
19
 
20
  export function Header() {
21
- const { project } = useEditor();
22
  const { user, openLoginWindow } = useUser();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  return (
24
  <header className="border-b bg-neutral-950 dark:border-neutral-800 grid grid-cols-3 lg:flex items-center max-lg:gap-3 justify-between z-20">
25
  <div className="flex items-center justify-between lg:max-w-[600px] lg:w-full py-2 px-2 lg:px-3 lg:pl-6 gap-3">
@@ -68,6 +90,15 @@ export function Header() {
68
  <RefreshCcw className="size-3 mr-0.5" />
69
  Refresh Preview
70
  </Button>
 
 
 
 
 
 
 
 
 
71
  <Link
72
  href="https://huggingface.co/spaces/enzostvs/deepsite/discussions/427"
73
  target="_blank"
 
1
+ import {
2
+ ArrowRight,
3
+ HelpCircle,
4
+ RefreshCcw,
5
+ Lock,
6
+ Download,
7
+ } from "lucide-react";
8
  import Image from "next/image";
9
  import Link from "next/link";
10
 
 
17
  import { SwitchTab } from "./switch-tab";
18
  import { History } from "@/components/editor/history";
19
  import { useEditor } from "@/hooks/useEditor";
20
+ import { exportProjectAsZip } from "@/lib/export-project";
21
+ import { toast } from "sonner";
22
  import {
23
  Tooltip,
24
  TooltipContent,
 
26
  } from "@/components/ui/tooltip";
27
 
28
  export function Header() {
29
+ const { project, pages, files } = useEditor();
30
  const { user, openLoginWindow } = useUser();
31
+
32
+ const handleExportZip = async () => {
33
+ try {
34
+ await exportProjectAsZip({
35
+ pages,
36
+ files,
37
+ projectName: project?.space_id || "project",
38
+ });
39
+ toast.success("Project exported successfully!");
40
+ } catch (error) {
41
+ console.error("Export failed:", error);
42
+ toast.error("Failed to export project. Please try again.");
43
+ }
44
+ };
45
  return (
46
  <header className="border-b bg-neutral-950 dark:border-neutral-800 grid grid-cols-3 lg:flex items-center max-lg:gap-3 justify-between z-20">
47
  <div className="flex items-center justify-between lg:max-w-[600px] lg:w-full py-2 px-2 lg:px-3 lg:pl-6 gap-3">
 
90
  <RefreshCcw className="size-3 mr-0.5" />
91
  Refresh Preview
92
  </Button>
93
+ <Button
94
+ size="xs"
95
+ variant="bordered"
96
+ className="max-lg:hidden"
97
+ onClick={handleExportZip}
98
+ >
99
+ <Download className="size-3 mr-0.5" />
100
+ Export ZIP
101
+ </Button>
102
  <Link
103
  href="https://huggingface.co/spaces/enzostvs/deepsite/discussions/427"
104
  target="_blank"
lib/export-project.ts ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import JSZip from "jszip";
2
+ import { Page } from "@/types";
3
+
4
+ interface ExportProjectParams {
5
+ pages: Page[];
6
+ files: string[];
7
+ projectName?: string;
8
+ }
9
+
10
+ export async function exportProjectAsZip({
11
+ pages,
12
+ files,
13
+ projectName = "project",
14
+ }: ExportProjectParams): Promise<void> {
15
+ const zip = new JSZip();
16
+
17
+ // Create a root folder with the project name
18
+ const projectFolder = zip.folder(projectName);
19
+
20
+ // Add all HTML pages inside the project folder
21
+ pages.forEach((page) => {
22
+ projectFolder?.file(page.path, page.html);
23
+ });
24
+
25
+ // Create assets folder as a subfolder within the project folder
26
+ if (files.length > 0) {
27
+ const assetsFolder = projectFolder?.folder("assets");
28
+
29
+ // Download and add each file to the assets folder
30
+ for (const fileUrl of files) {
31
+ try {
32
+ const response = await fetch(fileUrl);
33
+ if (response.ok) {
34
+ const blob = await response.blob();
35
+ const fileName = fileUrl.split("/").pop() || "file";
36
+ assetsFolder?.file(fileName, blob);
37
+ }
38
+ } catch (error) {
39
+ console.warn(`Failed to download file: ${fileUrl}`, error);
40
+ }
41
+ }
42
+ }
43
+
44
+ // Generate the ZIP file
45
+ const zipBlob = await zip.generateAsync({ type: "blob" });
46
+
47
+ // Create download link and trigger download
48
+ const url = URL.createObjectURL(zipBlob);
49
+ const link = document.createElement("a");
50
+ link.href = url;
51
+ link.download = `${projectName}-export.zip`;
52
+ document.body.appendChild(link);
53
+ link.click();
54
+ document.body.removeChild(link);
55
+ URL.revokeObjectURL(url);
56
+ }
package-lock.json CHANGED
@@ -27,12 +27,14 @@
27
  "@tanstack/eslint-plugin-query": "^5.86.0",
28
  "@tanstack/react-query": "^5.86.0",
29
  "@tanstack/react-query-devtools": "^5.86.0",
 
30
  "axios": "^1.11.0",
31
  "class-variance-authority": "^0.7.1",
32
  "classnames": "^2.5.1",
33
  "clsx": "^2.1.1",
34
  "date-fns": "^4.1.0",
35
  "framer-motion": "^12.23.22",
 
36
  "log4js": "^6.9.1",
37
  "log4js-json-layout": "^2.2.3",
38
  "lucide-react": "^0.542.0",
@@ -2427,6 +2429,15 @@
2427
  "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
2428
  "license": "MIT"
2429
  },
 
 
 
 
 
 
 
 
 
2430
  "node_modules/@types/node": {
2431
  "version": "20.19.13",
2432
  "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.13.tgz",
@@ -3239,6 +3250,12 @@
3239
  "toggle-selection": "^1.0.6"
3240
  }
3241
  },
 
 
 
 
 
 
3242
  "node_modules/cross-spawn": {
3243
  "version": "7.0.6",
3244
  "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -4085,6 +4102,12 @@
4085
  "node": ">= 4"
4086
  }
4087
  },
 
 
 
 
 
 
4088
  "node_modules/import-fresh": {
4089
  "version": "3.3.1",
4090
  "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
@@ -4112,6 +4135,12 @@
4112
  "node": ">=0.8.19"
4113
  }
4114
  },
 
 
 
 
 
 
4115
  "node_modules/inline-style-prefixer": {
4116
  "version": "7.0.1",
4117
  "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.1.tgz",
@@ -4158,6 +4187,12 @@
4158
  "node": ">=0.12.0"
4159
  }
4160
  },
 
 
 
 
 
 
4161
  "node_modules/isexe": {
4162
  "version": "2.0.0",
4163
  "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -4273,6 +4308,18 @@
4273
  "graceful-fs": "^4.1.6"
4274
  }
4275
  },
 
 
 
 
 
 
 
 
 
 
 
 
4276
  "node_modules/kareem": {
4277
  "version": "2.6.3",
4278
  "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz",
@@ -4306,6 +4353,15 @@
4306
  "node": ">= 0.8.0"
4307
  }
4308
  },
 
 
 
 
 
 
 
 
 
4309
  "node_modules/lightningcss": {
4310
  "version": "1.30.1",
4311
  "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz",
@@ -5086,6 +5142,12 @@
5086
  "url": "https://github.com/sponsors/sindresorhus"
5087
  }
5088
  },
 
 
 
 
 
 
5089
  "node_modules/parent-module": {
5090
  "version": "1.0.1",
5091
  "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -5176,6 +5238,12 @@
5176
  "node": ">= 0.8.0"
5177
  }
5178
  },
 
 
 
 
 
 
5179
  "node_modules/proxy-from-env": {
5180
  "version": "1.1.0",
5181
  "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@@ -5346,6 +5414,27 @@
5346
  "react-dom": "*"
5347
  }
5348
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5349
  "node_modules/require-from-string": {
5350
  "version": "2.0.2",
5351
  "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
@@ -5508,6 +5597,12 @@
5508
  "node": ">=6.9"
5509
  }
5510
  },
 
 
 
 
 
 
5511
  "node_modules/sharp": {
5512
  "version": "0.34.3",
5513
  "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz",
@@ -5703,6 +5798,21 @@
5703
  "node": ">=8.0"
5704
  }
5705
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5706
  "node_modules/strip-json-comments": {
5707
  "version": "3.1.1",
5708
  "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -6157,6 +6267,12 @@
6157
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
6158
  }
6159
  },
 
 
 
 
 
 
6160
  "node_modules/watchpack": {
6161
  "version": "2.4.4",
6162
  "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz",
 
27
  "@tanstack/eslint-plugin-query": "^5.86.0",
28
  "@tanstack/react-query": "^5.86.0",
29
  "@tanstack/react-query-devtools": "^5.86.0",
30
+ "@types/jszip": "^3.4.0",
31
  "axios": "^1.11.0",
32
  "class-variance-authority": "^0.7.1",
33
  "classnames": "^2.5.1",
34
  "clsx": "^2.1.1",
35
  "date-fns": "^4.1.0",
36
  "framer-motion": "^12.23.22",
37
+ "jszip": "^3.10.1",
38
  "log4js": "^6.9.1",
39
  "log4js-json-layout": "^2.2.3",
40
  "lucide-react": "^0.542.0",
 
2429
  "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
2430
  "license": "MIT"
2431
  },
2432
+ "node_modules/@types/jszip": {
2433
+ "version": "3.4.0",
2434
+ "resolved": "https://registry.npmjs.org/@types/jszip/-/jszip-3.4.0.tgz",
2435
+ "integrity": "sha512-GFHqtQQP3R4NNuvZH3hNCYD0NbyBZ42bkN7kO3NDrU/SnvIZWMS8Bp38XCsRKBT5BXvgm0y1zqpZWp/ZkRzBzg==",
2436
+ "license": "MIT",
2437
+ "dependencies": {
2438
+ "jszip": "*"
2439
+ }
2440
+ },
2441
  "node_modules/@types/node": {
2442
  "version": "20.19.13",
2443
  "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.13.tgz",
 
3250
  "toggle-selection": "^1.0.6"
3251
  }
3252
  },
3253
+ "node_modules/core-util-is": {
3254
+ "version": "1.0.3",
3255
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
3256
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
3257
+ "license": "MIT"
3258
+ },
3259
  "node_modules/cross-spawn": {
3260
  "version": "7.0.6",
3261
  "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
 
4102
  "node": ">= 4"
4103
  }
4104
  },
4105
+ "node_modules/immediate": {
4106
+ "version": "3.0.6",
4107
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
4108
+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
4109
+ "license": "MIT"
4110
+ },
4111
  "node_modules/import-fresh": {
4112
  "version": "3.3.1",
4113
  "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
 
4135
  "node": ">=0.8.19"
4136
  }
4137
  },
4138
+ "node_modules/inherits": {
4139
+ "version": "2.0.4",
4140
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
4141
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
4142
+ "license": "ISC"
4143
+ },
4144
  "node_modules/inline-style-prefixer": {
4145
  "version": "7.0.1",
4146
  "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.1.tgz",
 
4187
  "node": ">=0.12.0"
4188
  }
4189
  },
4190
+ "node_modules/isarray": {
4191
+ "version": "1.0.0",
4192
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
4193
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
4194
+ "license": "MIT"
4195
+ },
4196
  "node_modules/isexe": {
4197
  "version": "2.0.0",
4198
  "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
 
4308
  "graceful-fs": "^4.1.6"
4309
  }
4310
  },
4311
+ "node_modules/jszip": {
4312
+ "version": "3.10.1",
4313
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
4314
+ "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
4315
+ "license": "(MIT OR GPL-3.0-or-later)",
4316
+ "dependencies": {
4317
+ "lie": "~3.3.0",
4318
+ "pako": "~1.0.2",
4319
+ "readable-stream": "~2.3.6",
4320
+ "setimmediate": "^1.0.5"
4321
+ }
4322
+ },
4323
  "node_modules/kareem": {
4324
  "version": "2.6.3",
4325
  "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz",
 
4353
  "node": ">= 0.8.0"
4354
  }
4355
  },
4356
+ "node_modules/lie": {
4357
+ "version": "3.3.0",
4358
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
4359
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
4360
+ "license": "MIT",
4361
+ "dependencies": {
4362
+ "immediate": "~3.0.5"
4363
+ }
4364
+ },
4365
  "node_modules/lightningcss": {
4366
  "version": "1.30.1",
4367
  "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz",
 
5142
  "url": "https://github.com/sponsors/sindresorhus"
5143
  }
5144
  },
5145
+ "node_modules/pako": {
5146
+ "version": "1.0.11",
5147
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
5148
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
5149
+ "license": "(MIT AND Zlib)"
5150
+ },
5151
  "node_modules/parent-module": {
5152
  "version": "1.0.1",
5153
  "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
 
5238
  "node": ">= 0.8.0"
5239
  }
5240
  },
5241
+ "node_modules/process-nextick-args": {
5242
+ "version": "2.0.1",
5243
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
5244
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
5245
+ "license": "MIT"
5246
+ },
5247
  "node_modules/proxy-from-env": {
5248
  "version": "1.1.0",
5249
  "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
 
5414
  "react-dom": "*"
5415
  }
5416
  },
5417
+ "node_modules/readable-stream": {
5418
+ "version": "2.3.8",
5419
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
5420
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
5421
+ "license": "MIT",
5422
+ "dependencies": {
5423
+ "core-util-is": "~1.0.0",
5424
+ "inherits": "~2.0.3",
5425
+ "isarray": "~1.0.0",
5426
+ "process-nextick-args": "~2.0.0",
5427
+ "safe-buffer": "~5.1.1",
5428
+ "string_decoder": "~1.1.1",
5429
+ "util-deprecate": "~1.0.1"
5430
+ }
5431
+ },
5432
+ "node_modules/readable-stream/node_modules/safe-buffer": {
5433
+ "version": "5.1.2",
5434
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
5435
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
5436
+ "license": "MIT"
5437
+ },
5438
  "node_modules/require-from-string": {
5439
  "version": "2.0.2",
5440
  "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
 
5597
  "node": ">=6.9"
5598
  }
5599
  },
5600
+ "node_modules/setimmediate": {
5601
+ "version": "1.0.5",
5602
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
5603
+ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
5604
+ "license": "MIT"
5605
+ },
5606
  "node_modules/sharp": {
5607
  "version": "0.34.3",
5608
  "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz",
 
5798
  "node": ">=8.0"
5799
  }
5800
  },
5801
+ "node_modules/string_decoder": {
5802
+ "version": "1.1.1",
5803
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
5804
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
5805
+ "license": "MIT",
5806
+ "dependencies": {
5807
+ "safe-buffer": "~5.1.0"
5808
+ }
5809
+ },
5810
+ "node_modules/string_decoder/node_modules/safe-buffer": {
5811
+ "version": "5.1.2",
5812
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
5813
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
5814
+ "license": "MIT"
5815
+ },
5816
  "node_modules/strip-json-comments": {
5817
  "version": "3.1.1",
5818
  "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
 
6267
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
6268
  }
6269
  },
6270
+ "node_modules/util-deprecate": {
6271
+ "version": "1.0.2",
6272
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
6273
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
6274
+ "license": "MIT"
6275
+ },
6276
  "node_modules/watchpack": {
6277
  "version": "2.4.4",
6278
  "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz",
package.json CHANGED
@@ -27,12 +27,14 @@
27
  "@tanstack/eslint-plugin-query": "^5.86.0",
28
  "@tanstack/react-query": "^5.86.0",
29
  "@tanstack/react-query-devtools": "^5.86.0",
 
30
  "axios": "^1.11.0",
31
  "class-variance-authority": "^0.7.1",
32
  "classnames": "^2.5.1",
33
  "clsx": "^2.1.1",
34
  "date-fns": "^4.1.0",
35
  "framer-motion": "^12.23.22",
 
36
  "log4js": "^6.9.1",
37
  "log4js-json-layout": "^2.2.3",
38
  "lucide-react": "^0.542.0",
 
27
  "@tanstack/eslint-plugin-query": "^5.86.0",
28
  "@tanstack/react-query": "^5.86.0",
29
  "@tanstack/react-query-devtools": "^5.86.0",
30
+ "@types/jszip": "^3.4.0",
31
  "axios": "^1.11.0",
32
  "class-variance-authority": "^0.7.1",
33
  "classnames": "^2.5.1",
34
  "clsx": "^2.1.1",
35
  "date-fns": "^4.1.0",
36
  "framer-motion": "^12.23.22",
37
+ "jszip": "^3.10.1",
38
  "log4js": "^6.9.1",
39
  "log4js-json-layout": "^2.2.3",
40
  "lucide-react": "^0.542.0",