diff --git a/UnicadoGUI/Frontend/package-lock.json b/UnicadoGUI/Frontend/package-lock.json index b61a62dfb623a39504399af85db6b03a59e72e21..a576d6f81054845b41e24a8a59bf3022e9a83253 100644 --- a/UnicadoGUI/Frontend/package-lock.json +++ b/UnicadoGUI/Frontend/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@rollup/plugin-dsv": "^3.0.2", "@sveltestrap/sveltestrap": "^6.2.4", + "jszip": "^3.10.1", "layercake": "^8.0.2", "svelte-chartjs": "^3.1.5", "svelte-drag-and-drop-actions": "^1.0.2", @@ -999,6 +1000,12 @@ "node": ">= 0.6" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, "node_modules/css-tree": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", @@ -1356,6 +1363,12 @@ "node": ">=0.10.0" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -1395,8 +1408,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/internmap": { "version": "2.0.3", @@ -1456,11 +1468,29 @@ "@types/estree": "*" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, "node_modules/javascript-interface-library": { "version": "0.1.14", "resolved": "https://registry.npmjs.org/javascript-interface-library/-/javascript-interface-library-0.1.14.tgz", "integrity": "sha512-TSdQbLKqq6PcFk9M9Qk+l45rjqWfTMhSkvZVcByy1tKsDYDH+kblKu/FcVk4lWkeYXvcczX8NtPGdeyrZYEBrw==" }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, "node_modules/kleur": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", @@ -1485,6 +1515,15 @@ "typescript": "^5.0.2" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/locally-unique-id-generator": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/locally-unique-id-generator/-/locally-unique-id-generator-0.1.5.tgz", @@ -1635,6 +1674,12 @@ "wrappy": "1" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -1711,6 +1756,12 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -1731,6 +1782,21 @@ } ] }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -1846,6 +1912,12 @@ "node": ">=6" } }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -1869,6 +1941,12 @@ "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==", "dev": true }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/sirv": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", @@ -1906,6 +1984,15 @@ "node": ">=0.10.0" } }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -2165,6 +2252,12 @@ "node": ">=14.17" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/vite": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.1.tgz", diff --git a/UnicadoGUI/Frontend/package.json b/UnicadoGUI/Frontend/package.json index af5e6bb049aa64430eff081859c084e7b8dc7d89..68b770077f79eaf0c22c107feafd2e5a2ec26d76 100644 --- a/UnicadoGUI/Frontend/package.json +++ b/UnicadoGUI/Frontend/package.json @@ -23,6 +23,7 @@ "dependencies": { "@rollup/plugin-dsv": "^3.0.2", "@sveltestrap/sveltestrap": "^6.2.4", + "jszip": "^3.10.1", "layercake": "^8.0.2", "svelte-chartjs": "^3.1.5", "svelte-drag-and-drop-actions": "^1.0.2", diff --git a/UnicadoGUI/Frontend/src/routes/+page.svelte b/UnicadoGUI/Frontend/src/routes/+page.svelte index c5009e661a43860595f92adc42cdc878df6906ba..16c901d2d9f9f4c5fdf5a1ebac747668d37a47cd 100644 --- a/UnicadoGUI/Frontend/src/routes/+page.svelte +++ b/UnicadoGUI/Frontend/src/routes/+page.svelte @@ -1,6 +1,6 @@ <script> import { Button, Styles, Icon, Modal, ModalHeader, ModalBody, ModalFooter} from "@sveltestrap/sveltestrap"; - import { addProject, removeProject } from '../stores/projects'; + import { addProject, removeProject, saveProjectsAsZip, loadProjectsFromZip } from '../stores/projects'; let projectName = ''; let projectToRemove = ''; let showModal = false; @@ -9,6 +9,7 @@ if (projectName.trim() !== '') { addProject(projectName); projectName = ''; + closeModal() } } @@ -26,38 +27,59 @@ function closeModal() { showModal = false; } + const handleSave = async () => { + try { + await saveProjectsAsZip(); + console.log("Projekte wurden erfolgreich gespeichert."); + } catch (error) { + console.error("Fehler beim Speichern der Projekte:", error); + } + } + + function handleFileUpload(/** @type any */event) { + const selectedFile = event.target.files[0]; + if (selectedFile) { + loadProjectsFromZip(selectedFile); + } + } + function triggerFileInput() { + const fileInput = document.getElementById('file-input'); + if (fileInput) { + fileInput.click(); + } else { + console.error('Datei-Input-Element nicht gefunden.'); + } + } </script> <h1>Welcome to the UNICADO WebApp</h1> -<input - type="text" - bind:value={projectName} - placeholder="Enter project name..." -/> -<Button color="dark" outline on:click={newProject}> + +<Button color="dark" outline on:click={openModal}> <Icon name="window-plus"/> - new Project + New Project </Button> -<input - type="text" - bind:value={projectToRemove} - placeholder="Enter project name to remove..." -/> + <Button color="dark" outline on:click={deleteProject}> <Icon name="window-plus"/> Remove Project </Button> -<Button color="dark" outline on:click={openModal}> - <Icon name="window-plus"/> - New Project +<Button on:click={handleSave}> + <Icon name="cloud-download"/> + Projekte als ZIP speichern +</Button> + +<input type="file" accept=".zip" id="file-input" on:change={handleFileUpload} style="display: none;" /> + +<Button on:click={triggerFileInput}> + <Icon name="cloud-upload"/> + Project Liste von ZIP-Datei </Button> {#if showModal} <Modal isOpen={showModal} toggle={closeModal}> <ModalHeader toggle={closeModal}>Add New Project</ModalHeader> <ModalBody> - <!-- Eingabefeld für neuen Projektnamen --> <input type="text" bind:value={projectName} diff --git a/UnicadoGUI/Frontend/src/stores/projects.js b/UnicadoGUI/Frontend/src/stores/projects.js index 4acbc14699451b2482bb3ad60ace71773ba8a9af..02caf454630b0432b3474d92ac98566a9dfb00e1 100644 --- a/UnicadoGUI/Frontend/src/stores/projects.js +++ b/UnicadoGUI/Frontend/src/stores/projects.js @@ -1,4 +1,6 @@ import { writable } from 'svelte/store'; +import JSZip from 'jszip'; + //TODO project url and so function getStoredProjects() { @@ -49,3 +51,76 @@ export function removeProjectbyId(id) { return projects.filter((/** @type {{ id: number; }} */ project) => project.id !== id); }); } + + +/** + * @typedef {Object} Project + * @property {string} name - Der Name des Projekts + * @property {number} id - Die Projektid + */ + +/** + * Saves to Projects to a zip file + * @returns {Promise<void>} + */ +export const saveProjectsAsZip = async ()=> { + const zip = new JSZip(); + + /** @type {Project[]} */ + let projectArray = []; + userProjects.subscribe(value => { + projectArray = value; + })(); + + projectArray.forEach((project) => { + const folder = zip.folder(project.name); + + if (folder) { + const fileContent = JSON.stringify({ id : project.id}, null, 2); + folder.file("data.json", fileContent); + } else { + console.error(`Could not create folder for project: ${project.name}`); + } + }); + + const blob = await zip.generateAsync({ type: "blob" }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = 'projects.zip'; + a.click(); + URL.revokeObjectURL(url); + }; + + export const loadProjectsFromZip = (/** @type {File} */ file) => { + const zip = new JSZip(); + zip.loadAsync(file).then(async (zipContent) => { + /** @type {Project[]} */ + const projectArray = []; + /** @type {any[]} */ + const promises = []; + zipContent.forEach(async (relativePath, file) => { + const folderName = relativePath.split("/")[0]; + if (file.dir) return; + + const fileName = file.name.split("/")[1]; + if (fileName === "data.json") { + const promise = file.async("text").then(content => { + try { + const projectData = JSON.parse(content); + projectArray.push({ name: folderName, id: projectData.id }); + } catch (error) { + console.error(`Fehler beim Verarbeiten von ${folderName}:`, error); + } + }); + promises.push(promise); // Füge das Promise zum Array hinzu + } + }); + await Promise.all(promises); + userProjects.set(projectArray); + alert("Projekte erfolgreich geladen!"); + }).catch(error => { + alert("Fehler beim Laden des ZIP-Archivs."); + console.error(error); + }); + }; \ No newline at end of file