diff --git a/UC/src/db/backup_schemas.json b/UC/src/db/backup_schemas.json index aa876e7..9e26dfe 100644 --- a/UC/src/db/backup_schemas.json +++ b/UC/src/db/backup_schemas.json @@ -1,7 +1 @@ -[ - { - "id": "5ad08e32-9d5a-4054-b96f-ba361c02f30a", - "dir_config": "{dir_1: {}}", - "total_space": 0.1 - } -] \ No newline at end of file +{} \ No newline at end of file diff --git a/UC/src/db/config.json b/UC/src/db/config.json index f5952d4..5ed633e 100644 --- a/UC/src/db/config.json +++ b/UC/src/db/config.json @@ -1,4 +1,4 @@ { "adminKey": "590dceb42261808e4a804607b38eb119a09c93d84554bac0d23d705d5c6e8ca5", - "ceoID": "5ad08e32-9d5a-4054-b96f-ba361c02f30a" + "ceoID": "a7635c7a-d6a0-43dc-8e24-552ab33239b8" } \ No newline at end of file diff --git a/UC/src/db/departments.json b/UC/src/db/departments.json index 53fe240..8c847c1 100644 --- a/UC/src/db/departments.json +++ b/UC/src/db/departments.json @@ -1,7 +1,7 @@ [ { - "id": "6cd67946-6fe7-46df-9740-25eea7b95f69", + "id": "f1444987-c0c2-4ce8-89e3-b21f302bff7f", "name": "Programatori", - "key": "a2bb3329528704715e29c27fd778892d177bcacfd73e38bde818aef560f4d549" + "key": "52a50f932fabf2ba64e337664051804a4bf1e1dcadc548cfe85be92b8c1d470e" } ] \ No newline at end of file diff --git a/UC/src/db/security_levels.json b/UC/src/db/security_levels.json index e89a01d..64c4767 100644 --- a/UC/src/db/security_levels.json +++ b/UC/src/db/security_levels.json @@ -1,3 +1,3 @@ { - "1": "6cd67946-6fe7-46df-9740-25eea7b95f69" + "1": "f1444987-c0c2-4ce8-89e3-b21f302bff7f" } \ No newline at end of file diff --git a/UC/src/db/users.json b/UC/src/db/users.json index 071714a..f9240b8 100644 --- a/UC/src/db/users.json +++ b/UC/src/db/users.json @@ -1,8 +1,14 @@ [ { - "id": "5ad08e32-9d5a-4054-b96f-ba361c02f30a", - "name": "Andrei Cerbu", - "email": "a@c.com", - "password": "4dd45a4455b1db1fd9c204a6d7c26f30" + "id": "a7635c7a-d6a0-43dc-8e24-552ab33239b8", + "name": "CEO", + "email": "ceo@yourfirm.com", + "password": "405ee6885be5385223830874bd6dcfca" + }, + { + "id": "a48e1913-01f5-4df3-a2ba-476e9baa39e4", + "name": "Andrei", + "email": "a@b.com", + "password": "5447045dc3cfaafdb49b365e31dad41eb88616b0b1449930e55fd3a70bd8cee4" } ] \ No newline at end of file diff --git a/UC/src/exceptions/emailAlreadyInSystemError.js b/UC/src/exceptions/emailAlreadyInSystemError.js new file mode 100644 index 0000000..b3f5af9 --- /dev/null +++ b/UC/src/exceptions/emailAlreadyInSystemError.js @@ -0,0 +1,8 @@ +class EmailAlreadyInSystemError extends Error { + constructor(message = "Email is already in system!") { + super(message); + this.name = 'EmailAlreadyInSystem'; + } +} + +module.exports = EmailAlreadyInSystemError \ No newline at end of file diff --git a/UC/src/models/emailVerificationModel.js b/UC/src/models/emailVerificationModel.js new file mode 100644 index 0000000..d93342f --- /dev/null +++ b/UC/src/models/emailVerificationModel.js @@ -0,0 +1,7 @@ +const Joi = require('joi'); + +const emailVerificationModel = Joi.object({ + email: Joi.string().email().required() +}); + +module.exports = emailVerificationModel; \ No newline at end of file diff --git a/UC/src/models/schemaMapper.js b/UC/src/models/schemaMapper.js index c0adc74..067bfb1 100644 --- a/UC/src/models/schemaMapper.js +++ b/UC/src/models/schemaMapper.js @@ -1,15 +1,19 @@ const usersRegisterModelSchema = require('./usersRegisterModel'); const usersLoginModelSchema = require('./usersLoginModel'); +const usersModifyModelSchema = require('./usersModifyModel'); const dirStructureModelSchema = require('./dirStructureModel'); const departmentRegisterModelSchema = require('./departmentRegisterModel'); -const ceoRegisterModelSchema = require('./ceoRegisterModel') +const ceoRegisterModelSchema = require('./ceoRegisterModel'); +const emailVerificationSchema = require('./emailVerificationModel'); module.exports = { schemas: { dirStructureModelSchema, usersRegisterModelSchema, usersLoginModelSchema, + usersModifyModelSchema, departmentRegisterModelSchema, - ceoRegisterModelSchema + ceoRegisterModelSchema, + emailVerificationSchema }, }; \ No newline at end of file diff --git a/UC/src/models/usersModifyModel.js b/UC/src/models/usersModifyModel.js new file mode 100644 index 0000000..cbcb5d2 --- /dev/null +++ b/UC/src/models/usersModifyModel.js @@ -0,0 +1,9 @@ +const Joi = require('joi'); + +const usersModifyModel = Joi.object({ + name: Joi.string().required(), + email: Joi.string().email().required(), + password: Joi.string().min(8).max(20).required() +}); + +module.exports = usersModifyModel; \ No newline at end of file diff --git a/UC/src/models/usersRegisterModel.js b/UC/src/models/usersRegisterModel.js index 3321593..29d13c8 100644 --- a/UC/src/models/usersRegisterModel.js +++ b/UC/src/models/usersRegisterModel.js @@ -4,7 +4,7 @@ const usersRegisterModel = Joi.object({ name: Joi.string().required(), email: Joi.string().email().required(), password: Joi.string().min(8).max(20).required(), - department: Joi.string().required() + department: Joi.string().uuid().required() }); module.exports = usersRegisterModel; \ No newline at end of file diff --git a/UC/src/routes/users.js b/UC/src/routes/users.js index 720e62e..c6dee74 100644 --- a/UC/src/routes/users.js +++ b/UC/src/routes/users.js @@ -1,7 +1,7 @@ const express = require('express'); const fs = require('fs'); const path = require('path'); -const { v4: uuidv4 } = require('uuid'); +const { v4: uuidv4, validate} = require('uuid'); const crypto = require('crypto'); const { sendResponse } = require('../helpers/responseHelper'); @@ -11,6 +11,8 @@ const {schemas} = require("../models/schemaMapper"); const UserAlreadyExistsException =require('../exceptions/userAlreadyExistsError'); const UserNotExistingError = require("../exceptions/userNotExistingError"); const ImproperDirStructureError = require("../exceptions/improperDirStructureError"); +const EmailAlreadyInSystemError = require("../exceptions/emailAlreadyInSystemError"); +const {HttpStatusCode} = require("axios"); const router = express.Router(); @@ -99,7 +101,13 @@ router.post('/login', validateLoginBody, async (req, res) => { throw new UserNotExistingError('Invalid email or password'); } - return sendResponse(res, httpStatus.OK, 'Login successful'); + return sendResponse(res, httpStatus.OK, { + id: user.id, + name: user.name, + email: email, + password: password, + department: user.department + }); } catch (error) { console.error(`Error: ${error.message}`); @@ -111,6 +119,37 @@ router.post('/login', validateLoginBody, async (req, res) => { } }); +const validateEmailValidationBody = (req, res, next) => { + const { error, value } = schemas.emailVerificationSchema.validate(req.body); + if (error) { + return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message); + } + req.jsonModel = value; + next(); +} + +router.post('/validate_email', validateEmailValidationBody, (req, res) => { + try { + const {email} = req.jsonModel; + const usersFilePath = path.join(__dirname, '..', 'db', 'users.json'); + const usersData = fs.readFileSync(usersFilePath, 'utf8'); + const usersJson = JSON.parse(usersData); + + if (usersJson.some(user => user.email === email)) { + throw new EmailAlreadyInSystemError(); + } + + return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]); + }catch(error){ + console.error(`Error: ${error.message}`); + if (error instanceof EmailAlreadyInSystemError) { + return sendResponse(res, httpStatus.CONFLICT, error.message); + } + return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]); + } +}); + + const validateDirStructureBody = (req, res, next) => { const { error, value } = schemas.dirStructureModelSchema.validate(req.body); @@ -192,6 +231,61 @@ router.delete('/:id', async (req, res) => { } }); +const validateModifyUserBody = (req, res, next) => { + const { error, value } = schemas.usersModifyModelSchema.validate(req.body); + if (error) { + return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message); + } + req.jsonModel = value; + next(); +}; + +router.put('/:id', validateModifyUserBody, async (req, res) => { + const id = req.params.id; + + try { + const usersFilePath = path.join(__dirname, '..', 'db', 'users.json'); + const usersData = fs.readFileSync(usersFilePath, 'utf8'); + const usersJson = JSON.parse(usersData); + + const {name, email, password} = req.jsonModel; + + if (usersJson.some(user => user.email === email && user.id !== id)) { + throw new EmailAlreadyInSystemError(); + } + + let index = usersJson.findIndex(user => user.id === id); + if (index === -1) { + throw new UserNotExistingError(); + } + + const hashedPassword = crypto.createHash('sha256').update(password).digest('hex'); + + usersJson[index] = { + id: id, + name: name, + email: email, + password: hashedPassword + } + + fs.writeFileSync(usersFilePath, JSON.stringify(usersJson, null, 2)); + + return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]); + } catch (error) { + console.error(`Error: ${error.message}`); + + if (error instanceof EmailAlreadyInSystemError){ + return sendResponse(res, httpStatus.CONFLICT, error.message); + } + + if (error instanceof UserNotExistingError) { + return sendResponse(res, httpStatus.NOT_FOUND, error.message); + } + + return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]); + } +}); + router.use((req, res) => { return sendResponse(res, httpStatus.NOT_FOUND, "Path not found"); diff --git a/User/dirBackup.json b/User/dirBackup.json new file mode 100644 index 0000000..1ca8856 --- /dev/null +++ b/User/dirBackup.json @@ -0,0 +1,4 @@ +{ + "path": "C:\\Users\\Andrei Cerbu\\Documents\\Adobe\\Adobe Media Encoder\\24.0\\ArchivedWorkspaces", + "structure": {} +} \ No newline at end of file diff --git a/User/loginData.json b/User/loginData.json new file mode 100644 index 0000000..10a50c7 --- /dev/null +++ b/User/loginData.json @@ -0,0 +1,6 @@ +{ + "id": "a48e1913-01f5-4df3-a2ba-476e9baa39e4", + "name": "Andrei", + "email": "a@b.com", + "password": "andreicerbu" +} \ No newline at end of file diff --git a/User/package.json b/User/package.json index b57bb60..195543c 100644 --- a/User/package.json +++ b/User/package.json @@ -5,8 +5,8 @@ "description": "Aplicatie P2P pentru stocarea resurselor digitale", "main": "src/main/main.js", "scripts": { - "start": "electron ./src/main/main.js", - "dev": "electronmon ./src/main/main.js" + "start": "electron --trace-warnings ./src/main/main.js", + "dev": "electronmon --trace-warnings ./src/main/main.js" }, "author": "Cerbu Andrei - Mihnea", "license": "ISC", diff --git a/User/src/main/main.js b/User/src/main/main.js index ee93852..974a2a3 100644 --- a/User/src/main/main.js +++ b/User/src/main/main.js @@ -1,34 +1,191 @@ -const {app, BrowserWindow, screen, Menu} = require("electron"); -const path = require("path") +const { app, BrowserWindow, screen, ipcMain, dialog } = require('electron'); +const fs = require('fs'); +const path = require('path'); const isMac = process.platform === 'darwin'; +let html_page = undefined; +let mainWindow = undefined; +let alertWindow = undefined; const createMainWindow = ((title, width, height) => { - const mainWindows = new BrowserWindow({ + mainWindow = new BrowserWindow({ title: title, width: width, - height: height + height: height, + resizable: false, + icon: path.join(__dirname, '..', 'renderer', 'assets', 'hard-disk.png'), + webPreferences: { + preload: path.join(__dirname, 'preload.js') + } }); - mainWindows.loadFile(path.join(__dirname, '..', 'renderer', 'index.html')) - .then(r => console.log('Main window works!')); -}) + html_page = 'login.html'; + //mainWindow.setMenu(null); + mainWindow.loadFile(path.join(__dirname, '..', 'renderer', 'html', 'login.html')) + .then(() => { + console.log('Main window loaded!') + }) + .catch(err => console.error('Failed to load main window:', err)); +}); + +const createAlertWindow = (title, width, height) => { + alertWindow = new BrowserWindow({ + width: width, + height: height, + title: title, + icon: path.join(__dirname, '..', 'renderer', 'assets', 'hard-disk.png'), + resizable: false, + webPreferences: { + nodeIntegration: false, + preload: path.join(__dirname, 'preload.js') + } + }); + + //alertWindow.setMenu(null); + + alertWindow.loadFile(path.join(__dirname, '..', 'renderer', 'html', 'alert_modal.html')).then(() => { + console.log('Alert window loaded!') + }) + .catch(err => console.error('Failed to load alert window:', err)); + + alertWindow.on('closed', () => { + alertWindow = undefined; + }); +} + +function showAlert(message) { + if (alertWindow === undefined) { + const title = 'Alert'; + const mainScreen = screen.getPrimaryDisplay(); + const { width, height } = mainScreen.size; + createAlertWindow(title, width/4, height/4); + } + + alertWindow.webContents.once('dom-ready', () => { + alertWindow.webContents.executeJavaScript(`showAlert("${message}")`); + }); +} app.whenReady().then(() => { - const title = "Application" + const title = "Application"; const mainScreen = screen.getPrimaryDisplay(); - const {width: width, height: height} = mainScreen.size; - createMainWindow(title, width / 1.5, height / 1.5) + const { width, height } = mainScreen.size; + createMainWindow(title, width / 1.5, height / 1.5); app.on('activate', () => { if(BrowserWindow.getAllWindows().length === 0){ createMainWindow(title, width, height); } - }) -}) + }); +}); -app.on('window-all-closed', () =>{ +app.on('window-all-closed', () => { if(!isMac){ app.quit(); } -}) \ No newline at end of file +}); + +ipcMain.handle('write-file', async (event, fileName, content) => { + try { + let filePath = path.join(__dirname, '..', '..', fileName); + await fs.promises.writeFile(filePath, content); + console.log(`File successfully written to ${filePath}`); + return { success: true }; + } catch (error) { + console.error('Failed to write file:', error); + return { success: false, error: error.message }; + } +}); + +ipcMain.handle('delete-file', async (event, fileName) => { + try { + let filePath = path.join(__dirname, '..', '..', fileName); + console.log(filePath); + await fs.promises.unlink(filePath); + console.log(`File ${filePath} successfully deleted`); + return { success: true }; + } catch (error) { + console.error('Failed to delete file:', error); + return { success: false, error: error.message }; + } +}); + +ipcMain.handle('read-file', async (event, fileName) => { + try { + let filePath = path.join(__dirname, '..', '..', fileName); + + const content = await fs.promises.readFile(filePath, 'utf-8'); + return { success: true, content }; + } catch (error) { + console.error('Error reading file:', error); + return { success: false, error: error.message }; + } +}); + +ipcMain.handle('change-content', async (event, nextPage) => { + try { + html_page = nextPage; + mainWindow.webContents.executeJavaScript(` + document.body.classList.add('fade-out'); + `); + + await new Promise(resolve => setTimeout(resolve, 1000)); + await mainWindow.loadFile(path.join(__dirname, '..', 'renderer', 'html', nextPage)); + + mainWindow.webContents.executeJavaScript(` + document.body.classList.add('fade-in'); + `); + return true; + } catch (error) { + console.error('Error changing content:', error); + return false; + } +}); + +ipcMain.handle('open-backup-dir-dialog', async (event) => { + try { + const result = await dialog.showOpenDialog({ + properties: ['openDirectory'], + }); + + if (result.canceled || result.filePaths.length === 0) { + return {canceled: true} + } + + const dirPath = result.filePaths[0]; + await fs.promises.writeFile( + path.join(__dirname, '..', '..', 'dirBackup.json'), + JSON.stringify({ + path: dirPath, + structure: {} + }, null, 2)); + + return true; + } catch (error) { + console.error('Error opening file dialog:', error); + return { error: error.message }; + } +}); + +ipcMain.handle('check-file-exists', async (event, fileName) => { + try { + const filePath = path.join(__dirname, '..', '..', fileName); + return await fs.promises.access(filePath) + .then(() => true) + .catch(() => false); + } catch (error) { + console.error('Error checking file existence:', error); + throw error; // Propagate the error to the renderer process + } +}); + +ipcMain.handle('show-alert', async (event, message) =>{ + showAlert(message); +}); + +ipcMain.on('close-alert-window', () => { + if (alertWindow) { + alertWindow.close(); + alertWindow = undefined; + } +}); diff --git a/User/src/main/preload.js b/User/src/main/preload.js new file mode 100644 index 0000000..eacfecc --- /dev/null +++ b/User/src/main/preload.js @@ -0,0 +1,12 @@ +const { contextBridge, ipcRenderer } = require('electron'); + +contextBridge.exposeInMainWorld('electronAPI', { + writeFile: (fileName, content) => ipcRenderer.invoke('write-file', fileName, content), + readFile: (fileName) => ipcRenderer.invoke('read-file', fileName), + deleteFile: (fileName) => ipcRenderer.invoke('delete-file', fileName), + changeContent: (nextPage) => ipcRenderer.invoke('change-content', nextPage), + showAlert: (message) => ipcRenderer.invoke('show-alert', message), + closeAlertWindow: () => ipcRenderer.send('close-alert-window'), + openBackupDirDialog: () => ipcRenderer.invoke('open-backup-dir-dialog'), + checkFileExists: (fileName) => ipcRenderer.invoke('check-file-exists', fileName) +}); \ No newline at end of file diff --git a/User/src/renderer/css/alert_modal.css b/User/src/renderer/css/alert_modal.css new file mode 100644 index 0000000..7b2604c --- /dev/null +++ b/User/src/renderer/css/alert_modal.css @@ -0,0 +1,71 @@ +body, html { + height: 100%; + width: 100%; + margin: 0; + padding: 0; + + font-family: "Roboto", sans-serif; + font-weight: 500; + font-style: normal; + + background-image: url('../assets/background.png'); + background-size: cover; + background-repeat: no-repeat; + +} + +.container { + display: flex; + flex-direction: column; + justify-content: space-evenly; + align-items: center; + min-height: 100vh; +} + +h1{ + margin: 0; + padding: 0; +} + +.main_component{ + display: flex; + flex-wrap: wrap; + flex-direction: column; + justify-content: center; + align-items: center; + + background-color: #535C91; + opacity: 71; + padding: 2vh; + border-radius: 1rem; + box-shadow: 10px 10px 10px rgba(0, 0, 0, 0.9); + width: 90vw; + height: 80vh; +} + +.header{ + color: #1B1A55; + font-size: 0.8rem; + text-align: center; + text-transform: uppercase; +} + +button { + font-weight: bold; + width: 25%; + font-size: 1rem; + padding: 10px; + border: none; + border-radius: 5px; + margin-top: 20px; + cursor: pointer; +} + +button:hover{ + filter: brightness(85%); +} + +button[name="close"] { + background-color: #2196F3; + color: white; +} diff --git a/User/src/renderer/css/login.css b/User/src/renderer/css/login.css index e0b03a0..3e04a36 100644 --- a/User/src/renderer/css/login.css +++ b/User/src/renderer/css/login.css @@ -30,7 +30,7 @@ body, html { margin-bottom: 10rem; } -.signup-form { +.login-form { background-color: #535C91; opacity: 71; padding: 9vh 3vh 5vh; @@ -39,27 +39,26 @@ body, html { width: 25%; } -.signup-form-title{ +.login-form-title{ margin: 0 0 5vh 0; text-align: center; } -.signup-form-title h2{ +.login-form-title h2{ color: #FFFFFF; font-size: 5vh; margin: 0; padding: 0; } -.signup-form hr{ +.login-form hr{ width: 40%; } -.signup-form-content{ +.login-form-content{ display: flex; - flex-direction: column; - align-content: center; flex-wrap: wrap; + justify-content: center; } input { @@ -76,7 +75,7 @@ input { button { font-weight: bold; - width: 40%; + width: 35%; font-size: 1rem; padding: 10px; border: none; @@ -89,14 +88,21 @@ button:hover{ filter: brightness(85%); } -.signup-form-footer{ +.login-form-footer{ margin-top: 3vh; display: flex; + flex-wrap: wrap; align-content: center; - justify-content: center; + align-items: center; + justify-content: space-evenly; } -.signup-form button[name="submit"] { +button[name="submit"] { background-color: #F44336; color: white; +} + +button[name="signin"] { + background-color: #2196F3; + color: white; } \ No newline at end of file diff --git a/User/src/renderer/css/main_menu.css b/User/src/renderer/css/main_menu.css index 90d65e3..259ac29 100644 --- a/User/src/renderer/css/main_menu.css +++ b/User/src/renderer/css/main_menu.css @@ -14,6 +14,76 @@ body, html { } +.overlay { + display: none; + position: fixed; + width: 100%; + height: 100%; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.85); + z-index: 2; + cursor: pointer; +} + +.ceo-validation-form { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 300px; + padding: 20px; + background: #1B1A55; + border-radius: 10px; + cursor: default; +} + +.ceo-validation-form h2 { + text-align: center; + color: white; +} + +.form-actions { + text-align: center; + padding-top: 20px; +} + +.form-actions button { + padding: 10px 20px; + margin: 0 10px; + border: none; + border-radius: 5px; + cursor: pointer; +} + +input { + text-align: center; + color: white; + font-weight: bold; + font-size: 1.2rem; + width: 80%; + padding: 1rem; + margin: 0.7rem; + background-color: #1B1A55; + border-radius: 10px; +} + +#back_button { + background-color: #f44336; + width: 35%; + height: auto; + color: white; +} + +#submit_button { + background-color: #4CAF50; + width: 35%; + height: auto; + color: white; +} + .container { display: flex; flex-direction: row; @@ -104,6 +174,38 @@ button:hover{ color: white; } +.notifications{ + display: flex; + margin: 1rem 2rem 2rem 3rem; + flex-direction: column; + align-items: start; + + height: 40vh; /* Fixed height */ + width: 80%; /* Full width */ + overflow: auto; /* Enable scrolling */ + + font-size: 1.2rem; + color: white; + font-weight: bold; +} + +.notifications::-webkit-scrollbar { + width: 10px; /* Reduced width of the scrollbar by 2px */ +} + +.notifications::-webkit-scrollbar-track { + background: #1B1A55; /* Updated track color */ +} + +.notifications::-webkit-scrollbar-thumb { + background: #535C91; /* Updated handle color */ + border: 2px solid #1B1A55; /* Adding a border to effectively reduce the thumb size */ +} + +.notifications::-webkit-scrollbar-thumb:hover { + background: #555; /* Updated handle color on hover */ +} + button[name="logout"]{ margin: 0; padding: 0; diff --git a/User/src/renderer/css/profile.css b/User/src/renderer/css/profile.css index 005d193..f030018 100644 --- a/User/src/renderer/css/profile.css +++ b/User/src/renderer/css/profile.css @@ -31,7 +31,7 @@ body, html { margin-bottom: 10rem; } -.signup-form { +.profile-form { background-color: #535C91; opacity: 71; padding: 13vh 3vh 5vh; @@ -40,11 +40,15 @@ body, html { width: 25%; } -.signup-form-title{ +.profile-form-title{ margin-bottom: 5vh; } -.signup-form-title h2 { +.profile-form hr{ + width: 40%; +} + +.profile-form-title h2 { color: #FFFFFF; margin: 0; padding: 0; @@ -52,42 +56,20 @@ body, html { font-size: 5vh; } -.signup-form button { - width: 100%; - padding: 10px; - border: none; - border-radius: 5px; - margin-top: 5px; - cursor: pointer; +.profile-form-content{ + display: flex; + flex-wrap: wrap; + justify-content: center; } -.signup-form-footer{ +.profile-form-footer{ margin-top: 7vh; display: flex; flex-direction: row; + justify-content: center; } -.signup-form button{ - font-weight: bold; - width: 40%; - font-size: 1rem; -} - -.signup-form button:hover{ - filter: brightness(85%); -} - -.signup-form hr{ - width: 40%; -} - -.signup-form button[name="login"] { - background-color: #2196F3; - color: white; - margin-right: 5rem; -} - -.signup-form input { +input { text-align: center; color: white; font-weight: bold; @@ -99,7 +81,28 @@ body, html { border-radius: 10px; } -.signup-form button[name="submit"] { +button { + font-weight: bold; + width: 35%; + font-size: 1rem; + padding: 10px; + border: none; + border-radius: 5px; + margin-top: 1rem; + cursor: pointer; +} + +button:hover{ + filter: brightness(85%); +} + +button[name="submit"] { background-color: #F44336; color: white; +} + +button[name="login"] { + background-color: #2196F3; + color: white; + margin-right: 5rem; } \ No newline at end of file diff --git a/User/src/renderer/css/share_file.css b/User/src/renderer/css/share_file.css index 6f32ccf..07344a4 100644 --- a/User/src/renderer/css/share_file.css +++ b/User/src/renderer/css/share_file.css @@ -126,7 +126,7 @@ button:hover{ color: white; } -.security_level_form_title{ +.choose_user_form_title{ display: flex; flex-wrap: wrap; flex-direction: column; @@ -134,11 +134,11 @@ button:hover{ align-content: start; } -.security_level_form_title hr{ +.choose_user_form_title hr{ width: 40%; } -.security_level_form_content{ +.choose_user_form_content{ display: flex; margin: 1rem 7rem 2rem 0.5rem; flex-direction: column; @@ -153,20 +153,20 @@ button:hover{ font-weight: bold; } -.security_level_form_content::-webkit-scrollbar { +.choose_user_form_content::-webkit-scrollbar { width: 10px; /* Reduced width of the scrollbar by 2px */ } -.security_level_form_content::-webkit-scrollbar-track { +.choose_user_form_content::-webkit-scrollbar-track { background: #1B1A55; /* Updated track color */ } -.security_level_form_content::-webkit-scrollbar-thumb { +.choose_user_form_content::-webkit-scrollbar-thumb { background: #535C91; /* Updated handle color */ border: 2px solid #1B1A55; /* Adding a border to effectively reduce the thumb size */ } -.security_level_form_content::-webkit-scrollbar-thumb:hover { +.choose_user_form_content::-webkit-scrollbar-thumb:hover { background: #555; /* Updated handle color on hover */ } diff --git a/User/src/renderer/css/set_departments.css b/User/src/renderer/css/sign_up_department.css similarity index 66% rename from User/src/renderer/css/set_departments.css rename to User/src/renderer/css/sign_up_department.css index fcba47b..f15f98a 100644 --- a/User/src/renderer/css/set_departments.css +++ b/User/src/renderer/css/sign_up_department.css @@ -62,26 +62,48 @@ input{ .signup-form-content{ display: flex; - margin-left: 2rem; + margin: 1rem 2rem 2rem 3rem; flex-direction: column; align-items: start; - font-size: 1.5rem; + + height: 20vh; /* Fixed height */ + width: 80%; /* Full width */ + overflow: auto; /* Enable scrolling */ + + font-size: 1.2rem; color: white; font-weight: bold; } +.signup-form-content::-webkit-scrollbar { + width: 10px; /* Reduced width of the scrollbar by 2px */ +} + +.signup-form-content::-webkit-scrollbar-track { + background: #1B1A55; /* Updated track color */ +} + +.signup-form-content::-webkit-scrollbar-thumb { + background: #535C91; /* Updated handle color */ + border: 2px solid #1B1A55; /* Adding a border to effectively reduce the thumb size */ +} + +.signup-form-content::-webkit-scrollbar-thumb:hover { + background: #555; /* Updated handle color on hover */ +} + .signup-form-footer{ margin-top: 5vh; display: flex; + flex-wrap: wrap; flex-direction: row; - justify-content: center; + justify-content: space-between; align-items: center; - align-content: space-between; } button { font-weight: bold; - width: 40%; + width: 30%; font-size: 1rem; padding: 10px; border: none; @@ -94,7 +116,7 @@ button:hover{ filter: brightness(85%); } -button[name="continue"] { +button[name="submit"] { background-color: #F44336; color: white; } diff --git a/User/src/renderer/css/sign_up_start.css b/User/src/renderer/css/sing_up_profile.css similarity index 95% rename from User/src/renderer/css/sign_up_start.css rename to User/src/renderer/css/sing_up_profile.css index 1b0cf20..43fdc42 100644 --- a/User/src/renderer/css/sign_up_start.css +++ b/User/src/renderer/css/sing_up_profile.css @@ -69,13 +69,12 @@ input { } .signup-form-footer{ + margin-top: 3vh; display: flex; - flex-direction: row; flex-wrap: wrap; - justify-content: space-between; align-content: center; align-items: center; - margin-top: 7vh; + justify-content: space-evenly; } button { diff --git a/User/src/renderer/css/transition.css b/User/src/renderer/css/transition.css new file mode 100644 index 0000000..442e6f8 --- /dev/null +++ b/User/src/renderer/css/transition.css @@ -0,0 +1,25 @@ +.fade-in { + animation: fadeInAnimation 0.5s ease-in forwards; +} + +.fade-out { + animation: fadeOutAnimation 0.5s ease-out forwards; +} + +@keyframes fadeInAnimation { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes fadeOutAnimation { + from { + opacity: 1; + } + to { + opacity: 0; + } +} diff --git a/User/src/renderer/html/alert_modal.html b/User/src/renderer/html/alert_modal.html new file mode 100644 index 0000000..340c97b --- /dev/null +++ b/User/src/renderer/html/alert_modal.html @@ -0,0 +1,21 @@ + + + + + + + + Alert Modal + + +
+
+
+ +

+
+ +
+
+ + diff --git a/User/src/renderer/html/change_department.html b/User/src/renderer/html/change_department.html index d96caf0..98f2b1d 100644 --- a/User/src/renderer/html/change_department.html +++ b/User/src/renderer/html/change_department.html @@ -19,6 +19,7 @@ + diff --git a/User/src/renderer/html/main_menu.html b/User/src/renderer/html/main_menu.html index f1c3b33..24e7c37 100644 --- a/User/src/renderer/html/main_menu.html +++ b/User/src/renderer/html/main_menu.html @@ -4,35 +4,50 @@ - Main Page + + + + + Main Page + +
+
+

CEO Authentication

+ +
+ + +
+
+

WELCOME BACK,

-

'username'!

+

Hope you have a productive day!

- - + +
- - + +
- +
@@ -40,16 +55,8 @@

NOTIFICATIONS


-
-
- -
-
- -
-
- -
+
+
diff --git a/User/src/renderer/html/profile.html b/User/src/renderer/html/profile.html index 274dd31..0684a8e 100644 --- a/User/src/renderer/html/profile.html +++ b/User/src/renderer/html/profile.html @@ -4,28 +4,31 @@ - Signup + + + + + + Profile
-
-
- -
- - - -
- -
-
+
+
+

Profile

+
+
+
+ + + +
+ +
diff --git a/User/src/renderer/html/share_file.html b/User/src/renderer/html/share_file.html index 9597f6e..83768d6 100644 --- a/User/src/renderer/html/share_file.html +++ b/User/src/renderer/html/share_file.html @@ -4,8 +4,8 @@ - Share File + Share File
diff --git a/User/src/renderer/html/sign_up_confirmation.html b/User/src/renderer/html/sign_up_confirmation.html index 051b042..0b34001 100644 --- a/User/src/renderer/html/sign_up_confirmation.html +++ b/User/src/renderer/html/sign_up_confirmation.html @@ -4,15 +4,17 @@ - Setup Completion + + + Setup Completion

ALL THE SETUP IS DONE!

LET’S PROCEED TO THE

-

MAIN PAGE

+

LOGIN PAGE

diff --git a/User/src/renderer/html/sign_up_departments.html b/User/src/renderer/html/sign_up_departments.html new file mode 100644 index 0000000..4d2f37b --- /dev/null +++ b/User/src/renderer/html/sign_up_departments.html @@ -0,0 +1,37 @@ + + + + + + + + + + + + + Department Selection + + +
+
+

TELL ME MORE

+

ABOUT

+

YOUR WORK

+
+ +
+ + diff --git a/User/src/renderer/html/sign_up_profile.html b/User/src/renderer/html/sign_up_profile.html index fd88854..6d23359 100644 --- a/User/src/renderer/html/sign_up_profile.html +++ b/User/src/renderer/html/sign_up_profile.html @@ -4,7 +4,9 @@ - + + + Signup @@ -13,23 +15,19 @@

LET US MEET

EACH OTHER

-
-
- -
- - - -
- -
-
- - - +
+
+

Sign Up

+
+
+
+ + + +
+ +
+ \ No newline at end of file diff --git a/User/src/renderer/html/sing_up_departments.html b/User/src/renderer/html/sing_up_departments.html deleted file mode 100644 index be9a6e2..0000000 --- a/User/src/renderer/html/sing_up_departments.html +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - Department Selection - - - -
-
-

TELL ME MORE

-

ABOUT

-

YOUR WORK

-
-
-
- - - -
-
-
- - diff --git a/User/src/renderer/js/alert_modal.js b/User/src/renderer/js/alert_modal.js new file mode 100644 index 0000000..0367509 --- /dev/null +++ b/User/src/renderer/js/alert_modal.js @@ -0,0 +1,11 @@ +document.addEventListener('DOMContentLoaded', () => { + const closeButton = document.getElementById('closeButton'); + closeButton.addEventListener('click', () => { + window.electronAPI.closeAlertWindow(); + }); +}); + +function showAlert(message) { + const modalMessage = document.getElementById('modal-message'); + modalMessage.textContent = message; +} diff --git a/User/src/renderer/js/login.js b/User/src/renderer/js/login.js new file mode 100644 index 0000000..a199849 --- /dev/null +++ b/User/src/renderer/js/login.js @@ -0,0 +1,89 @@ +document.addEventListener('DOMContentLoaded', async function () { + const signinButton = document.getElementById('signin'); + const submitButton = document.getElementById('submit'); + + await window.electronAPI.readFile('loginData.json') + .then(async result => { + const loginData = JSON.parse(result.content); + const { email, password } = loginData; + + const response = await fetch('http://localhost:5000/users/login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': 'uc_api' + }, + body: JSON.stringify({ + email: email, + password: password + }) + }); + + if(response.ok) { + window.electronAPI.changeContent('main_menu.html') + .then(() => console.log('Navigated to dashboard')) + .catch(error => console.error('Error navigating:', error)); + } + }).catch(async error => { + console.error('Can\'t read loginData'); + await window.electronAPI.deleteFile('loginData.json') + }); + + signinButton.addEventListener('click', function (e) { + window.electronAPI.changeContent('sign_up_profile.html') + .then(() => console.log('Content changed successfully')) + .catch(error => console.error('Error changing content:', error)); + }); + + submitButton.addEventListener('click', async function (e) { + try { + e.preventDefault(); + console.log('Submit button clicked'); + + const form = document.getElementById('loginForm'); + const formData = new FormData(form); + + const email = formData.get('email'); + const password = formData.get('password'); + + if (!email || !password) { + throw new Error('Both email and password are required.'); + } + + const response = await fetch('http://localhost:5000/users/login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': 'uc_api' + }, + body: JSON.stringify({ + email: email, + password: password + }) + }); + + switch (response.status) { + case 401: + throw new Error('Invalid credentials!'); + case 500: + throw new Error('Internal server error. Try again later!'); + } + + const responseBody = await response.json(); + + const result = await window.electronAPI.writeFile('loginData.json', JSON.stringify(responseBody.message, null, 2)); + if (!result.success) { + throw new Error('Error writing to file. Please try again later.'); + } + + window.electronAPI.changeContent('main_menu.html') + .then(() => console.log('Navigated to dashboard')) + .catch(error => console.error('Error navigating:', error)); + + } catch (error) { + await window.electronAPI.showAlert(error.message) + .then(() => console.log('Alert window opened')) + .catch(error => console.error('Error showing alert:', error)); + } + }); +}); diff --git a/User/src/renderer/js/main_menu.js b/User/src/renderer/js/main_menu.js new file mode 100644 index 0000000..472336f --- /dev/null +++ b/User/src/renderer/js/main_menu.js @@ -0,0 +1,156 @@ +document.addEventListener('DOMContentLoaded', async function () { + const backupButton = document.getElementById('backup'); + const changeDepartmentButton = document.getElementById('change_department'); + const changeInfoButton = document.getElementById('change_info'); + const shareFileButton = document.getElementById('share_file'); + const decryptButton = document.getElementById('decrypt'); + const logoutButton = document.getElementById('logout'); + + const overlay = document.getElementById('overlay'); + const ceoBackButton = document.getElementById('back_button'); + const ceoSubmitButton = document.getElementById('submit_button'); + + let triggerSource = ''; + + function handleOverlayOpen(buttonId) { + overlay.style.display = 'block'; + triggerSource = buttonId; // Remember the button that triggered the overlay + console.log(`${buttonId} button clicked!`); + } + + changeDepartmentButton.addEventListener('click', function () { + handleOverlayOpen('change_department'); + }); + + decryptButton.addEventListener('click', function () { + handleOverlayOpen('decrypt'); + }); + + // Hide the form when the "Back" button is clicked + ceoBackButton.addEventListener('click', function() { + overlay.style.display = 'none'; + }); + + // Validate and process the form when submitted + ceoSubmitButton.addEventListener('click', async function(event) { + event.preventDefault(); + const password = document.getElementById('ceo_password').value; + const ceoPassword = getCeoPassword(); + if (password === '') { + alert('Please enter a password.'); + return; + } + + if(password !== ceoPassword){ + return; + } + + if (triggerSource === 'change_department') { + } else if (triggerSource === 'decrypt') { + await decryptFiles() + } + + overlay.style.display = 'none'; + }); + + checkFileExists() + .then(() => console.log('verificare facuta')); + await insertUsername(); + + backupButton.addEventListener('click', function () { + console.log('Set backup directory button clicked!'); + + window.electronAPI.openBackupDirDialog() + .then(() => console.log('Back-up directory set')) + .catch(async error => await window.electronAPI.showAlert(error.message) + .then(() => console.log('Alert window opened')) + .catch(error => console.error('Error showing alert:', error))); + }); + + changeInfoButton.addEventListener('click', function () { + console.log('Change your info button clicked!'); + + window.electronAPI.changeContent('profile.html') + .then(() => console.log('Navigated to dashboard')) + .catch(error => console.error('Error navigating:', error)); + }); + + shareFileButton.addEventListener('click', function () { + console.log('Share a file button clicked!'); + + window.electronAPI.changeContent('share_file.html') + .then(() => console.log('Navigated to dashboard')) + .catch(error => console.error('Error navigating:', error)); + }); + + + + logoutButton.addEventListener('click', async function () { + console.log('Logout button clicked!'); + + await window.electronAPI.deleteFile('loginData.json'); + window.electronAPI.changeContent('login.html') + .then(() => console.log('Navigated to dashboard')) + .catch(error => console.error('Error navigating:', error)); + }); + + async function checkFileExists() { + try { + // Make an IPC call to check file existence + const fileExists = await window.electronAPI.checkFileExists('dirBackup.json'); + + if (!fileExists) { + const notificationsDiv = document.getElementById('notifications'); + const button = document.createElement('button'); + button.id = 'backup_alert'; + button.name = 'alert'; + button.textContent = 'Set your backup directory!'; + button.addEventListener('click', handleButtonClick); + notificationsDiv.appendChild(button); + } + } catch (error) { + console.error('Error checking file existence:', error); + } + } + + + async function handleButtonClick() { + console.log('Button clicked!'); + + await window.electronAPI.openBackupDirDialog() + .then(() => console.log('Back-up directory set')) + .catch(async error => await window.electronAPI.showAlert(error.message) + .then(() => console.log('Alert window opened')) + .catch(error => console.error('Error showing alert:', error))); + + const button = document.getElementById('backup_alert'); + button.remove(); + } + + async function insertUsername() { + try { + const userData = await window.electronAPI.readFile('loginData.json'); + const userJson = JSON.parse(userData.content); + const username = userJson.name; + console.log(userData); + console.log(username); + + const usernameField = document.getElementById('username_field'); + if (usernameField) { + usernameField.textContent = username + '!'; + } + } catch (error) { + console.error('Error loading username:', error); + const usernameField = document.getElementById('username_field'); + usernameField.textContent = 'User!'; + } + } + + async function decryptFiles(){ + + } + + async function getCeoPassword(){ + + } +}); diff --git a/User/src/renderer/js/profile.js b/User/src/renderer/js/profile.js new file mode 100644 index 0000000..a3d0f41 --- /dev/null +++ b/User/src/renderer/js/profile.js @@ -0,0 +1,87 @@ +document.addEventListener('DOMContentLoaded', async function () { + try { + const result = await window.electronAPI.readFile('loginData.json'); + const loginData = JSON.parse(result.content); + + // Set values for the inputs + const emailInput = document.querySelector('input[name="email"]'); + const usernameInput = document.querySelector('input[name="username"]'); + const passwordInput = document.querySelector('input[name="password"]'); + + emailInput.value = loginData.email; + usernameInput.value = loginData.name; + passwordInput.value = loginData.password; + + } catch (error) { + console.error('Error reading login data:', error); + } + + const backButton = document.querySelector('button[name="login"]'); + const submitButton = document.querySelector('button[name="submit"]'); + + // Add event listeners for the back and submit buttons + backButton.addEventListener('click', function () { + console.log('Back button clicked!'); + + window.electronAPI.changeContent('main_menu.html') + .then(() => console.log('Navigated to dashboard')) + .catch(error => console.error('Error navigating:', error)); + }); + + submitButton.addEventListener('click', async function (e) { + try { + e.preventDefault(); + console.log('Submit button clicked!'); + + const emailInput = document.querySelector('input[name="email"]'); + const usernameInput = document.querySelector('input[name="username"]'); + const passwordInput = document.querySelector('input[name="password"]'); + + const result = await window.electronAPI.readFile('loginData.json'); + const loginData = JSON.parse(result.content); + + const {id, department} = loginData; + const email = emailInput.value; + const name = usernameInput.value; + const password = passwordInput.value; + + const response = await fetch(`http://localhost:5000/users/${id}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': 'uc_api' + }, + body: JSON.stringify({ + name: name, + email: email, + password: password + }) + }); + + switch (response.status) { + case 400: + throw new Error('Email format invalid!') + case 409: + throw new Error('Email already in system!') + case 500: + throw new Error('Internal server error. Try again later!') + } + + await window.electronAPI.writeFile('loginData.json', JSON.stringify({ + id: id, + name: name, + email: email, + password: password, + department: department + })); + + window.electronAPI.changeContent('main_menu.html') + .then(() => console.log('Navigated to dashboard')) + .catch(error => console.error('Error navigating:', error)); + }catch(error) { + await window.electronAPI.showAlert(error.message) + .then(() => console.log('Alert window opened')) + .catch(error => console.error('Error showing alert:', error)); + } + }); +}); diff --git a/User/src/renderer/js/sign_up_confirmation.js b/User/src/renderer/js/sign_up_confirmation.js new file mode 100644 index 0000000..c13607f --- /dev/null +++ b/User/src/renderer/js/sign_up_confirmation.js @@ -0,0 +1,7 @@ +document.addEventListener('DOMContentLoaded', async function() { + await new Promise(resolve => setTimeout(resolve, 2000)); + + window.electronAPI.changeContent('login.html') + .then(() => console.log('Content changed successfully')) + .catch(error => console.error('Error changing content:', error)); +}); diff --git a/User/src/renderer/js/sign_up_departments.js b/User/src/renderer/js/sign_up_departments.js new file mode 100644 index 0000000..4d08d0d --- /dev/null +++ b/User/src/renderer/js/sign_up_departments.js @@ -0,0 +1,93 @@ +document.addEventListener('DOMContentLoaded', async function() { + try { + // Make API call to fetch department data + const response = await fetch('http://localhost:5000/departments', { + method: 'GET', + headers: { + 'x-api-key': 'uc_api' + } + }); + const res = await response.json(); + const data = res['data']; + + const formContent = document.querySelector('.signup-form-content'); + data.forEach(department => { + const label = document.createElement('label'); + label.innerHTML = `${department.name}`; + formContent.appendChild(label); + }); + } catch (error) { + await window.electronAPI.showAlert(error.message) + .then(() => console.log('Alert window opened')) + .catch(error => console.error('Error changing content:', error)); + } + + document.getElementById('back').addEventListener('click', async function() { + try { + await window.electronAPI.changeContent('sign_up_profile.html'); + console.log('Content changed successfully'); + } catch (error) { + console.error('Error changing content:', error); + } + }); + + // Handler for the continue button + document.getElementById('submit').addEventListener('click', async function(e) { + e.preventDefault(); + + const selectedDept = document.querySelector('input[name="dept"]:checked').value; + + try { + if (!selectedDept) { + throw new Error('No department had been selected!.'); + } + + let result = await window.electronAPI.readFile('signupData.json'); + if (!result.success) { + throw new Error('Error reading the file. Please try again later.'); + } + + const data = JSON.parse(result.content); + + const email = data.email; + const name = data.name; + const password = data.password; + + result = await window.electronAPI.deleteFile('signupData.json'); + if (!result.success) { + throw new Error('Error deleting the file. Please try again later.'); + } + + await fetch('http://localhost:5000/users/register', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': 'uc_api' + }, + body: JSON.stringify({ + name: name, + email: email, + password: password, + department: selectedDept, + }) + }).then(async response => { + if(!response.ok){ + await window.electronAPI.showAlert("Internal server error. Try again later!") + .then(() => console.log('Alert window opened')) + .catch(error => console.error('Error changing content:', error)); + } + + window.electronAPI.changeContent('sign_up_confirmation.html') + .then(() => console.log('Content changed successfully')) + .catch(error => console.error('Error changing content:', error)); + }).catch(error => { + console.error(error); + }); + + } catch (error) { + await window.electronAPI.showAlert(error.message) + .then(() => console.log('Alert window opened')) + .catch(error => console.error('Error changing content:', error)); + } + }); +}); diff --git a/User/src/renderer/js/sign_up_profile.js b/User/src/renderer/js/sign_up_profile.js new file mode 100644 index 0000000..a9cfb63 --- /dev/null +++ b/User/src/renderer/js/sign_up_profile.js @@ -0,0 +1,81 @@ +document.addEventListener('DOMContentLoaded', function () { + const cancelButton = document.getElementById('login'); + const continueButton = document.getElementById('continue'); + + cancelButton.addEventListener('click', function () { + console.log(`'Login' button clicked!`); + window.electronAPI.changeContent('login.html') + .then(() => console.log('Content changed successfully')) + .catch(error => console.error('Error changing content:', error)); + }); + + continueButton.addEventListener('click', async function (e) { + try { + e.preventDefault(); + console.log('Continue button clicked'); + + const form = document.getElementById('signupForm'); + const formData = new FormData(form); + + const email = formData.get('email'); + const name = formData.get('name'); + const password = formData.get('password'); + + if (!email || !name || !password) { + throw new Error('All fields are required.'); + } + + if(password.length < 8){ + throw new Error('Password must have minimum length 8!'); + } + + let statusFetch = 200; + + await fetch('http://localhost:5000/users/validate_email', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': 'uc_api' + }, + body: JSON.stringify({ + email: email + }) + }).then(async response => { + const responseData = await response.json(); + statusFetch = response.status; + console.error(responseData.message); + }) + .catch(error => { + console.log(error); + }) + + switch(statusFetch){ + case 500: + throw new Error('Internal server error! Try again later'); + case 400: + throw new Error('Not a valid email!'); + case 409: + throw new Error('Email already exists in system!'); + } + + const formDataJSON = {}; + formData.forEach((value, key) => { + formDataJSON[key] = value; + }); + + const result = await window.electronAPI.writeFile('signupData.json', JSON.stringify(formDataJSON)); + if (!result.success) { + throw new Error('Error writing to file. Please try again later.'); + } + + window.electronAPI.changeContent('sign_up_departments.html') + .then(() => console.log('Content changed successfully')) + .catch(error => console.error('Error changing content:', error)); + } + catch (error) { + await window.electronAPI.showAlert(error.message) + .then(() => console.log('Alert window opened')) + .catch(error => console.error('Error changing content:', error));; + } + }); +}); \ No newline at end of file