diff --git a/backend/Api/BusinessLogic/DeviceLogic.cs b/backend/Api/BusinessLogic/DeviceLogic.cs index 506316c..ceb386b 100644 --- a/backend/Api/BusinessLogic/DeviceLogic.cs +++ b/backend/Api/BusinessLogic/DeviceLogic.cs @@ -29,12 +29,21 @@ namespace Api.BusinessLogic return new OkObjectResult(devices); } - public async Task<IActionResult> AddDevice(Device device, int userId) + public async Task<IActionResult> AddDevice(string referenceId, int userId) { var profile = await _dbAccess.ReadUser(userId); if (profile == null) { return new ConflictObjectResult(new { message = "Could not find user" }); } + Device device = new Device + { + Name = "Undefined", + TempHigh = 0, + TempLow = 0, + ReferenceId = referenceId, + Logs = new List<TemperatureLogs>(), + }; + return await _dbAccess.CreateDevice(device, userId); } diff --git a/backend/Api/BusinessLogic/UserLogic.cs b/backend/Api/BusinessLogic/UserLogic.cs index 43d21f6..1e8b282 100644 --- a/backend/Api/BusinessLogic/UserLogic.cs +++ b/backend/Api/BusinessLogic/UserLogic.cs @@ -1,5 +1,6 @@ using Api.DBAccess; using Api.Models; +using Api.Models.User; using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; @@ -22,6 +23,14 @@ namespace Api.BusinessLogic _configuration = configuration; } + public async Task<IActionResult> getUser(int userId) + { + User user = await _dbAccess.getUser(userId); + + if (user == null || user.Id == 0) { return new ConflictObjectResult(new { message = "Could not find user" }); } + return new OkObjectResult(new { user.Id, user.UserName, user.Email }); + } + public async Task<IActionResult> RegisterUser(User user) { if (!new Regex(@".+@.+\..+").IsMatch(user.Email)) @@ -47,7 +56,6 @@ namespace Api.BusinessLogic return await _dbAccess.CreateUser(user); } - public async Task<IActionResult> Login(Login login) { User user = await _dbAccess.Login(login); @@ -59,30 +67,37 @@ namespace Api.BusinessLogic if (user.Password == hashedPassword) { var token = GenerateJwtToken(user); - return new OkObjectResult(new { token, user.UserName, user.Id }); + return new OkObjectResult(new { token, user.Id}); } return new ConflictObjectResult(new { message = "Invalid password" }); } - public async Task<IActionResult> EditProfile(User user, int userId) + public async Task<IActionResult> EditProfile(EditUserRequest userRequest, int userId) { - if (!new Regex(@".+@.+\..+").IsMatch(user.Email)) + return await _dbAccess.UpdateUser(userRequest, userId); + } + + public async Task<IActionResult> changePassword(ChangePasswordRequest passwordRequest, int userId) + { + var user = await _dbAccess.ReadUser(userId); + + string hashedPassword = ComputeHash(passwordRequest.OldPassword, SHA256.Create(), user.Salt); + + if (user.Password != hashedPassword) { - return new ConflictObjectResult(new { message = "Invalid email address" }); + return new ConflictObjectResult(new { message = "Old password is incorrect" }); + } - if (!PasswordSecurity(user.Password)) + if (!PasswordSecurity(passwordRequest.NewPassword)) { - return new ConflictObjectResult(new { message = "Password is not up to the security standard" }); + return new ConflictObjectResult(new { message = "New password is not up to the security standard" }); } - var profile = await _dbAccess.ReadUser(userId); + string hashedNewPassword = ComputeHash(passwordRequest.NewPassword, SHA256.Create(), user.Salt); - string hashedPassword = ComputeHash(user.Password, SHA256.Create(), profile.Salt); - user.Password = hashedPassword; - - return await _dbAccess.UpdateUser(user, userId); + return await _dbAccess.updatePassword(hashedNewPassword, userId); } public async Task<IActionResult> DeleteUser(int userId) diff --git a/backend/Api/Controllers/DeviceController.cs b/backend/Api/Controllers/DeviceController.cs index d936fdb..31b3188 100644 --- a/backend/Api/Controllers/DeviceController.cs +++ b/backend/Api/Controllers/DeviceController.cs @@ -30,9 +30,9 @@ namespace Api.Controllers [Authorize] [HttpPost("adddevice/{userId}")] - public async Task<IActionResult> AddDevice([FromBody] Device device, int userId) + public async Task<IActionResult> AddDevice([FromBody] string referenceId, int userId) { - return await _deviceLogic.AddDevice(device, userId); + return await _deviceLogic.AddDevice(referenceId, userId); } [Authorize] @@ -48,5 +48,13 @@ namespace Api.Controllers { return await _deviceLogic.EditDevice(device, deviceId); } + + + [Authorize] + [HttpDelete("Delete/{referenceId}")] + public async Task<IActionResult> EditDevice(string referenceId) + { + return await _deviceLogic.EditDevice(referenceId); + } } } diff --git a/backend/Api/Controllers/UserController.cs b/backend/Api/Controllers/UserController.cs index 186e1d4..567a416 100644 --- a/backend/Api/Controllers/UserController.cs +++ b/backend/Api/Controllers/UserController.cs @@ -1,12 +1,8 @@ using Microsoft.AspNetCore.Mvc; using Api.Models; -using Api.DBAccess; -using Microsoft.IdentityModel.Tokens; -using System.IdentityModel.Tokens.Jwt; -using System.Security.Claims; -using System.Text; using Microsoft.AspNetCore.Authorization; using Api.BusinessLogic; +using Api.Models.User; namespace Api.Controllers { @@ -21,31 +17,43 @@ namespace Api.Controllers _userLogic = userLogic; } - [HttpPost("Login")] + //[Authorize] + [HttpGet("{userId}")] + public async Task<IActionResult> GetUSer(int userId) + { + return await _userLogic.getUser(userId); + } + [HttpPost("login")] public async Task<IActionResult> Login([FromBody] Login login) { return await _userLogic.Login(login); } - [HttpPost("Create")] + [HttpPost("create")] public async Task<IActionResult> CreateUser([FromBody] User user) { return await _userLogic.RegisterUser(user); } - [Authorize] - [HttpPut("Edit/{userId}")] - public async Task<IActionResult> EditUser([FromBody] User user, int userId) + //[Authorize] + [HttpPut("edit/{userId}")] + public async Task<IActionResult> EditUser([FromBody] EditUserRequest userRequest, int userId) { - return await _userLogic.EditProfile(user, userId); + return await _userLogic.EditProfile(userRequest, userId); + } + + //[Authorize] + [HttpPut("change-password/{userId}")] + public async Task<IActionResult> changePassword([FromBody] ChangePasswordRequest passwordRequest, int userId) + { + return await _userLogic.changePassword(passwordRequest, userId); } [Authorize] - [HttpDelete("Delete/{userId}")] + [HttpDelete("delete/{userId}")] public async Task<IActionResult> DeleteUser(int userId) { return await _userLogic.DeleteUser(userId); } - } } diff --git a/backend/Api/DBAccess/DBAccess.cs b/backend/Api/DBAccess/DBAccess.cs index ad83328..aa51634 100644 --- a/backend/Api/DBAccess/DBAccess.cs +++ b/backend/Api/DBAccess/DBAccess.cs @@ -5,6 +5,7 @@ using System.Runtime.Intrinsics.Arm; using System.Security.Cryptography; using Microsoft.AspNetCore.Mvc; using static System.Runtime.InteropServices.JavaScript.JSType; +using Api.Models.User; namespace Api.DBAccess @@ -18,6 +19,12 @@ namespace Api.DBAccess _context = context; } + public async Task<User> getUser(int userId) + { + return await _context.Users.FirstOrDefaultAsync(u => u.Id == userId); + } + + public async Task<IActionResult> CreateUser(User user) { var users = await _context.Users.ToListAsync(); @@ -64,7 +71,7 @@ namespace Api.DBAccess return await _context.Users.FirstOrDefaultAsync(u => u.Id == userId); } - public async Task<IActionResult> UpdateUser(User user, int userId) + public async Task<IActionResult> UpdateUser(EditUserRequest user, int userId) { var profile = await _context.Users.FirstOrDefaultAsync(u => u.Id == userId); var users = await _context.Users.ToListAsync(); @@ -73,22 +80,39 @@ namespace Api.DBAccess foreach (var item in users) { - if (item.UserName == user.UserName) + if (item.UserName == user.UserName && userId != item.Id) { return new ConflictObjectResult(new { message = "Username is already in use." }); } - if (item.Email == user.Email) + if (item.Email == user.Email && userId != item.Id) { return new ConflictObjectResult(new { message = "Email is being used already" }); } } - profile.UserName = user.UserName; + if(user.Email != "" && user.Email != null) + profile.Email = user.Email; - profile.Email = user.Email; + if (user.UserName != "" && user.UserName != null) + profile.UserName = user.UserName; - profile.Password = user.Password; + + + bool saved = await _context.SaveChangesAsync() == 1; + + if (saved) { return new OkObjectResult(profile); } + + return new ConflictObjectResult(new { message = "Could not save to database" }); + } + + public async Task<IActionResult> updatePassword(string newPassword, int userId) + { + var profile = await _context.Users.FirstOrDefaultAsync(u => u.Id == userId); + + if (profile == null) { return new ConflictObjectResult(new { message = "User does not exist" }); } + + profile.Password = newPassword; bool saved = await _context.SaveChangesAsync() == 1; diff --git a/backend/Api/DBAccess/DBContext.cs b/backend/Api/DBAccess/DBContext.cs index 5c23603..ae2427a 100644 --- a/backend/Api/DBAccess/DBContext.cs +++ b/backend/Api/DBAccess/DBContext.cs @@ -1,5 +1,6 @@ using Microsoft.EntityFrameworkCore; using Api.Models; +using Api.Models.User; namespace Api { diff --git a/backend/Api/Models/User/ChangePasswordRequest.cs b/backend/Api/Models/User/ChangePasswordRequest.cs new file mode 100644 index 0000000..6dff72d --- /dev/null +++ b/backend/Api/Models/User/ChangePasswordRequest.cs @@ -0,0 +1,8 @@ +namespace Api.Models.User +{ + public class ChangePasswordRequest + { + public string OldPassword { get; set; } + public string NewPassword { get; set; } + } +} diff --git a/backend/Api/Models/User/EditUserRequest.cs b/backend/Api/Models/User/EditUserRequest.cs new file mode 100644 index 0000000..6b3f6f8 --- /dev/null +++ b/backend/Api/Models/User/EditUserRequest.cs @@ -0,0 +1,8 @@ +namespace Api.Models.User +{ + public class EditUserRequest + { + public string UserName { get; set; } + public string Email { get; set; } + } +} diff --git a/backend/Api/Models/User.cs b/backend/Api/Models/User/User.cs similarity index 91% rename from backend/Api/Models/User.cs rename to backend/Api/Models/User/User.cs index 4583fab..37bbd8f 100644 --- a/backend/Api/Models/User.cs +++ b/backend/Api/Models/User/User.cs @@ -1,4 +1,4 @@ -namespace Api.Models +namespace Api.Models.User { public class User { diff --git a/frontend/devices/index.html b/frontend/devices/index.html index e69de29..9fe9dcc 100644 --- a/frontend/devices/index.html +++ b/frontend/devices/index.html @@ -0,0 +1,107 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>Temperature-Alarm-Web</title> + <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js"></script> + <link rel="stylesheet" href="/styles/auth.css"> + <link rel="stylesheet" href="/styles/devices.css" /> + <script defer type="module" src="/scripts/devices.js"></script> + <script defer type="module" src="/shared/utils.js"></script> + </head> + + <body> + <div id="container"> + <div class="topnav"> + <a href="/home/index.html">Home</a> + <a class="active" href="/devices/index.html">Devices</a> + <div style="display: flex; justify-content: flex-end;"> + <a href="/profile/index.html">Profile</a> + <span class="logoutContainer"> + <img class="logout" src="/img/logout.png"> + </span> + </div> + </div> + <div class="addDeviceContainer"> + <button id="addDevice">Add Device</button> + </div> + <div class="tableConatiner"> + <table> + <tr> + <th>Id</th> + <th>Placement(name)</th> + <th>Set Temp High</th> + <th>Set Temp Low</th> + <th>Latest Meassurement</th> + <th></th> + </tr> + <tbody id="deviceTable"></tbody> + </table> + </div> + </div> + + + <div id="deleteModal" class="modal"> + <form class="modal-content"> + <div class="container"> + <h1 class="deviceHeader" id="deleteDeviceHeader"></h1> + <p>Are you sure you want to delete this device from your account?</p> + + <div class="clearfix"> + <button type="button" class="cancelbtn">Cancel</button> + <button type="button" id="deletebtn">Delete</button> + </div> + </div> + </form> + </div> + + <div id="editModal" class="modal"> + <div class="modal-content"> + <div class="container"> + <h1 class="deviceHeader" id="editDeviceHeader"></h1> + <form id="editForm"> + <div class="form-container"> + <label for="name"><b>Name</b></label> + <input type="text" placeholder="Enter a name for the placement" id="name" required> + + <label for="tempHigh"><b>High Temperature</b></label> + <input type="text" placeholder="Edit the high temperature" id="tempHigh" required> + + <label for="tempLow"><b>Low Temperature</b></label> + <input type="text" placeholder="Edit the low temperature" id="tempLow" required> + + <div class="clearfix"> + <button type="button" class="cancelbtn">Cancel</button> + <button type="button" id="editbtn">Save Changes</button> + </div> + + <div id="form-error"></div> + </div> + </form> + </div> + </div> + </div> + + <div id="addModal" class="modal"> + <div class="modal-content"> + <div class="container"> + <h1 class="deviceHeader">Add Device</h1> + <form id="editForm"> + <div class="form-container"> + <label for="referenceId"><b>Device Id</b></label> + <input type="text" placeholder="Enter the device Id" id="referenceId" required> + <div id="form-error"></div> + </div> + <div class="clearfix"> + <button type="button" class="cancelbtn">Cancel</button> + <button type="button" id="addbtn">Add</button> + </div> + + <div id="form-error"></div> + </form> + </div> + </div> +</div> + </body> +</html> diff --git a/frontend/home/index.html b/frontend/home/index.html index 403e197..b960d84 100644 --- a/frontend/home/index.html +++ b/frontend/home/index.html @@ -5,32 +5,39 @@ <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Temperature-Alarm-Web</title> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.js"></script> + <link rel="stylesheet" href="/styles/auth.css"> <link rel="stylesheet" href="/styles/home.css" /> <script type="module" src="/scripts/home.js"></script> + <script defer type="module" src="/shared/utils.js"></script> </head> <body> <div id="container"> <div class="topnav"> <a class="active" href="/home/index.html">Home</a> + <a href="/devices/index.html">Devices</a> <div style="display: flex; justify-content: flex-end;"> - <a class="" href="/home/index.html">Devices</a> - <a class="" href="/profile/index.html">Profile</a> + <a href="/profile/index.html">Profile</a> + <span class="logoutContainer"> + <img class="logout" src="/img/logout.png"> + </span> </div> </div> <div class="chartContainer"> <canvas id="myChart" style="width: 49%; height: 49%;"></canvas> </div> - <table> - <tr> - <th>Name</th> - <th>Temperature</th> - <th>Date</th> - <th>TempHigh</th> - <th>TempLow</th> - </tr> - <tbody id="TemperatureTable"></tbody> - </table> + <div class="tableConatiner"> + <table> + <tr> + <th>Name</th> + <th>Temperature</th> + <th>Date</th> + <th>TempHigh</th> + <th>TempLow</th> + </tr> + <tbody id="TemperatureTable"></tbody> + </table> + </div> </div> </body> </html> diff --git a/frontend/img/Edit.png b/frontend/img/Edit.png new file mode 100644 index 0000000..f0f9e79 Binary files /dev/null and b/frontend/img/Edit.png differ diff --git a/frontend/img/Trash.png b/frontend/img/Trash.png new file mode 100644 index 0000000..0c36d08 Binary files /dev/null and b/frontend/img/Trash.png differ diff --git a/frontend/img/logout.png b/frontend/img/logout.png new file mode 100644 index 0000000..e493049 Binary files /dev/null and b/frontend/img/logout.png differ diff --git a/frontend/index.html b/frontend/index.html index c30d98b..26a5fe8 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,6 +4,8 @@ <meta charset="UTF-8" /> <title>Temperature Alarm</title> <link rel="stylesheet" href="/styles/frontpage.css"> + <link rel="stylesheet" href="/styles/auth.css"> + </head> <body> <main> diff --git a/frontend/login/index.html b/frontend/login/index.html index 4b22dea..914597a 100644 --- a/frontend/login/index.html +++ b/frontend/login/index.html @@ -20,9 +20,6 @@ <button type="submit">Login</button> <div class="details"> - <label> - <input type="checkbox" name="remember"> Remember me - </label> <span> Dont have an account? <a href="/register">Register</a> </span> diff --git a/frontend/mockdata/devices.mockdata.js b/frontend/mockdata/devices.mockdata.js new file mode 100644 index 0000000..00f74bf --- /dev/null +++ b/frontend/mockdata/devices.mockdata.js @@ -0,0 +1,3 @@ +export const devices = [ + {referenceId: "DKOFG", name: "Kitchen", tempHigh: 23.0, tempLow: 23.0, latestLog: { id: 1, temperature: 18.9, date: "2025-03-19T17:00:00Z", tempHigh: 22.0, tempLow: 18.0 }} +] \ No newline at end of file diff --git a/frontend/profile/index.html b/frontend/profile/index.html index 3592996..1fb7dc1 100644 --- a/frontend/profile/index.html +++ b/frontend/profile/index.html @@ -7,9 +7,20 @@ <link rel="stylesheet" href="/styles/auth.css"> <link rel="stylesheet" href="/styles/profile.css"> <script defer type="module" src="/scripts/profile.js"></script> + <script defer type="module" src="/shared/utils.js"></script> </head> <body> <div id="container"> + <div class="topnav"> + <a href="/home/index.html">Home</a> + <a href="/devices/index.html">Devices</a> + <div style="display: flex; justify-content: flex-end;"> + <a class="active" href="/profile/index.html">Profile</a> + <span class="logoutContainer"> + <img class="logout" src="/img/logout.png"> + </span> + </div> + </div> <div id="profileCard"></div> <div class="btnContainer"> <button class="btn" id="openEditModal">Edit</button> @@ -25,14 +36,13 @@ <form id="editForm"> <div class="form-container"> <label for="email"><b>Email</b></label> - <input type="email" placeholder="Enter email "id="email" required> + <input type="email" placeholder="Enter email "id="email"> <label for="uname"><b>Username</b></label> - <input type="text" placeholder="Enter username" id="uname" required> + <input type="text" placeholder="Enter username" id="uname"> <button type="submit">Save Changes</button> - <div class="error-text" id="form-error-edit"></div> </div> </form> </div> @@ -55,7 +65,7 @@ <button type="submit">Change Password</button> - <div class="error-text" id="form-error-password"></div> + <div id="form-error"></div> </div> </form> </div> diff --git a/frontend/register/index.html b/frontend/register/index.html index 666fe02..94e358c 100644 --- a/frontend/register/index.html +++ b/frontend/register/index.html @@ -10,7 +10,7 @@ <body> <form id="registerForm"> <div class="form-container"> - <h1>Create Account</h1> + <h1>Sign Up</h1> <label for="email"><b>Email</b></label> <input type="email" placeholder="Enter email "id="email" required> diff --git a/frontend/scripts/devices.js b/frontend/scripts/devices.js new file mode 100644 index 0000000..e958d0f --- /dev/null +++ b/frontend/scripts/devices.js @@ -0,0 +1,90 @@ +import { getDevicesOnUserId, deleteDevice, update, add } from "./services/devices.service.js"; +import { devices } from "../mockdata/devices.mockdata.js"; + +let id = localStorage.getItem("id"); +// getDevicesOnUserId(id).then(res => { +// buildTable(res) +// }) +buildTable(devices); + +let selectedReferenceId = null; // Store the selected referenceId + +function buildTable(data) { + var table = document.getElementById("deviceTable"); + table.innerHTML = ""; // Clear existing rows before adding new ones + + data.forEach((device) => { + var row = document.createElement("tr"); + row.innerHTML = ` + <td>${device.referenceId}</td> + <td>${device.name}</td> + <td>${device.tempHigh}</td> + <td>${device.tempLow}</td> + <td>Temperature: ${device.latestLog.temperature}°C, Date: ${device.latestLog.date}</td> + <td style="width: 90px;"> + <img class="editIconbtn tableIcons" src="/img/Edit.png" data-referenceid="${device.referenceId}"> + <img class="trashBtn tableIcons" src="/img/Trash.png" data-referenceid="${device.referenceId}"> + </td> + `; + table.appendChild(row); + }); + + document.getElementById("addDevice").onclick = () => { + document.getElementById("addModal").style.display = "block"; + + } + + // Attach click event to all trash buttons + document.querySelectorAll(".trashBtn").forEach((btn) => { + btn.onclick = function () { + selectedReferenceId = this.getAttribute("data-referenceid"); // Store referenceId + document.getElementById("deleteDeviceHeader").innerHTML = `Delete Device ${selectedReferenceId}`; + document.getElementById("deleteModal").style.display = "block"; + }; + }); + + // Attach click event to all trash buttons + document.querySelectorAll(".editIconbtn").forEach((btn) => { + btn.onclick = function () { + selectedReferenceId = this.getAttribute("data-referenceid"); // Store referenceId + document.getElementById("editDeviceHeader").innerHTML = `Edit Device ${selectedReferenceId}`; + document.getElementById("editModal").style.display = "block"; + }; + }); +} + +document.querySelectorAll(".cancelbtn").forEach(button => { + button.onclick = () => { + document.getElementById("deleteModal").style.display = "none"; + document.getElementById("editModal").style.display = "none"; + document.getElementById("addModal").style.display = "none"; + + }; +}); +// Delete button logic +document.getElementById("deletebtn").onclick = () => { + if (selectedReferenceId) { + deleteDevice(selectedReferenceId); // Call delete function with referenceId + document.getElementById("deleteModal").style.display = "none"; + window.location.reload(); + } +}; + +document.getElementById("addbtn").onclick = () => { + const referenceId = document.getElementById("referenceId").value; + add(referenceId); // Call delete function with referenceId + document.getElementById("deleteModal").style.display = "none"; + window.location.reload(); +}; + +document.getElementById("editbtn").onclick = () => { + if (selectedReferenceId) { + const name = document.getElementById("name").value; + const tempHigh = document.getElementById("tempHigh").value; + const tempLow = document.getElementById("tempLow").value; + + update(selectedReferenceId, name, tempHigh, tempLow); // Call delete function with referenceId + document.getElementById("deleteModal").style.display = "none"; + window.location.reload(); + } +}; diff --git a/frontend/scripts/home.js b/frontend/scripts/home.js index 8538bbb..912ffa0 100644 --- a/frontend/scripts/home.js +++ b/frontend/scripts/home.js @@ -61,24 +61,4 @@ function buildTable(data) { </tr>`; table.innerHTML += row; }); -} - -// Get the modal -var modal = document.getElementById("chartModal"); -var btn = document.getElementById("myBtn"); -var span = document.getElementsByClassName("close")[0]; -btn.onclick = function () { - modal.style.display = "block"; -}; - -// When the user clicks on <span> (x), close the modal -span.onclick = function () { - modal.style.display = "none"; -}; - -// When the user clicks anywhere outside of the modal, close it -window.onclick = function (event) { - if (event.target == modal) { - modal.style.display = "none"; - } -}; +} \ No newline at end of file diff --git a/frontend/scripts/login.js b/frontend/scripts/login.js index e264a10..5d661d5 100644 --- a/frontend/scripts/login.js +++ b/frontend/scripts/login.js @@ -13,9 +13,13 @@ document.getElementById("loginForm").addEventListener("submit", function(event) if (response.error) { document.getElementById("form-error").innerText = response.error; document.getElementById("form-error").style.display = "block"; - return; } + else{ + if (typeof(Storage) !== "undefined") { + localStorage.setItem("id", response.id); + } + } location.href = "/home"; }); diff --git a/frontend/scripts/profile.js b/frontend/scripts/profile.js index ccca75d..f0962a7 100644 --- a/frontend/scripts/profile.js +++ b/frontend/scripts/profile.js @@ -1,23 +1,32 @@ import { profileData } from "../mockdata/profile.mockdata.js"; +import { get } from "./services/users.service.js"; +import { update } from "./services/users.service.js"; +import { updatePassword } from "./services/users.service.js"; -var table = document.getElementById(`profileCard`); +let id = localStorage.getItem("id"); + +get(id).then(res => { + var table = document.getElementById(`profileCard`); table.innerHTML += ` <div class="pfp"> <img style="width:200px; height:200px" src="${profileData.pfp}"> </div> <div class="userData"> - <h2>${profileData.username}</h2> - <h2>${profileData.email}</h2> + <h2>${res.userName}</h2> + <h2>${res.email}</h2> </div> </div>`; +}) + + var pswModal = document.getElementById("PasswordModal"); var editModal = document.getElementById("editModal"); -var editBtn = document.getElementById("openEditModal"); +var editIconbtn = document.getElementById("openEditModal"); var passwordBtn = document.getElementById("openPasswordModal"); // Open modals -editBtn.onclick = () => (editModal.style.display = "block"); +editIconbtn.onclick = () => (editModal.style.display = "block"); passwordBtn.onclick = () => (pswModal.style.display = "block"); // Close modals when clicking on any close button @@ -25,6 +34,8 @@ document.querySelectorAll(".close").forEach(closeBtn => { closeBtn.onclick = () => { pswModal.style.display = "none"; editModal.style.display = "none"; + document.getElementById("form-error").innerText = ""; + document.getElementById("form-error").style.display = "none"; }; }); @@ -33,20 +44,22 @@ window.onclick = (event) => { if (event.target == pswModal || event.target == editModal) { pswModal.style.display = "none"; editModal.style.display = "none"; + document.getElementById("form-error").innerText = ""; + document.getElementById("form-error").style.display = "none"; } }; document.getElementById("editForm").addEventListener("submit", function(event) { event.preventDefault(); // Prevents default form submission - document.getElementById("form-error-edit").style.display = "none"; + document.getElementById("form-error").style.display = "none"; // Get form values const email = document.getElementById("email").value; const username = document.getElementById("uname").value; // Call function with form values - update(email, username) + update(email, username, id) .then(response => { if (response?.error) { document.getElementById("form-error").innerText = response.error; @@ -55,37 +68,33 @@ document.getElementById("editForm").addEventListener("submit", function(event) { return; } - location.href = "/login"; + location.href = "/profile"; }); }); document.getElementById("PasswordForm").addEventListener("submit", function(event) { event.preventDefault(); // Prevents default form submission - document.getElementById("form-error-password").style.display = "none"; + document.getElementById("form-error").style.display = "none"; - // Get form values const oldPassword = document.getElementById("oldpsw").value; const newPassword = document.getElementById("psw").value; const repeatPassword = document.getElementById("rpsw").value; if (newPassword !== repeatPassword) { - let errorDiv = document.getElementById("form-error-password"); + let errorDiv = document.getElementById("form-error"); errorDiv.style.display = "block"; errorDiv.innerText = "Passwords do not match!"; return; } - // Call function with form values - update(email, username) + updatePassword(oldPassword, newPassword, id) .then(response => { - if (response?.error) { - document.getElementById("form-error").innerText = response.error; + //error messages do not work + if (response.error) { + document.getElementById("form-error").innerText = response.message; document.getElementById("form-error").style.display = "block"; - return; } - - location.href = "/login"; }); }); diff --git a/frontend/scripts/services/devices.service.js b/frontend/scripts/services/devices.service.js index b2b18d8..50211b8 100644 --- a/frontend/scripts/services/devices.service.js +++ b/frontend/scripts/services/devices.service.js @@ -1,25 +1,50 @@ -import { address } from "../../shared/constants"; +import { address } from "../../shared/constants.js"; -export function getDevicesOnUserId(id) { - fetch(`${address}/get-on-user-id`, { +export function getDevicesOnUserId(userId) { + fetch(`${address}/device/${userId}`, { method: "GET", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ id: id }) }) .then(response => response.json()) .then(data => console.log("Success:", data)) .catch(error => console.error("Error:", error)); } -export function update(ids) { - fetch(`${address}/get-on-user-id`, { - method: "PATCH", +export function add(id) { + fetch(`${address}/device`, { + method: "CREATE", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ referenceId: id}) + }) + .then(response => response.json()) + .then(data => console.log("Success:", data)) + .catch(error => console.error("Error:", error)); +} + +export function update(id, name, tempHigh, tempLow) { + fetch(`${address}/device/${id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ name: name, tempHigh: tempHigh, tempLow: tempLow }) + }) + .then(response => response.json()) + .then(data => console.log("Success:", data)) + .catch(error => console.error("Error:", error)); +} + +export function deleteDevice(referenceId) { + console.log(referenceId) + fetch(`${address}/device/${referenceId}`, { + method: "DELETE", headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ ids: ids }) }) .then(response => response.json()) .then(data => console.log("Success:", data)) diff --git a/frontend/scripts/services/users.service.js b/frontend/scripts/services/users.service.js index 4b5fa02..234f275 100644 --- a/frontend/scripts/services/users.service.js +++ b/frontend/scripts/services/users.service.js @@ -1,6 +1,18 @@ import { address } from "../../shared/constants.js"; import { handleResponse } from "../../shared/utils.js"; + +export function get(userId) { + return fetch(`${address}/user/${userId}`, { + method: "GET", + headers: { + "Content-Type": "application/json" + }, + }) + .then(handleResponse) + .catch(err => { error: err.message }); +} + export function login(usernameOrEmail, password) { return fetch(`${address}/user/login`, { method: "POST", @@ -28,9 +40,9 @@ export function create(email, username, password, repeatPassword){ .catch(err => { error: err.message }); } -export function update(email, username){ - return fetch(`${address}/user/update`, { - method: "PATCH", +export function update(email, username, userId){ + return fetch(`${address}/user/edit/${userId}`, { + method: "PUT", headers: { "Content-Type": "application/json" }, @@ -40,9 +52,9 @@ export function update(email, username){ .catch(err => { error: err.message }); } -export function updatePassword(oldPassword, newPassword){ - return fetch(`${address}/user/update-password`, { - method: "PATCH", +export function updatePassword(oldPassword, newPassword, userId){ + return fetch(`${address}/user/change-password/${userId}`, { + method: "PUT", headers: { "Content-Type": "application/json" }, diff --git a/frontend/shared/constants.js b/frontend/shared/constants.js index a21da0c..6027ef9 100644 --- a/frontend/shared/constants.js +++ b/frontend/shared/constants.js @@ -1 +1 @@ -export const address = "hhttps://temperature.mercantec.tech/api" +export const address = "http://127.0.0.1:5000/api" diff --git a/frontend/shared/utils.js b/frontend/shared/utils.js index 419b3c0..fa13ce0 100644 --- a/frontend/shared/utils.js +++ b/frontend/shared/utils.js @@ -10,3 +10,9 @@ export async function handleResponse(response) { return { error: "Request failed with HTTP code " + response.status }; } +document.querySelectorAll(".logoutContainer").forEach(closeBtn => { + closeBtn.onclick = () => { + localStorage.clear(); + window.location.href = "/index.html"; + }; +}); diff --git a/frontend/styles/auth.css b/frontend/styles/auth.css index bb7a66a..0ce6579 100644 --- a/frontend/styles/auth.css +++ b/frontend/styles/auth.css @@ -1,5 +1,32 @@ body { font-family: Arial, Helvetica, sans-serif; + margin: 0; +} + +.topnav { + overflow: hidden; + background-color: #333; + } + + .topnav a { + height: 20px; + float: left; + color: #f2f2f2; + text-align: center; + padding: 14px 16px; + text-decoration: none; + font-size: 17px; + } + + .topnav a:hover, .topnav span:hover { + background-color: #ddd; + color: black; + cursor: pointer; +} + +.topnav a.active { + background-color: #04aa6d; + color: white; } /* Full-width input fields */ @@ -15,12 +42,12 @@ input[type=text], input[type=password], input[type=email] { /* Set a style for all buttons */ button { background-color: #04AA6D; - color: white; padding: 14px 20px; margin: 8px 0; border: none; cursor: pointer; width: 100%; + border-radius: 20px; } button:hover { @@ -35,7 +62,7 @@ button:hover { .details { display: flex; - justify-content: space-between; + justify-content: flex-end; margin-top: 0.5rem; } @@ -49,6 +76,15 @@ button:hover { margin-top: 1rem; } -button{ - border-radius: 20px; +.logoutContainer{ + display: flex; + justify-content: center; + align-items: center; + width: 50px; } + +.logout{ + width: 20px; + height: 24px; +} + diff --git a/frontend/styles/devices.css b/frontend/styles/devices.css new file mode 100644 index 0000000..bc0b68f --- /dev/null +++ b/frontend/styles/devices.css @@ -0,0 +1,150 @@ +table { + margin: 20px; + font-family: arial, sans-serif; + border-collapse: collapse; + width: 95%; + } + + td, + th { + border: 1px solid #dddddd; + text-align: left; + padding: 8px; + } + + tr:nth-child(even) { + background-color: #dddddd; +} + +.addDeviceContainer{ + margin-top: 20px; + display: flex; + justify-content: flex-end; +} + +#addDevice{ + width: 120px; + margin: 0 20px 0 0; +} + +.tableConatiner{ + display: flex; + justify-content: center; +} + +.tableIcons{ + margin-inline: 5px; + width: 30px; + height: 30px; +} + + .tableIcons:hover { + background-color: #ddd; + color: black; + cursor: pointer; + } + + + button { + background-color: #04AA6D; + color: white; + padding: 14px 20px; + margin: 8px 0; + border: none; + cursor: pointer; + width: 100%; + opacity: 0.9; + } + + button:hover { + opacity:1; + } + + /* Float cancel and delete buttons and add an equal width */ + .cancelbtn, #addbtn, #deletebtn, #editbtn{ + float: left; + width: 50%; + } + + /* Add a color to the cancel button */ + .cancelbtn { + background-color: #ccc; + color: black; + } + + /* Add a color to the delete button */ + #deletebtn { + background-color: #f44336; + } + + /* Add padding and center-align text to the container */ + .container { + padding: 16px; + } + + /* The Modal (background) */ + .modal { + display: none; /* Hidden by default */ + position: fixed; /* Stay in place */ + z-index: 1; /* Sit on top */ + left: 0; + top: 0; + width: 100%; /* Full width */ + height: 100%; /* Full height */ + overflow: auto; /* Enable scroll if needed */ + background-color: #474e5d; + padding-top: 50px; + } + + /* Modal Content/Box */ + .modal-content { + background-color: #fefefe; + margin: 5% auto 15% auto; /* 5% from the top, 15% from the bottom and centered */ + border: 1px solid #888; + width: 80%; /* Could be more or less, depending on screen size */ + } + + /* Style the horizontal ruler */ + hr { + border: 1px solid #f1f1f1; + margin-bottom: 25px; + } + + /* The Modal Close Button (x) */ + #close { + position: absolute; + right: 35px; + top: 15px; + font-size: 40px; + font-weight: bold; + color: #f1f1f1; + } + + #close:hover, + #close:focus { + color: #f44336; + cursor: pointer; + } + + /* Clear floats */ + .clearfix::after { + content: ""; + clear: both; + display: table; + } + + /* Change styles for cancel button and delete button on extra small screens */ + @media screen and (max-width: 300px) { + .cancelbtn, .deletebtn { + width: 100%; + } + } + + .form-container{ + width: 90%; + } + + .deviceHeader{ + display: flex; + text-align: center; + } diff --git a/frontend/styles/frontpage.css b/frontend/styles/frontpage.css index f8cbe7a..f15df4b 100644 --- a/frontend/styles/frontpage.css +++ b/frontend/styles/frontpage.css @@ -4,7 +4,6 @@ } body { - margin: 0; font-family: sans-serif; } diff --git a/frontend/styles/home.css b/frontend/styles/home.css index 9eb7fac..a28e4d5 100644 --- a/frontend/styles/home.css +++ b/frontend/styles/home.css @@ -1,5 +1,4 @@ body { - margin: 0; font-family: Arial, Helvetica, sans-serif; } @@ -8,34 +7,11 @@ body { opacity: 100%; } -.topnav { - overflow: hidden; - background-color: #333; -} - -.topnav a { - float: left; - color: #f2f2f2; - text-align: center; - padding: 14px 16px; - text-decoration: none; - font-size: 17px; -} - -.topnav a:hover { - background-color: #ddd; - color: black; -} - -.topnav a.active { - background-color: #04aa6d; - color: white; -} - table { + margin: 20px; font-family: arial, sans-serif; border-collapse: collapse; - width: 100%; + width: 95%; } td, @@ -77,3 +53,8 @@ tr:nth-child(even) { .chartContainer{ margin: 20px; } + +.tableConatiner{ + display: flex; + justify-content: center; +} diff --git a/frontend/styles/profile.css b/frontend/styles/profile.css index 3d2c752..87f727d 100644 --- a/frontend/styles/profile.css +++ b/frontend/styles/profile.css @@ -49,8 +49,23 @@ h2{ padding: 20px; border: 1px solid #888; width: 80%; + -webkit-animation-name: animatetop; + -webkit-animation-duration: 0.4s; + animation-name: animatetop; + animation-duration: 0.4s } - + + /* Add Animation */ + @-webkit-keyframes animatetop { + from {top:-300px; opacity:0} + to {top:0; opacity:1} + } + + @keyframes animatetop { + from {top:-300px; opacity:0} + to {top:0; opacity:1} + } + /* The Close Button */ .close { color: #aaaaaa;