diff --git a/examples/YOLO_server_api/frontend/dist/assets/detection-A_EFsCI6.js b/examples/YOLO_server_api/frontend/dist/assets/detection-A_EFsCI6.js
deleted file mode 100644
index bc41aa1..0000000
--- a/examples/YOLO_server_api/frontend/dist/assets/detection-A_EFsCI6.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import{checkAccess as H,showAppropriateLinks as R,authHeaders as N}from"./common-Ceipz8Vt.js";import"./headerFooterLoader-LYlhFStm.js";const P="/api";document.addEventListener("DOMContentLoaded",()=>{H([]),R(),document.getElementById("logout-btn");const k=document.getElementById("detection-form"),m=document.getElementById("detection-error"),u=document.getElementById("detection-result"),r=document.getElementById("file-drop-area"),f=document.getElementById("image-input"),y=document.getElementById("remove-image-btn");let p=0,E=0;function L(){const e=document.getElementById("image-canvas");e.getContext("2d").clearRect(0,0,e.width,e.height),f.value="",u.textContent="",m.textContent="",y.style.display="none"}y.addEventListener("click",()=>{L()}),document.querySelector(".choose-file-btn").addEventListener("click",e=>{e.preventDefault(),f.click()}),r.addEventListener("dragover",e=>{e.preventDefault(),r.classList.add("dragover")}),r.addEventListener("dragleave",()=>{r.classList.remove("dragover")}),r.addEventListener("drop",e=>{if(e.preventDefault(),r.classList.remove("dragover"),e.dataTransfer.files&&e.dataTransfer.files.length>0){const a=e.dataTransfer.files[0];f.files=e.dataTransfer.files,x(a)}}),f.addEventListener("change",e=>{e.target.files&&e.target.files[0]&&x(e.target.files[0])});function x(e){const a=new FileReader;a.onload=()=>{const t=document.getElementById("image-canvas"),o=t.getContext("2d"),n=new Image;n.onload=()=>{p=n.width,E=n.height;const c=r.clientWidth-40,d=r.clientHeight-40;let{width:i,height:s}=n;if(i>c){const l=c/i;i=c,s*=l}if(s>d){const l=d/s;s=d,i*=l}t.width=i,t.height=s,o.clearRect(0,0,t.width,t.height),o.drawImage(n,0,0,i,s),y.style.display="inline-block"},n.src=a.result},a.readAsDataURL(e)}k.addEventListener("submit",async e=>{e.preventDefault(),m.textContent="",u.textContent="";const a=document.getElementById("model-select").value,t=f.files[0];if(!t){m.textContent="Please select an image.";return}const o=new FormData;o.append("image",t),o.append("model",a);try{const n=await fetch(`${P}/detect`,{method:"POST",headers:N(),body:o});if(!n.ok){const d=await n.json();m.textContent=d.detail||"Detection failed.";return}const c=await n.json();B(c),S(c)}catch(n){console.error(n),m.textContent="Error performing detection."}});function B(e){const a=document.getElementById("image-canvas"),t=a.getContext("2d"),o=["Hardhat","Mask","NO-Hardhat","NO-Mask","NO-Safety Vest","Person","Safety Cone","Safety Vest","machinery","vehicle"],n={Hardhat:"green","Safety Vest":"green",machinery:"yellow",vehicle:"yellow","NO-Hardhat":"red","NO-Safety Vest":"red",Person:"orange","Safety Cone":"pink"};e.forEach(([c,d,i,s,l,b])=>{const v=o[b],w=n[v]||"blue",I=a.width/p,C=a.height/E,g=c*I,h=d*C,O=i*I,D=s*C;t.strokeStyle=w,t.lineWidth=2,t.strokeRect(g,h,O-g,D-h),t.fillStyle=w,t.fillRect(g,h-20,t.measureText(v).width+10,20),t.fillStyle="black",t.font="14px Arial",t.fillText(v,g+5,h-5)})}function S(e){const a={},t=["Hardhat","Mask","NO-Hardhat","NO-Mask","NO-Safety Vest","Person","Safety Cone","Safety Vest","machinery","vehicle"];t.forEach(o=>a[o]=0),e.forEach(([o,n,c,d,i,s])=>{const l=t[s];l&&(a[l]+=1)}),u.textContent=Object.entries(a).filter(([o,n])=>n>0).map(([o,n])=>`${o}: ${n}`).join(`
-`)}});
diff --git a/examples/YOLO_server_api/frontend/dist/assets/detection-CjEUTsGE.js b/examples/YOLO_server_api/frontend/dist/assets/detection-CjEUTsGE.js
new file mode 100644
index 0000000..639b960
--- /dev/null
+++ b/examples/YOLO_server_api/frontend/dist/assets/detection-CjEUTsGE.js
@@ -0,0 +1,2 @@
+import{checkAccess as I,showAppropriateLinks as E,authHeaders as w}from"./common-Ceipz8Vt.js";import"./headerFooterLoader-CkcITKwD.js";const B="/api";let m=0,f=0;document.addEventListener("DOMContentLoaded",()=>{I([]),E();const e=document.getElementById("logout-btn"),n=document.getElementById("detection-form"),t=document.getElementById("detection-error"),o=document.getElementById("detection-result"),a=document.getElementById("file-drop-area"),c=document.getElementById("image-input"),s=document.getElementById("remove-image-btn"),i=document.querySelector(".choose-file-btn");C(e),L(s),S(i,c),k(a,c),b(c,a,s),D(n,c,t,o)});function C(e){e&&e.addEventListener("click",()=>{window.location.href="/login.html"})}function L(e){e.addEventListener("click",N)}function S(e,n){e&&e.addEventListener("click",t=>{t.preventDefault(),n.click()})}function k(e,n){e.addEventListener("dragover",O),e.addEventListener("dragleave",R),e.addEventListener("drop",t=>x(t,e,n))}function b(e,n,t){e.addEventListener("change",o=>{const a=o.target.files&&o.target.files[0];a&&g(a,n,t)})}function D(e,n,t,o){e.addEventListener("submit",async a=>{a.preventDefault(),H(t,o);const c=document.getElementById("model-select").value,s=n.files[0];if(!s){t.textContent="Please select an image.";return}await V(s,c,t,o)})}function O(e){e.preventDefault(),e.currentTarget.classList.add("dragover")}function R(e){e.currentTarget.classList.remove("dragover")}function x(e,n,t){e.preventDefault(),n.classList.remove("dragover");const o=e.dataTransfer.files&&e.dataTransfer.files[0];if(o){t.files=e.dataTransfer.files;const a=document.getElementById("remove-image-btn");g(o,n,a)}}function H(e,n){e.textContent="",n.textContent=""}function N(){const e=document.getElementById("image-canvas");e.getContext("2d").clearRect(0,0,e.width,e.height);const t=document.getElementById("image-input");t.value="";const o=document.getElementById("detection-result"),a=document.getElementById("detection-error");o.textContent="",a.textContent="";const c=document.getElementById("remove-image-btn");c.style.display="none"}function g(e,n,t){const o=new FileReader;o.onload=()=>P(o.result,n,t),o.readAsDataURL(e)}function P(e,n,t){const o=new Image;o.onload=()=>{m=o.width,f=o.height,T(o,n),t.style.display="inline-block"},o.src=e}function T(e,n){const t=document.getElementById("image-canvas"),o=t.getContext("2d"),{width:a,height:c}=F(e,n);t.width=a,t.height=c,o.clearRect(0,0,t.width,t.height),o.drawImage(e,0,0,a,c)}function F(e,n){const t=n.clientWidth-40,o=n.clientHeight-40;let{width:a,height:c}=e;return{width:a,height:c}=M(a,c,t,o),{width:a,height:c}}function M(e,n,t,o){if(e>t){const a=t/e;e=t,n*=a}if(n>o){const a=o/n;n=o,e*=a}return{width:e,height:n}}async function V(e,n,t,o){const a=new FormData;a.append("image",e),a.append("model",n);try{const c=await fetch(`${B}/detect`,{method:"POST",headers:w(),body:a});if(!c.ok){const i=await c.json();t.textContent=i.detail||"Detection failed.";return}const s=await c.json();j(s),W(s,o)}catch(c){console.error(c),t.textContent="Error performing detection."}}function j(e){const n=document.getElementById("image-canvas"),t=n.getContext("2d");e.forEach(o=>X(t,o,n))}function X(e,n,t){const o=["Hardhat","Mask","NO-Hardhat","NO-Mask","NO-Safety Vest","Person","Safety Cone","Safety Vest","machinery","vehicle"],a={Hardhat:"green","Safety Vest":"green",machinery:"yellow",vehicle:"yellow","NO-Hardhat":"red","NO-Safety Vest":"red",Person:"orange","Safety Cone":"pink"},[c,s,i,h,A,y]=n,r=o[y],d=a[r]||"blue",{scaledX1:l,scaledY1:u,scaledX2:v,scaledY2:p}=Y(c,s,i,h,t);$(e,l,u,v,p,d),U(e,r,l,u,d)}function Y(e,n,t,o,a){const c=a.width/m,s=a.height/f;return{scaledX1:e*c,scaledY1:n*s,scaledX2:t*c,scaledY2:o*s}}function $(e,n,t,o,a,c){e.strokeStyle=c,e.lineWidth=2,e.strokeRect(n,t,o-n,a-t)}function U(e,n,t,o,a){e.fillStyle=a,e.fillRect(t,o-20,e.measureText(n).width+10,20),e.fillStyle="black",e.font="14px Arial",e.fillText(n,t+5,o-5)}function W(e,n){const t=["Hardhat","Mask","NO-Hardhat","NO-Mask","NO-Safety Vest","Person","Safety Cone","Safety Vest","machinery","vehicle"],o=_(t);q(e,t,o),z(n,o)}function _(e){const n={};return e.forEach(t=>n[t]=0),n}function q(e,n,t){e.forEach(([,,,,,o])=>{const a=n[o];a&&(t[a]+=1)})}function z(e,n){e.textContent=Object.entries(n).filter(([t,o])=>o>0).map(([t,o])=>`${t}: ${o}`).join(`
+`)}
diff --git a/examples/YOLO_server_api/frontend/dist/assets/headerFooterLoader-CkcITKwD.js b/examples/YOLO_server_api/frontend/dist/assets/headerFooterLoader-CkcITKwD.js
new file mode 100644
index 0000000..906962a
--- /dev/null
+++ b/examples/YOLO_server_api/frontend/dist/assets/headerFooterLoader-CkcITKwD.js
@@ -0,0 +1 @@
+const g="modulepreload",E=function(e){return"/"+e},u={},y=function(t,o,l){let i=Promise.resolve();if(o&&o.length>0){document.getElementsByTagName("link");const n=document.querySelector("meta[property=csp-nonce]"),r=(n==null?void 0:n.nonce)||(n==null?void 0:n.getAttribute("nonce"));i=Promise.allSettled(o.map(c=>{if(c=E(c),c in u)return;u[c]=!0;const s=c.endsWith(".css"),m=s?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${c}"]${m}`))return;const a=document.createElement("link");if(a.rel=s?"stylesheet":g,s||(a.as="script"),a.crossOrigin="",a.href=c,r&&a.setAttribute("nonce",r),document.head.appendChild(a),s)return new Promise((f,h)=>{a.addEventListener("load",f),a.addEventListener("error",()=>h(new Error(`Unable to preload CSS for ${c}`)))})}))}function d(n){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=n,window.dispatchEvent(r),!r.defaultPrevented)throw n}return i.then(n=>{for(const r of n||[])r.status==="rejected"&&d(r.reason);return t().catch(d)})};document.addEventListener("DOMContentLoaded",async()=>{const e=document.getElementById("header-container"),t=document.getElementById("footer-container");e&&await v(e),t&&await p(t)});async function v(e){try{const o=await(await fetch("/header.html")).text();e.innerHTML=o,w()}catch(t){console.error("Error loading header:",t)}}function w(){const e=document.getElementById("logout-btn");e&&y(()=>import("./common-Ceipz8Vt.js"),[]).then(l=>{const{clearToken:i}=l;e.addEventListener("click",()=>{i(),window.location.href="/login.html"})});const t=document.getElementById("menu-toggle"),o=document.getElementById("nav-links");t&&o&&t.addEventListener("click",()=>{o.classList.toggle("expanded")})}async function p(e){try{const o=await(await fetch("/footer.html")).text();e.innerHTML=o,L()}catch(t){console.error("Error loading footer:",t)}}function L(){const e=document.getElementById("current-year");e&&(e.textContent=new Date().getFullYear())}
diff --git a/examples/YOLO_server_api/frontend/dist/assets/headerFooterLoader-LYlhFStm.js b/examples/YOLO_server_api/frontend/dist/assets/headerFooterLoader-LYlhFStm.js
deleted file mode 100644
index ab1b54c..0000000
--- a/examples/YOLO_server_api/frontend/dist/assets/headerFooterLoader-LYlhFStm.js
+++ /dev/null
@@ -1 +0,0 @@
-const g="modulepreload",E=function(i){return"/"+i},u={},y=function(a,e,c){let l=Promise.resolve();if(e&&e.length>0){document.getElementsByTagName("link");const t=document.querySelector("meta[property=csp-nonce]"),n=(t==null?void 0:t.nonce)||(t==null?void 0:t.getAttribute("nonce"));l=Promise.allSettled(e.map(o=>{if(o=E(o),o in u)return;u[o]=!0;const d=o.endsWith(".css"),m=d?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${o}"]${m}`))return;const r=document.createElement("link");if(r.rel=d?"stylesheet":g,d||(r.as="script"),r.crossOrigin="",r.href=o,n&&r.setAttribute("nonce",n),document.head.appendChild(r),d)return new Promise((h,f)=>{r.addEventListener("load",h),r.addEventListener("error",()=>f(new Error(`Unable to preload CSS for ${o}`)))})}))}function s(t){const n=new Event("vite:preloadError",{cancelable:!0});if(n.payload=t,window.dispatchEvent(n),!n.defaultPrevented)throw t}return l.then(t=>{for(const n of t||[])n.status==="rejected"&&s(n.reason);return a().catch(s)})};document.addEventListener("DOMContentLoaded",()=>{const i=document.getElementById("header-container"),a=document.getElementById("footer-container");i&&fetch("/header.html").then(e=>e.text()).then(e=>{i.innerHTML=e;const c=document.getElementById("logout-btn");c&&y(()=>import("./common-Ceipz8Vt.js"),[]).then(t=>{const{clearToken:n}=t;c.addEventListener("click",()=>{n(),window.location.href="/login.html"})});const l=document.getElementById("menu-toggle"),s=document.getElementById("nav-links");l&&s&&l.addEventListener("click",()=>{s.classList.toggle("expanded")})}).catch(e=>console.error("Error loading header:",e)),a&&fetch("/footer.html").then(e=>e.text()).then(e=>{a.innerHTML=e;const c=document.getElementById("current-year");c&&(c.textContent=new Date().getFullYear())}).catch(e=>console.error("Error loading footer:",e))});
diff --git a/examples/YOLO_server_api/frontend/dist/assets/login-6oBM0D2Y.js b/examples/YOLO_server_api/frontend/dist/assets/login-6oBM0D2Y.js
new file mode 100644
index 0000000..bcb625d
--- /dev/null
+++ b/examples/YOLO_server_api/frontend/dist/assets/login-6oBM0D2Y.js
@@ -0,0 +1 @@
+import{clearToken as s,setToken as i}from"./common-Ceipz8Vt.js";const d="/api";document.addEventListener("DOMContentLoaded",()=>{const n=document.getElementById("login-form"),e=document.getElementById("login-error");n.addEventListener("submit",t=>c(t,e))});async function c(n,e){n.preventDefault();const{username:t,password:o}=m();if(!t||!o){a(e,"Username and password are required.");return}try{await u(t,o),l()}catch(r){a(e,r.message||"Error logging in.")}}function m(){const n=document.getElementById("username").value.trim(),e=document.getElementById("password").value.trim();return{username:n,password:e}}async function u(n,e){s();const t=await fetch(`${d}/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:n,password:e})});if(!t.ok){const r=await t.json();throw new Error(r.detail||"Login failed.")}const o=await t.json();i(o.access_token)}function l(){window.location.href="./index.html"}function a(n,e){n.textContent=e}
diff --git a/examples/YOLO_server_api/frontend/dist/assets/login-DjA6hhVY.js b/examples/YOLO_server_api/frontend/dist/assets/login-DjA6hhVY.js
deleted file mode 100644
index 1738786..0000000
--- a/examples/YOLO_server_api/frontend/dist/assets/login-DjA6hhVY.js
+++ /dev/null
@@ -1 +0,0 @@
-import{clearToken as i,setToken as c}from"./common-Ceipz8Vt.js";const m="/api";document.addEventListener("DOMContentLoaded",()=>{const n=document.getElementById("login-form"),t=document.getElementById("login-error");n.addEventListener("submit",async o=>{o.preventDefault();const r=document.getElementById("username").value.trim(),a=document.getElementById("password").value.trim();try{i();const e=await fetch(`${m}/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:r,password:a})});if(!e.ok){const d=await e.json();t.textContent=d.detail||"Login failed";return}const s=await e.json();c(s.access_token),window.location.href="./index.html"}catch{t.textContent="Error logging in."}})});
diff --git a/examples/YOLO_server_api/frontend/dist/assets/main-C-9B3wGB.js b/examples/YOLO_server_api/frontend/dist/assets/main-C-9B3wGB.js
deleted file mode 100644
index 7540a48..0000000
--- a/examples/YOLO_server_api/frontend/dist/assets/main-C-9B3wGB.js
+++ /dev/null
@@ -1 +0,0 @@
-import{checkAccess as r,clearToken as s,getToken as d,getUsernameFromToken as i,getUserRoleFromToken as l}from"./common-Ceipz8Vt.js";import"./headerFooterLoader-LYlhFStm.js";document.addEventListener("DOMContentLoaded",()=>{r([]);const e=document.getElementById("logout-btn");e&&e.addEventListener("click",()=>{s(),window.location.href="./login.html"});const t=d(),o=document.getElementById("user-info");if(t){const n=i(),c=l();o.textContent=`Logged in as ${n} (Role: ${c})`}else o.textContent="You are not logged in."});
diff --git a/examples/YOLO_server_api/frontend/dist/assets/main-dtSFUXYT.js b/examples/YOLO_server_api/frontend/dist/assets/main-dtSFUXYT.js
new file mode 100644
index 0000000..6e12580
--- /dev/null
+++ b/examples/YOLO_server_api/frontend/dist/assets/main-dtSFUXYT.js
@@ -0,0 +1 @@
+import{checkAccess as c,clearToken as i,getToken as s,getUsernameFromToken as r,getUserRoleFromToken as d}from"./common-Ceipz8Vt.js";import"./headerFooterLoader-CkcITKwD.js";document.addEventListener("DOMContentLoaded",()=>{l(),u(),m()});function l(){c([])}function u(){const e=document.getElementById("logout-btn");e&&e.addEventListener("click",g)}function g(){i(),window.location.href="./login.html"}function m(){const e=s(),n=document.getElementById("user-info");if(e){const o=r(),t=d();n.textContent=`Logged in as ${o} (Role: ${t})`}else n.textContent="You are not logged in."}
diff --git a/examples/YOLO_server_api/frontend/dist/assets/model_management-BkAfeMO-.js b/examples/YOLO_server_api/frontend/dist/assets/model_management-BkAfeMO-.js
new file mode 100644
index 0000000..3f05af6
--- /dev/null
+++ b/examples/YOLO_server_api/frontend/dist/assets/model_management-BkAfeMO-.js
@@ -0,0 +1 @@
+import{checkAccess as r,authHeaders as a}from"./common-Ceipz8Vt.js";import"./headerFooterLoader-CkcITKwD.js";const i="/api";document.addEventListener("DOMContentLoaded",()=>{r(["admin","model_manager"]),m(),u()});function m(){const t=document.getElementById("model-file-update-form"),e=document.getElementById("model-update-error");t&&t.addEventListener("submit",async o=>{o.preventDefault(),e.textContent="";const n=s(e);if(n)try{await c(n,e)}catch{e.textContent="Error updating model file."}})}function s(t){const e=document.getElementById("model-name").value.trim(),n=document.getElementById("model-file").files[0];if(!n)return t.textContent="Please select a model file.",null;const d=new FormData;return d.append("model",e),d.append("file",n),d}async function c(t,e){const o=await fetch(`${i}/model_file_update`,{method:"POST",headers:a(),body:t});if(!o.ok){const n=await o.json();e.textContent=n.detail||"Failed to update model file.";return}alert("Model file updated successfully.")}function u(){const t=document.getElementById("get-new-model-form"),e=document.getElementById("get-new-model-error"),o=document.getElementById("model-file-content");t&&t.addEventListener("submit",async n=>{n.preventDefault(),f(e,o);const d=p();if(d)try{await g(d,e,o)}catch{e.textContent="Error retrieving model file."}})}function f(t,e){t.textContent="",e.textContent=""}function p(){return{model:document.getElementById("get-model-name").value.trim(),last_update_time:"1970-01-01"}}async function g(t,e,o){const n=await fetch(`${i}/get_new_model`,{method:"POST",headers:{"Content-Type":"application/json",...a()},body:JSON.stringify(t)});if(!n.ok){const l=await n.json();e.textContent=l.detail||"Failed to retrieve new model.";return}const d=await n.json();y(d,o)}function y(t,e){t.model_file?e.textContent=`Base64 Model File: ${t.model_file}`:e.textContent=t.message||"No model file available."}
diff --git a/examples/YOLO_server_api/frontend/dist/assets/model_management-BkVGVr4y.js b/examples/YOLO_server_api/frontend/dist/assets/model_management-BkVGVr4y.js
deleted file mode 100644
index 8b195bb..0000000
--- a/examples/YOLO_server_api/frontend/dist/assets/model_management-BkVGVr4y.js
+++ /dev/null
@@ -1 +0,0 @@
-import{checkAccess as p,authHeaders as i}from"./common-Ceipz8Vt.js";import"./headerFooterLoader-LYlhFStm.js";const s="/api";document.addEventListener("DOMContentLoaded",()=>{p(["admin","model_manager"]);const c=document.getElementById("model-file-update-form"),n=document.getElementById("model-update-error");c.addEventListener("submit",async a=>{a.preventDefault(),n.textContent="";const m=document.getElementById("model-name").value.trim(),e=document.getElementById("model-file").files[0];if(!e){n.textContent="Please select a model file.";return}const t=new FormData;t.append("model",m),t.append("file",e);try{const o=await fetch(`${s}/model_file_update`,{method:"POST",headers:i(),body:t});if(!o.ok){const u=await o.json();n.textContent=u.detail||"Failed to update model file.";return}alert("Model file updated successfully.")}catch{n.textContent="Error updating model file."}});const f=document.getElementById("get-new-model-form"),d=document.getElementById("get-new-model-error"),l=document.getElementById("model-file-content");f.addEventListener("submit",async a=>{a.preventDefault(),d.textContent="",l.textContent="";const m=document.getElementById("get-model-name").value.trim(),r="1970-01-01";try{const e=await fetch(`${s}/get_new_model`,{method:"POST",headers:{"Content-Type":"application/json",...i()},body:JSON.stringify({model:m,last_update_time:r})});if(!e.ok){const o=await e.json();d.textContent=o.detail||"Failed to retrieve new model.";return}const t=await e.json();t.model_file?l.textContent=`Base64 Model File: ${t.model_file}`:l.textContent=t.message}catch{d.textContent="Error retrieving model file."}})});
diff --git a/examples/YOLO_server_api/frontend/dist/assets/user_management-C9tlKTo9.js b/examples/YOLO_server_api/frontend/dist/assets/user_management-C9tlKTo9.js
deleted file mode 100644
index cbbbd03..0000000
--- a/examples/YOLO_server_api/frontend/dist/assets/user_management-C9tlKTo9.js
+++ /dev/null
@@ -1 +0,0 @@
-import{checkAccess as w,clearToken as h,authHeaders as s}from"./common-Ceipz8Vt.js";import"./headerFooterLoader-LYlhFStm.js";const o="/api";document.addEventListener("DOMContentLoaded",()=>{w(["admin"]);const l=document.getElementById("logout-btn");l&&l.addEventListener("click",()=>{h(),window.location.href="/login.html"});const p=document.getElementById("add-user-form"),d=document.getElementById("add-user-error");p.addEventListener("submit",async n=>{n.preventDefault(),d.textContent="";const a=document.getElementById("add-username").value.trim(),t=document.getElementById("add-password").value.trim(),e=document.getElementById("add-role").value;try{const r=await fetch(`${o}/add_user`,{method:"POST",headers:{"Content-Type":"application/json",...s()},body:JSON.stringify({username:a,password:t,role:e})});if(!r.ok){const v=await r.json();d.textContent=v.detail||"Failed to add user.";return}alert("User added successfully.")}catch{d.textContent="Error adding user."}});const y=document.getElementById("delete-user-form"),u=document.getElementById("delete-user-error");y.addEventListener("submit",async n=>{n.preventDefault(),u.textContent="";const a=document.getElementById("delete-username").value.trim();try{const t=await fetch(`${o}/delete_user`,{method:"POST",headers:{"Content-Type":"application/json",...s()},body:JSON.stringify({username:a})});if(!t.ok){const e=await t.json();u.textContent=e.detail||"Failed to delete user.";return}alert("User deleted successfully.")}catch{u.textContent="Error deleting user."}});const E=document.getElementById("update-username-form"),c=document.getElementById("update-username-error");E.addEventListener("submit",async n=>{n.preventDefault(),c.textContent="";const a=document.getElementById("old-username").value.trim(),t=document.getElementById("new-username").value.trim();try{const e=await fetch(`${o}/update_username`,{method:"PUT",headers:{"Content-Type":"application/json",...s()},body:JSON.stringify({old_username:a,new_username:t})});if(!e.ok){const r=await e.json();c.textContent=r.detail||"Failed to update username.";return}alert("Username updated successfully.")}catch{c.textContent="Error updating username."}});const g=document.getElementById("update-password-form"),m=document.getElementById("update-password-error");g.addEventListener("submit",async n=>{n.preventDefault(),m.textContent="";const a=document.getElementById("update-password-username").value.trim(),t=document.getElementById("new-password").value.trim();try{const e=await fetch(`${o}/update_password`,{method:"PUT",headers:{"Content-Type":"application/json",...s()},body:JSON.stringify({username:a,new_password:t})});if(!e.ok){const r=await e.json();m.textContent=r.detail||"Failed to update password.";return}alert("Password updated successfully.")}catch{m.textContent="Error updating password."}});const f=document.getElementById("set-active-status-form"),i=document.getElementById("set-active-status-error");f.addEventListener("submit",async n=>{n.preventDefault(),i.textContent="";const a=document.getElementById("active-status-username").value.trim(),t=document.getElementById("is-active").checked;try{const e=await fetch(`${o}/set_user_active_status`,{method:"PUT",headers:{"Content-Type":"application/json",...s()},body:JSON.stringify({username:a,is_active:t})});if(!e.ok){const r=await e.json();i.textContent=r.detail||"Failed to update active status.";return}alert("User active status updated successfully.")}catch{i.textContent="Error updating active status."}})});
diff --git a/examples/YOLO_server_api/frontend/dist/assets/user_management-CzKH3sMr.js b/examples/YOLO_server_api/frontend/dist/assets/user_management-CzKH3sMr.js
new file mode 100644
index 0000000..3569c77
--- /dev/null
+++ b/examples/YOLO_server_api/frontend/dist/assets/user_management-CzKH3sMr.js
@@ -0,0 +1 @@
+import{checkAccess as c,clearToken as m,authHeaders as l}from"./common-Ceipz8Vt.js";import"./headerFooterLoader-CkcITKwD.js";const r="/api";document.addEventListener("DOMContentLoaded",()=>{c(["admin"]),i(),p()});function i(){const e=document.getElementById("logout-btn");e&&e.addEventListener("click",()=>{m(),window.location.href="/login.html"})}function p(){a("add-user-form",f,"add-user-error"),a("delete-user-form",y,"delete-user-error"),a("update-username-form",w,"update-username-error"),a("update-password-form",v,"update-password-error"),a("set-active-status-form",g,"set-active-status-error")}function a(e,t,n){const s=document.getElementById(e),o=document.getElementById(n);s&&s.addEventListener("submit",async u=>{u.preventDefault(),o.textContent="";try{await t()}catch{o.textContent="An error occurred while processing the form."}})}async function f(){const e=document.getElementById("add-username").value.trim(),t=document.getElementById("add-password").value.trim(),n=document.getElementById("add-role").value;await d(`${r}/add_user`,"POST",{username:e,password:t,role:n}),alert("User added successfully.")}async function y(){const e=document.getElementById("delete-username").value.trim();await d(`${r}/delete_user`,"POST",{username:e}),alert("User deleted successfully.")}async function w(){const e=document.getElementById("old-username").value.trim(),t=document.getElementById("new-username").value.trim();await d(`${r}/update_username`,"PUT",{old_username:e,new_username:t}),alert("Username updated successfully.")}async function v(){const e=document.getElementById("update-password-username").value.trim(),t=document.getElementById("new-password").value.trim();await d(`${r}/update_password`,"PUT",{username:e,new_password:t}),alert("Password updated successfully.")}async function g(){const e=document.getElementById("active-status-username").value.trim(),t=document.getElementById("is-active").checked;await d(`${r}/set_user_active_status`,"PUT",{username:e,is_active:t}),alert("User active status updated successfully.")}async function d(e,t,n){const s=await fetch(e,{method:t,headers:{"Content-Type":"application/json",...l()},body:JSON.stringify(n)});if(!s.ok){const o=await s.json();throw new Error(o.detail||"Request failed.")}}
diff --git a/examples/YOLO_server_api/frontend/dist/detection.html b/examples/YOLO_server_api/frontend/dist/detection.html
index eff0d87..6591b5e 100644
--- a/examples/YOLO_server_api/frontend/dist/detection.html
+++ b/examples/YOLO_server_api/frontend/dist/detection.html
@@ -11,9 +11,9 @@
-
+
-
+
diff --git a/examples/YOLO_server_api/frontend/dist/index.html b/examples/YOLO_server_api/frontend/dist/index.html
index 24826d8..ebcbf26 100644
--- a/examples/YOLO_server_api/frontend/dist/index.html
+++ b/examples/YOLO_server_api/frontend/dist/index.html
@@ -15,9 +15,9 @@
-
+
-
+
diff --git a/examples/YOLO_server_api/frontend/dist/login.html b/examples/YOLO_server_api/frontend/dist/login.html
index 6793f18..e1f487b 100644
--- a/examples/YOLO_server_api/frontend/dist/login.html
+++ b/examples/YOLO_server_api/frontend/dist/login.html
@@ -11,7 +11,7 @@
-
+
diff --git a/examples/YOLO_server_api/frontend/dist/model_management.html b/examples/YOLO_server_api/frontend/dist/model_management.html
index 0c7cea2..d596455 100644
--- a/examples/YOLO_server_api/frontend/dist/model_management.html
+++ b/examples/YOLO_server_api/frontend/dist/model_management.html
@@ -11,9 +11,9 @@
-
+
-
+
diff --git a/examples/YOLO_server_api/frontend/dist/user_management.html b/examples/YOLO_server_api/frontend/dist/user_management.html
index c67d3d3..25bb95e 100644
--- a/examples/YOLO_server_api/frontend/dist/user_management.html
+++ b/examples/YOLO_server_api/frontend/dist/user_management.html
@@ -16,9 +16,9 @@
-
+
-
+
diff --git a/examples/YOLO_server_api/frontend/public/js/auth.js b/examples/YOLO_server_api/frontend/public/js/auth.js
index f55f929..7038942 100644
--- a/examples/YOLO_server_api/frontend/public/js/auth.js
+++ b/examples/YOLO_server_api/frontend/public/js/auth.js
@@ -3,45 +3,84 @@ const API_URL = '/api'; // Base path for the backend API
// Wait until the DOM content is fully loaded
document.addEventListener('DOMContentLoaded', () => {
- // Select the login form and error message elements
const form = document.getElementById('login-form');
const errorMsg = document.getElementById('login-error');
- // Attach an event listener to handle form submission
- form.addEventListener('submit', async (e) => {
- e.preventDefault(); // Prevent the default form submission behaviour
-
- // Retrieve the username and password from the form inputs
- const username = document.getElementById('username').value.trim();
- const password = document.getElementById('password').value.trim();
-
- try {
- // Clear any existing tokens before making a new login request
- clearToken();
-
- // Send a POST request to the API with the username and password
- const response = await fetch(`${API_URL}/token`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' }, // Set the request header for JSON
- body: JSON.stringify({ username, password }) // Convert credentials to a JSON string
- });
-
- // Check if the response indicates an unsuccessful login
- if (!response.ok) {
- const data = await response.json();
- errorMsg.textContent = data.detail || 'Login failed'; // Display the error message
- return; // Exit the function
- }
-
- // Parse the response JSON to retrieve the token
- const data = await response.json();
- setToken(data.access_token); // Store the token in localStorage
-
- // Redirect to the homepage after successful login
- window.location.href = './index.html';
- } catch (err) {
- // Handle network or other unexpected errors
- errorMsg.textContent = 'Error logging in.';
- }
- });
+ form.addEventListener('submit', (e) => handleLoginFormSubmit(e, errorMsg));
});
+
+/**
+ * Handles the login form submission.
+ *
+ * @param {Event} e - The form submit event.
+ * @param {HTMLElement} errorMsg - The error message element.
+ */
+async function handleLoginFormSubmit(e, errorMsg) {
+ e.preventDefault(); // Prevent the default form submission behaviour
+
+ const { username, password } = getFormCredentials();
+ if (!username || !password) {
+ displayError(errorMsg, 'Username and password are required.');
+ return;
+ }
+
+ try {
+ await login(username, password);
+ redirectToHomePage();
+ } catch (err) {
+ displayError(errorMsg, err.message || 'Error logging in.');
+ }
+}
+
+/**
+ * Retrieves the username and password from the form inputs.
+ *
+ * @returns {Object} An object containing username and password.
+ */
+function getFormCredentials() {
+ const username = document.getElementById('username').value.trim();
+ const password = document.getElementById('password').value.trim();
+ return { username, password };
+}
+
+/**
+ * Logs in the user by sending a POST request to the API.
+ *
+ * @param {string} username - The username.
+ * @param {string} password - The password.
+ * @throws Will throw an error if the login fails.
+ */
+async function login(username, password) {
+ clearToken(); // Clear any existing tokens before making a new login request
+
+ const response = await fetch(`${API_URL}/token`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ username, password })
+ });
+
+ if (!response.ok) {
+ const data = await response.json();
+ throw new Error(data.detail || 'Login failed.');
+ }
+
+ const data = await response.json();
+ setToken(data.access_token); // Store the token in localStorage
+}
+
+/**
+ * Redirects the user to the homepage.
+ */
+function redirectToHomePage() {
+ window.location.href = './index.html';
+}
+
+/**
+ * Displays an error message in the specified element.
+ *
+ * @param {HTMLElement} element - The element to display the error message in.
+ * @param {string} message - The error message to display.
+ */
+function displayError(element, message) {
+ element.textContent = message;
+}
diff --git a/examples/YOLO_server_api/frontend/public/js/detection.js b/examples/YOLO_server_api/frontend/public/js/detection.js
index f3885e0..ceb8cdb 100644
--- a/examples/YOLO_server_api/frontend/public/js/detection.js
+++ b/examples/YOLO_server_api/frontend/public/js/detection.js
@@ -1,13 +1,13 @@
import { checkAccess, authHeaders, showAppropriateLinks } from './common.js';
+
const API_URL = '/api'; // Base path for the backend API
+let originalImageWidth = 0;
+let originalImageHeight = 0;
-// Wait until the DOM content is fully loaded
document.addEventListener('DOMContentLoaded', () => {
- // Check the user's access and show links based on their role
checkAccess([]);
showAppropriateLinks();
- // Select key DOM elements
const logoutBtn = document.getElementById('logout-btn');
const form = document.getElementById('detection-form');
const detectionError = document.getElementById('detection-error');
@@ -15,200 +15,294 @@ document.addEventListener('DOMContentLoaded', () => {
const fileDropArea = document.getElementById('file-drop-area');
const imageInput = document.getElementById('image-input');
const removeImageBtn = document.getElementById('remove-image-btn');
+ const chooseFileBtn = document.querySelector('.choose-file-btn');
- // Variables to store the original dimensions of the uploaded image
- let originalImageWidth = 0;
- let originalImageHeight = 0;
-
- // Function to remove the currently uploaded image and clear related content
- function removeImage() {
- const canvas = document.getElementById('image-canvas');
- const ctx = canvas.getContext('2d');
- ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
- imageInput.value = ''; // Reset the file input
- detectionResult.textContent = ''; // Clear the detection results
- detectionError.textContent = ''; // Clear any error messages
- removeImageBtn.style.display = 'none'; // Hide the "Remove Image" button
- }
+ setupLogoutButton(logoutBtn);
+ setupRemoveImageButton(removeImageBtn);
+ setupChooseFileButton(chooseFileBtn, imageInput);
+ setupFileDrop(fileDropArea, imageInput);
+ setupFileInputChange(imageInput, fileDropArea, removeImageBtn);
+ setupFormSubmission(form, imageInput, detectionError, detectionResult);
+});
- // Attach an event listener to the "Remove Image" button
- removeImageBtn.addEventListener('click', () => {
- removeImage();
+/** Set up the logout button event. */
+function setupLogoutButton(logoutBtn) {
+ if (!logoutBtn) return;
+ logoutBtn.addEventListener('click', () => {
+ window.location.href = '/login.html';
});
+}
- // Allow users to click the "Choose File" button to open the file selector
- const chooseFileBtn = document.querySelector('.choose-file-btn');
- chooseFileBtn.addEventListener('click', (e) => {
- e.preventDefault(); // Prevent default behaviour
- imageInput.click(); // Trigger the file input
- });
+/** Set up the remove image button event. */
+function setupRemoveImageButton(removeImageBtn) {
+ removeImageBtn.addEventListener('click', removeImage);
+}
- // Handle drag-and-drop events for the file drop area
- fileDropArea.addEventListener('dragover', (e) => {
- e.preventDefault(); // Prevent the default drag behaviour
- fileDropArea.classList.add('dragover'); // Highlight the drop area
- });
-
- fileDropArea.addEventListener('dragleave', () => {
- fileDropArea.classList.remove('dragover'); // Remove the highlight
+/** Set up the choose file button to trigger file input. */
+function setupChooseFileButton(chooseFileBtn, imageInput) {
+ if (!chooseFileBtn) return;
+ chooseFileBtn.addEventListener('click', (e) => {
+ e.preventDefault();
+ imageInput.click();
});
+}
- fileDropArea.addEventListener('drop', (e) => {
- e.preventDefault(); // Prevent the default drop behaviour
- fileDropArea.classList.remove('dragover'); // Remove the highlight
- if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
- const file = e.dataTransfer.files[0];
- imageInput.files = e.dataTransfer.files; // Set the dropped file to the input
- showImagePreview(file); // Display the preview
- }
- });
+/** Set up drag-and-drop file handling. */
+function setupFileDrop(fileDropArea, imageInput) {
+ fileDropArea.addEventListener('dragover', handleDragOver);
+ fileDropArea.addEventListener('dragleave', handleDragLeave);
+ fileDropArea.addEventListener('drop', (e) => handleFileDrop(e, fileDropArea, imageInput));
+}
- // Handle file input changes
+/** Set up file input change event. */
+function setupFileInputChange(imageInput, fileDropArea, removeImageBtn) {
imageInput.addEventListener('change', (e) => {
- if (e.target.files && e.target.files[0]) {
- showImagePreview(e.target.files[0]); // Display the preview
+ const file = e.target.files && e.target.files[0];
+ if (file) {
+ showImagePreview(file, fileDropArea, removeImageBtn);
}
});
+}
- // Function to display a preview of the uploaded image on the canvas
- function showImagePreview(file) {
- const reader = new FileReader();
- reader.onload = () => {
- const canvas = document.getElementById('image-canvas');
- const ctx = canvas.getContext('2d');
- const img = new Image();
- img.onload = () => {
- originalImageWidth = img.width;
- originalImageHeight = img.height;
-
- // Scale the image to fit within the drop area
- const maxWidth = fileDropArea.clientWidth - 40; // Allow for padding
- const maxHeight = fileDropArea.clientHeight - 40;
- let { width, height } = img;
-
- // Adjust width and height to maintain the aspect ratio
- if (width > maxWidth) {
- const ratio = maxWidth / width;
- width = maxWidth;
- height *= ratio;
- }
- if (height > maxHeight) {
- const ratio = maxHeight / height;
- height = maxHeight;
- width *= ratio;
- }
-
- // Set canvas dimensions and draw the image
- canvas.width = width;
- canvas.height = height;
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- ctx.drawImage(img, 0, 0, width, height);
- removeImageBtn.style.display = 'inline-block'; // Show the "Remove Image" button
- };
- img.src = reader.result;
- };
- reader.readAsDataURL(file); // Read the file as a data URL
- }
-
- // Handle form submission for detection requests
+/** Set up form submission for detection. */
+function setupFormSubmission(form, imageInput, detectionError, detectionResult) {
form.addEventListener('submit', async (e) => {
- e.preventDefault(); // Prevent the default form submission
- detectionError.textContent = ''; // Clear any previous errors
- detectionResult.textContent = ''; // Clear any previous results
+ e.preventDefault();
+ clearMessages(detectionError, detectionResult);
+
+ const model = document.getElementById('model-select').value;
+ const file = imageInput.files[0];
- const model = document.getElementById('model-select').value; // Get the selected model
- const file = imageInput.files[0]; // Get the uploaded file
if (!file) {
- detectionError.textContent = 'Please select an image.'; // Display an error if no file is selected
+ detectionError.textContent = 'Please select an image.';
return;
}
- const formData = new FormData(); // Create a FormData object
- formData.append('image', file); // Append the image file
- formData.append('model', model); // Append the selected model
-
- try {
- const response = await fetch(`${API_URL}/detect`, {
- method: 'POST',
- headers: authHeaders(), // Add authentication headers
- body: formData // Send the form data
- });
-
- if (!response.ok) {
- const data = await response.json();
- detectionError.textContent = data.detail || 'Detection failed.'; // Display an error message
- return;
- }
-
- const results = await response.json();
- drawDetectionResults(results); // Draw the detection results on the image
- displayObjectCounts(results); // Display the counts of detected objects
- } catch (err) {
- console.error(err);
- detectionError.textContent = 'Error performing detection.'; // Display a generic error message
- }
+ await performDetection(file, model, detectionError, detectionResult);
});
+}
- // Draw detection results on the canvas
- function drawDetectionResults(results) {
- const canvas = document.getElementById('image-canvas');
- const ctx = canvas.getContext('2d');
-
- const names = ['Hardhat', 'Mask', 'NO-Hardhat', 'NO-Mask', 'NO-Safety Vest', 'Person', 'Safety Cone', 'Safety Vest', 'machinery', 'vehicle'];
- const colors = {
- 'Hardhat': 'green',
- 'Safety Vest': 'green',
- 'machinery': 'yellow',
- 'vehicle': 'yellow',
- 'NO-Hardhat': 'red',
- 'NO-Safety Vest': 'red',
- 'Person': 'orange',
- 'Safety Cone': 'pink'
- };
-
- results.forEach(([x1, y1, x2, y2, confidence, classId]) => {
- const label = names[classId];
- const color = colors[label] || 'blue';
-
- // Scale the bounding box coordinates to fit the canvas
- const scaleX = canvas.width / originalImageWidth;
- const scaleY = canvas.height / originalImageHeight;
- const scaledX1 = x1 * scaleX;
- const scaledY1 = y1 * scaleY;
- const scaledX2 = x2 * scaleX;
- const scaledY2 = y2 * scaleY;
-
- // Draw the bounding box
- ctx.strokeStyle = color;
- ctx.lineWidth = 2;
- ctx.strokeRect(scaledX1, scaledY1, scaledX2 - scaledX1, scaledY2 - scaledY1);
-
- // Draw the label background
- ctx.fillStyle = color;
- ctx.fillRect(scaledX1, scaledY1 - 20, ctx.measureText(label).width + 10, 20);
-
- // Draw the label text
- ctx.fillStyle = 'black';
- ctx.font = '14px Arial';
- ctx.fillText(label, scaledX1 + 5, scaledY1 - 5); // Display the object label
- });
+/** Handle drag over event. */
+function handleDragOver(e) {
+ e.preventDefault();
+ e.currentTarget.classList.add('dragover');
+}
+
+/** Handle drag leave event. */
+function handleDragLeave(e) {
+ e.currentTarget.classList.remove('dragover');
+}
+
+/** Handle file drop event. */
+function handleFileDrop(e, fileDropArea, imageInput) {
+ e.preventDefault();
+ fileDropArea.classList.remove('dragover');
+ const file = e.dataTransfer.files && e.dataTransfer.files[0];
+ if (file) {
+ imageInput.files = e.dataTransfer.files;
+ const removeImageBtn = document.getElementById('remove-image-btn');
+ showImagePreview(file, fileDropArea, removeImageBtn);
+ }
+}
+
+/** Clear error and result messages. */
+function clearMessages(detectionError, detectionResult) {
+ detectionError.textContent = '';
+ detectionResult.textContent = '';
+}
+
+/** Remove the currently uploaded image and clear related content. */
+function removeImage() {
+ const canvas = document.getElementById('image-canvas');
+ const ctx = canvas.getContext('2d');
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ const imageInput = document.getElementById('image-input');
+ imageInput.value = '';
+
+ const detectionResult = document.getElementById('detection-result');
+ const detectionError = document.getElementById('detection-error');
+ detectionResult.textContent = '';
+ detectionError.textContent = '';
+
+ const removeImageBtn = document.getElementById('remove-image-btn');
+ removeImageBtn.style.display = 'none';
+}
+
+/** Display a preview of the uploaded image on the canvas. */
+function showImagePreview(file, fileDropArea, removeImageBtn) {
+ const reader = new FileReader();
+ reader.onload = () => loadImagePreview(reader.result, fileDropArea, removeImageBtn);
+ reader.readAsDataURL(file);
+}
+
+/** Load image preview once file is read. */
+function loadImagePreview(imageSrc, fileDropArea, removeImageBtn) {
+ const img = new Image();
+ img.onload = () => {
+ originalImageWidth = img.width;
+ originalImageHeight = img.height;
+ drawScaledImage(img, fileDropArea);
+ removeImageBtn.style.display = 'inline-block';
+ };
+ img.src = imageSrc;
+}
+
+/** Draw the scaled image on the canvas. */
+function drawScaledImage(img, fileDropArea) {
+ const canvas = document.getElementById('image-canvas');
+ const ctx = canvas.getContext('2d');
+
+ const { width, height } = calculateScaledDimensions(img, fileDropArea);
+ canvas.width = width;
+ canvas.height = height;
+
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.drawImage(img, 0, 0, width, height);
+}
+
+/** Calculate scaled dimensions for the image to fit the drop area. */
+function calculateScaledDimensions(img, fileDropArea) {
+ const maxWidth = fileDropArea.clientWidth - 40;
+ const maxHeight = fileDropArea.clientHeight - 40;
+ let { width, height } = img;
+
+ ({ width, height } = scaleDimension(width, height, maxWidth, maxHeight));
+ return { width, height };
+}
+
+/** Scale dimensions to maintain aspect ratio within maxWidth and maxHeight. */
+function scaleDimension(width, height, maxWidth, maxHeight) {
+ if (width > maxWidth) {
+ const ratio = maxWidth / width;
+ width = maxWidth;
+ height *= ratio;
}
+ if (height > maxHeight) {
+ const ratio = maxHeight / height;
+ height = maxHeight;
+ width *= ratio;
+ }
+ return { width, height };
+}
- // Display the counts of detected objects
- function displayObjectCounts(results) {
- const counts = {};
- const names = ['Hardhat', 'Mask', 'NO-Hardhat', 'NO-Mask', 'NO-Safety Vest', 'Person', 'Safety Cone', 'Safety Vest', 'machinery', 'vehicle'];
- names.forEach(name => counts[name] = 0); // Initialise counts for all object names
+/** Perform the detection request to the backend. */
+async function performDetection(file, model, detectionError, detectionResult) {
+ const formData = new FormData();
+ formData.append('image', file);
+ formData.append('model', model);
- results.forEach(([x1, y1, x2, y2, confidence, classId]) => {
- const label = names[classId];
- if (label) counts[label] += 1; // Increment the count for the detected object
+ try {
+ const response = await fetch(`${API_URL}/detect`, {
+ method: 'POST',
+ headers: authHeaders(),
+ body: formData
});
- // Display the object counts
- detectionResult.textContent = Object.entries(counts)
- .filter(([_, count]) => count > 0) // Only show objects that were detected
- .map(([name, count]) => `${name}: ${count}`)
- .join('\n');
+ if (!response.ok) {
+ const data = await response.json();
+ detectionError.textContent = data.detail || 'Detection failed.';
+ return;
+ }
+
+ const results = await response.json();
+ drawDetectionResults(results);
+ displayObjectCounts(results, detectionResult);
+ } catch (err) {
+ console.error(err);
+ detectionError.textContent = 'Error performing detection.';
}
-});
+}
+
+/** Draw detection results on the canvas. */
+function drawDetectionResults(results) {
+ const canvas = document.getElementById('image-canvas');
+ const ctx = canvas.getContext('2d');
+
+ results.forEach((detection) => drawSingleDetection(ctx, detection, canvas));
+}
+
+/** Draw a single detection result. */
+function drawSingleDetection(ctx, detection, canvas) {
+ const names = ['Hardhat', 'Mask', 'NO-Hardhat', 'NO-Mask', 'NO-Safety Vest', 'Person', 'Safety Cone', 'Safety Vest', 'machinery', 'vehicle'];
+ const colors = {
+ 'Hardhat': 'green',
+ 'Safety Vest': 'green',
+ 'machinery': 'yellow',
+ 'vehicle': 'yellow',
+ 'NO-Hardhat': 'red',
+ 'NO-Safety Vest': 'red',
+ 'Person': 'orange',
+ 'Safety Cone': 'pink'
+ };
+
+ const [x1, y1, x2, y2, confidence, classId] = detection;
+ const label = names[classId];
+ const color = colors[label] || 'blue';
+
+ const { scaledX1, scaledY1, scaledX2, scaledY2 } = scaleCoordinates(x1, y1, x2, y2, canvas);
+
+ drawBoundingBox(ctx, scaledX1, scaledY1, scaledX2, scaledY2, color);
+ drawLabel(ctx, label, scaledX1, scaledY1, color);
+}
+
+/** Scale coordinates to fit the canvas. */
+function scaleCoordinates(x1, y1, x2, y2, canvas) {
+ const scaleX = canvas.width / originalImageWidth;
+ const scaleY = canvas.height / originalImageHeight;
+
+ return {
+ scaledX1: x1 * scaleX,
+ scaledY1: y1 * scaleY,
+ scaledX2: x2 * scaleX,
+ scaledY2: y2 * scaleY
+ };
+}
+
+/** Draw bounding box around the detected object. */
+function drawBoundingBox(ctx, x1, y1, x2, y2, color) {
+ ctx.strokeStyle = color;
+ ctx.lineWidth = 2;
+ ctx.strokeRect(x1, y1, x2 - x1, y2 - y1);
+}
+
+/** Draw label above the bounding box. */
+function drawLabel(ctx, label, x1, y1, color) {
+ ctx.fillStyle = color;
+ ctx.fillRect(x1, y1 - 20, ctx.measureText(label).width + 10, 20);
+
+ ctx.fillStyle = 'black';
+ ctx.font = '14px Arial';
+ ctx.fillText(label, x1 + 5, y1 - 5);
+}
+
+/** Display counts of detected objects. */
+function displayObjectCounts(results, detectionResult) {
+ const names = ['Hardhat', 'Mask', 'NO-Hardhat', 'NO-Mask', 'NO-Safety Vest', 'Person', 'Safety Cone', 'Safety Vest', 'machinery', 'vehicle'];
+ const counts = initializeCounts(names);
+ countDetections(results, names, counts);
+ showCounts(detectionResult, counts);
+}
+
+/** Initialize counts for each label. */
+function initializeCounts(names) {
+ const counts = {};
+ names.forEach(name => counts[name] = 0);
+ return counts;
+}
+
+/** Count detections per label. */
+function countDetections(results, names, counts) {
+ results.forEach(([, , , , , classId]) => {
+ const label = names[classId];
+ if (label) counts[label] += 1;
+ });
+}
+
+/** Show counts in the detection result area. */
+function showCounts(detectionResult, counts) {
+ detectionResult.textContent = Object.entries(counts)
+ .filter(([_, count]) => count > 0)
+ .map(([name, count]) => `${name}: ${count}`)
+ .join('\n');
+}
diff --git a/examples/YOLO_server_api/frontend/public/js/headerFooterLoader.js b/examples/YOLO_server_api/frontend/public/js/headerFooterLoader.js
index f5428fc..9918a36 100644
--- a/examples/YOLO_server_api/frontend/public/js/headerFooterLoader.js
+++ b/examples/YOLO_server_api/frontend/public/js/headerFooterLoader.js
@@ -1,55 +1,62 @@
-// Wait for the DOM content to be fully loaded
-document.addEventListener('DOMContentLoaded', () => {
- // Select the containers for the header and footer
+document.addEventListener('DOMContentLoaded', async () => {
const headerContainer = document.getElementById('header-container');
const footerContainer = document.getElementById('footer-container');
- // Load the Header
if (headerContainer) {
- fetch('/header.html') // Fetch the header HTML file
- .then(response => response.text()) // Convert the response to text
- .then(html => {
- headerContainer.innerHTML = html; // Insert the header content into the container
+ await loadHeader(headerContainer);
+ }
- // Bind the Logout button event
- const logoutBtn = document.getElementById('logout-btn');
- if (logoutBtn) {
- // Dynamically import the common.js module to access clearToken
- import('/js/common.js').then(module => {
- const { clearToken } = module;
- logoutBtn.addEventListener('click', () => {
- clearToken(); // Clear the stored token
- window.location.href = '/login.html'; // Redirect to the login page
- });
- });
- }
+ if (footerContainer) {
+ await loadFooter(footerContainer);
+ }
+});
- // Bind the Menu Toggle button for mobile navigation
- const menuToggle = document.getElementById('menu-toggle');
- const navLinks = document.getElementById('nav-links');
- if (menuToggle && navLinks) {
- // Toggle the visibility of navigation links when the button is clicked
- menuToggle.addEventListener('click', () => {
- navLinks.classList.toggle('expanded'); // Add or remove the 'expanded' class
- });
- }
- })
- .catch(err => console.error('Error loading header:', err)); // Log any errors that occur while fetching the header
+async function loadHeader(headerContainer) {
+ try {
+ const response = await fetch('/header.html');
+ const html = await response.text();
+ headerContainer.innerHTML = html;
+ bindHeaderEvents();
+ } catch (err) {
+ console.error('Error loading header:', err);
}
+}
- // Load the Footer
- if (footerContainer) {
- fetch('/footer.html') // Fetch the footer HTML file
- .then(response => response.text()) // Convert the response to text
- .then(html => {
- footerContainer.innerHTML = html; // Insert the footer content into the container
+function bindHeaderEvents() {
+ const logoutBtn = document.getElementById('logout-btn');
+ if (logoutBtn) {
+ import('/js/common.js').then(module => {
+ const { clearToken } = module;
+ logoutBtn.addEventListener('click', () => {
+ clearToken();
+ window.location.href = '/login.html';
+ });
+ });
+ }
- // Dynamically set the current year in the footer
- const yearSpan = document.getElementById('current-year');
- if (yearSpan) {
- yearSpan.textContent = new Date().getFullYear(); // Get and display the current year
- }
- })
- .catch(err => console.error('Error loading footer:', err)); // Log any errors that occur while fetching the footer
+ const menuToggle = document.getElementById('menu-toggle');
+ const navLinks = document.getElementById('nav-links');
+ if (menuToggle && navLinks) {
+ menuToggle.addEventListener('click', () => {
+ navLinks.classList.toggle('expanded');
+ });
}
-});
+}
+
+async function loadFooter(footerContainer) {
+ try {
+ const response = await fetch('/footer.html');
+ const html = await response.text();
+ footerContainer.innerHTML = html;
+ updateFooterYear();
+ } catch (err) {
+ console.error('Error loading footer:', err);
+ }
+}
+
+function updateFooterYear() {
+ const yearSpan = document.getElementById('current-year');
+ if (yearSpan) {
+ yearSpan.textContent = new Date().getFullYear();
+ }
+}
diff --git a/examples/YOLO_server_api/frontend/public/js/main.js b/examples/YOLO_server_api/frontend/public/js/main.js
index a55e43f..0d3d8a7 100644
--- a/examples/YOLO_server_api/frontend/public/js/main.js
+++ b/examples/YOLO_server_api/frontend/public/js/main.js
@@ -1,34 +1,50 @@
import { getUsernameFromToken, getUserRoleFromToken, getToken, clearToken, checkAccess } from './common.js';
-// Wait for the DOM content to be fully loaded
document.addEventListener('DOMContentLoaded', () => {
- // Check access permissions
+ // Initialize the page functionalities
+ initAccessCheck();
+ initLogoutButton();
+ displayUserInfo();
+});
+
+/**
+ * Check user access permissions.
+ */
+function initAccessCheck() {
// No specific role is required, meaning any logged-in user can access this page
checkAccess([]);
+}
- // Handle the Logout button functionality
+/**
+ * Set up the logout button functionality.
+ */
+function initLogoutButton() {
const logoutBtn = document.getElementById('logout-btn');
if (logoutBtn) {
- logoutBtn.addEventListener('click', () => {
- clearToken(); // Clear the stored token
- window.location.href = './login.html'; // Redirect the user to the login page
- });
+ logoutBtn.addEventListener('click', handleLogout);
}
+}
- // Retrieve the user's token from localStorage
- const token = getToken();
+/**
+ * Handle logout functionality.
+ */
+function handleLogout() {
+ clearToken(); // Clear the stored token
+ window.location.href = './login.html'; // Redirect the user to the login page
+}
- // Display user information or a message if the user is not logged in
+/**
+ * Display the user's information or a message if the user is not logged in.
+ */
+function displayUserInfo() {
+ const token = getToken();
const userInfoDiv = document.getElementById('user-info');
+
if (token) {
- // If a token exists, decode it to retrieve the username and role
const username = getUsernameFromToken();
const role = getUserRoleFromToken();
-
- // Display the user's username and role
userInfoDiv.textContent = `Logged in as ${username} (Role: ${role})`;
} else {
- // Display a message indicating the user is not logged in
userInfoDiv.textContent = 'You are not logged in.';
}
-});
+}
diff --git a/examples/YOLO_server_api/frontend/public/js/model_management.js b/examples/YOLO_server_api/frontend/public/js/model_management.js
index 9827a5f..e8e929c 100644
--- a/examples/YOLO_server_api/frontend/public/js/model_management.js
+++ b/examples/YOLO_server_api/frontend/public/js/model_management.js
@@ -1,96 +1,130 @@
import { checkAccess, authHeaders, clearToken } from './common.js';
const API_URL = '/api'; // Base path for the backend API
-// Wait for the DOM content to be fully loaded
document.addEventListener('DOMContentLoaded', () => {
- // Restrict access to users with the roles 'admin' or 'model_manager'
- checkAccess(['admin', 'model_manager']);
+ checkAccess(['admin', 'model_manager']); // Restrict access to specific roles
+ setupModelFileUpdate();
+ setupGetNewModel();
+});
- // Handle the model file update form submission
+/** Setup for model file update form submission */
+function setupModelFileUpdate() {
const modelFileUpdateForm = document.getElementById('model-file-update-form');
const modelUpdateError = document.getElementById('model-update-error');
+
+ if (!modelFileUpdateForm) return;
+
modelFileUpdateForm.addEventListener('submit', async (e) => {
- e.preventDefault(); // Prevent the default form submission behaviour
+ e.preventDefault();
modelUpdateError.textContent = ''; // Clear any previous error messages
- // Retrieve the model name and file from the form
- const model = document.getElementById('model-name').value.trim();
- const fileInput = document.getElementById('model-file');
- const file = fileInput.files[0];
- if (!file) {
- modelUpdateError.textContent = 'Please select a model file.'; // Show an error if no file is selected
- return;
- }
-
- // Create a FormData object to hold the form data
- const formData = new FormData();
- formData.append('model', model);
- formData.append('file', file);
+ const formData = gatherModelFileUpdateData(modelUpdateError);
+ if (!formData) return;
try {
- // Send a POST request to the API to update the model file
- const res = await fetch(`${API_URL}/model_file_update`, {
- method: 'POST',
- headers: authHeaders(), // Include the authentication headers
- body: formData // Send the FormData object as the request body
- });
-
- if (!res.ok) {
- const data = await res.json();
- modelUpdateError.textContent = data.detail || 'Failed to update model file.'; // Show an error message
- return;
- }
-
- // Show a success message upon successful model file update
- alert('Model file updated successfully.');
+ await sendModelFileUpdateRequest(formData, modelUpdateError);
} catch (err) {
- // Handle any unexpected errors
modelUpdateError.textContent = 'Error updating model file.';
}
});
+}
+
+/** Gather data for model file update */
+function gatherModelFileUpdateData(errorElement) {
+ const model = document.getElementById('model-name').value.trim();
+ const fileInput = document.getElementById('model-file');
+ const file = fileInput.files[0];
+
+ if (!file) {
+ errorElement.textContent = 'Please select a model file.';
+ return null;
+ }
+
+ const formData = new FormData();
+ formData.append('model', model);
+ formData.append('file', file);
+ return formData;
+}
+
+/** Send the model file update request */
+async function sendModelFileUpdateRequest(formData, errorElement) {
+ const response = await fetch(`${API_URL}/model_file_update`, {
+ method: 'POST',
+ headers: authHeaders(),
+ body: formData,
+ });
+
+ if (!response.ok) {
+ const data = await response.json();
+ errorElement.textContent = data.detail || 'Failed to update model file.';
+ return;
+ }
- // Handle the form for retrieving a new model file
+ alert('Model file updated successfully.');
+}
+
+/** Setup for retrieving a new model file */
+function setupGetNewModel() {
const getNewModelForm = document.getElementById('get-new-model-form');
const getNewModelError = document.getElementById('get-new-model-error');
const modelFileContent = document.getElementById('model-file-content');
+
+ if (!getNewModelForm) return;
+
getNewModelForm.addEventListener('submit', async (e) => {
- e.preventDefault(); // Prevent the default form submission behaviour
- getNewModelError.textContent = ''; // Clear any previous error messages
- modelFileContent.textContent = ''; // Clear any previous content
+ e.preventDefault();
+ clearGetNewModelMessages(getNewModelError, modelFileContent);
- // Retrieve the model name from the form
- const model = document.getElementById('get-model-name').value.trim();
- const lastUpdateTime = '1970-01-01'; // Automatically set the default last update time
+ const requestData = gatherGetNewModelData();
+ if (!requestData) return;
try {
- // Send a POST request to the API to retrieve a new model file
- const res = await fetch(`${API_URL}/get_new_model`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- ...authHeaders() // Include the authentication headers
- },
- body: JSON.stringify({ model, last_update_time: lastUpdateTime }) // Send the model name and last update time as JSON
- });
-
- if (!res.ok) {
- const data = await res.json();
- getNewModelError.textContent = data.detail || 'Failed to retrieve new model.'; // Show an error message
- return;
- }
-
- // Parse the response data
- const data = await res.json();
- if (data.model_file) {
- // Display the Base64-encoded model file content
- modelFileContent.textContent = `Base64 Model File: ${data.model_file}`;
- } else {
- // Display any returned message if no file is included
- modelFileContent.textContent = data.message;
- }
+ await sendGetNewModelRequest(requestData, getNewModelError, modelFileContent);
} catch (err) {
- // Handle any unexpected errors
getNewModelError.textContent = 'Error retrieving model file.';
}
});
-});
+}
+
+/** Clear messages for the "Get New Model" form */
+function clearGetNewModelMessages(errorElement, contentElement) {
+ errorElement.textContent = '';
+ contentElement.textContent = '';
+}
+
+/** Gather data for retrieving a new model file */
+function gatherGetNewModelData() {
+ const model = document.getElementById('get-model-name').value.trim();
+ const lastUpdateTime = '1970-01-01'; // Default last update time
+ return { model, last_update_time: lastUpdateTime };
+}
+
+/** Send the request to retrieve a new model file */
+async function sendGetNewModelRequest(requestData, errorElement, contentElement) {
+ const response = await fetch(`${API_URL}/get_new_model`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ ...authHeaders(),
+ },
+ body: JSON.stringify(requestData),
+ });
+
+ if (!response.ok) {
+ const data = await response.json();
+ errorElement.textContent = data.detail || 'Failed to retrieve new model.';
+ return;
+ }
+
+ const data = await response.json();
+ displayModelFileContent(data, contentElement);
+}
+
+/** Display the retrieved model file content */
+function displayModelFileContent(data, contentElement) {
+ if (data.model_file) {
+ contentElement.textContent = `Base64 Model File: ${data.model_file}`;
+ } else {
+ contentElement.textContent = data.message || 'No model file available.';
+ }
+}
diff --git a/examples/YOLO_server_api/frontend/public/js/user_management.js b/examples/YOLO_server_api/frontend/public/js/user_management.js
index 79079ce..f06f77e 100644
--- a/examples/YOLO_server_api/frontend/public/js/user_management.js
+++ b/examples/YOLO_server_api/frontend/public/js/user_management.js
@@ -1,12 +1,17 @@
import { checkAccess, authHeaders, clearToken } from './common.js';
-const API_URL = '/api'; // Base path for the backend API, ensure the server has the corresponding routes
-// Wait for the DOM content to be fully loaded
+const API_URL = '/api'; // Base path for the backend API
+
document.addEventListener('DOMContentLoaded', () => {
- // Restrict access to users with the 'admin' role
- checkAccess(['admin']);
+ checkAccess(['admin']); // Restrict access to admin users
+ setupLogoutButton();
+ setupFormHandlers();
+});
- // Bind the Logout button functionality
+/**
+ * Bind the Logout button functionality.
+ */
+function setupLogoutButton() {
const logoutBtn = document.getElementById('logout-btn');
if (logoutBtn) {
logoutBtn.addEventListener('click', () => {
@@ -14,174 +19,105 @@ document.addEventListener('DOMContentLoaded', () => {
window.location.href = '/login.html'; // Redirect the user to the login page
});
}
-
- // Handle the Add User form submission
- const addUserForm = document.getElementById('add-user-form');
- const addUserError = document.getElementById('add-user-error');
- addUserForm.addEventListener('submit', async (e) => {
- e.preventDefault(); // Prevent the default form submission behaviour
- addUserError.textContent = ''; // Clear any previous error messages
-
- // Retrieve the input values from the form
- const username = document.getElementById('add-username').value.trim();
- const password = document.getElementById('add-password').value.trim();
- const role = document.getElementById('add-role').value;
-
- try {
- // Send a POST request to add a new user
- const res = await fetch(`${API_URL}/add_user`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- ...authHeaders() // Include the authentication headers
- },
- body: JSON.stringify({ username, password, role }) // Send the user data as JSON
- });
-
- if (!res.ok) {
- const data = await res.json();
- addUserError.textContent = data.detail || 'Failed to add user.'; // Show an error message
- return;
- }
-
- alert('User added successfully.'); // Show a success message
- } catch (err) {
- addUserError.textContent = 'Error adding user.'; // Show a generic error message
- }
- });
-
- // Handle the Delete User form submission
- const deleteUserForm = document.getElementById('delete-user-form');
- const deleteUserError = document.getElementById('delete-user-error');
- deleteUserForm.addEventListener('submit', async (e) => {
- e.preventDefault();
- deleteUserError.textContent = ''; // Clear any previous error messages
-
- // Retrieve the username to delete
- const username = document.getElementById('delete-username').value.trim();
-
- try {
- // Send a POST request to delete the user
- const res = await fetch(`${API_URL}/delete_user`, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- ...authHeaders() // Include the authentication headers
- },
- body: JSON.stringify({ username }) // Send the username as JSON
- });
-
- if (!res.ok) {
- const data = await res.json();
- deleteUserError.textContent = data.detail || 'Failed to delete user.'; // Show an error message
- return;
- }
-
- alert('User deleted successfully.'); // Show a success message
- } catch (err) {
- deleteUserError.textContent = 'Error deleting user.'; // Show a generic error message
- }
- });
-
- // Handle the Update Username form submission
- const updateUsernameForm = document.getElementById('update-username-form');
- const updateUsernameError = document.getElementById('update-username-error');
- updateUsernameForm.addEventListener('submit', async (e) => {
- e.preventDefault();
- updateUsernameError.textContent = ''; // Clear any previous error messages
-
- // Retrieve the old and new usernames
- const old_username = document.getElementById('old-username').value.trim();
- const new_username = document.getElementById('new-username').value.trim();
-
- try {
- // Send a PUT request to update the username
- const res = await fetch(`${API_URL}/update_username`, {
- method: 'PUT',
- headers: {
- 'Content-Type': 'application/json',
- ...authHeaders() // Include the authentication headers
- },
- body: JSON.stringify({ old_username, new_username }) // Send the old and new usernames as JSON
- });
-
- if (!res.ok) {
- const data = await res.json();
- updateUsernameError.textContent = data.detail || 'Failed to update username.'; // Show an error message
- return;
- }
-
- alert('Username updated successfully.'); // Show a success message
- } catch (err) {
- updateUsernameError.textContent = 'Error updating username.'; // Show a generic error message
- }
- });
-
- // Handle the Update Password form submission
- const updatePasswordForm = document.getElementById('update-password-form');
- const updatePasswordError = document.getElementById('update-password-error');
- updatePasswordForm.addEventListener('submit', async (e) => {
- e.preventDefault();
- updatePasswordError.textContent = ''; // Clear any previous error messages
-
- // Retrieve the username and new password
- const username = document.getElementById('update-password-username').value.trim();
- const new_password = document.getElementById('new-password').value.trim();
-
- try {
- // Send a PUT request to update the user's password
- const res = await fetch(`${API_URL}/update_password`, {
- method: 'PUT',
- headers: {
- 'Content-Type': 'application/json',
- ...authHeaders() // Include the authentication headers
- },
- body: JSON.stringify({ username, new_password }) // Send the username and new password as JSON
- });
-
- if (!res.ok) {
- const data = await res.json();
- updatePasswordError.textContent = data.detail || 'Failed to update password.'; // Show an error message
- return;
+}
+
+/**
+ * Set up all form submission handlers.
+ */
+function setupFormHandlers() {
+ setupFormHandler('add-user-form', handleAddUser, 'add-user-error');
+ setupFormHandler('delete-user-form', handleDeleteUser, 'delete-user-error');
+ setupFormHandler('update-username-form', handleUpdateUsername, 'update-username-error');
+ setupFormHandler('update-password-form', handleUpdatePassword, 'update-password-error');
+ setupFormHandler('set-active-status-form', handleSetActiveStatus, 'set-active-status-error');
+}
+
+/**
+ * Generic function to set up form submission handlers.
+ */
+function setupFormHandler(formId, handlerFunction, errorElementId) {
+ const form = document.getElementById(formId);
+ const errorElement = document.getElementById(errorElementId);
+
+ if (form) {
+ form.addEventListener('submit', async (e) => {
+ e.preventDefault();
+ errorElement.textContent = ''; // Clear any previous error messages
+ try {
+ await handlerFunction();
+ } catch (err) {
+ errorElement.textContent = 'An error occurred while processing the form.';
}
-
- alert('Password updated successfully.'); // Show a success message
- } catch (err) {
- updatePasswordError.textContent = 'Error updating password.'; // Show a generic error message
- }
+ });
+ }
+}
+
+/**
+ * Handle the Add User form submission.
+ */
+async function handleAddUser() {
+ const username = document.getElementById('add-username').value.trim();
+ const password = document.getElementById('add-password').value.trim();
+ const role = document.getElementById('add-role').value;
+
+ await sendRequest(`${API_URL}/add_user`, 'POST', { username, password, role });
+ alert('User added successfully.');
+}
+
+/**
+ * Handle the Delete User form submission.
+ */
+async function handleDeleteUser() {
+ const username = document.getElementById('delete-username').value.trim();
+ await sendRequest(`${API_URL}/delete_user`, 'POST', { username });
+ alert('User deleted successfully.');
+}
+
+/**
+ * Handle the Update Username form submission.
+ */
+async function handleUpdateUsername() {
+ const old_username = document.getElementById('old-username').value.trim();
+ const new_username = document.getElementById('new-username').value.trim();
+ await sendRequest(`${API_URL}/update_username`, 'PUT', { old_username, new_username });
+ alert('Username updated successfully.');
+}
+
+/**
+ * Handle the Update Password form submission.
+ */
+async function handleUpdatePassword() {
+ const username = document.getElementById('update-password-username').value.trim();
+ const new_password = document.getElementById('new-password').value.trim();
+ await sendRequest(`${API_URL}/update_password`, 'PUT', { username, new_password });
+ alert('Password updated successfully.');
+}
+
+/**
+ * Handle the Set Active Status form submission.
+ */
+async function handleSetActiveStatus() {
+ const username = document.getElementById('active-status-username').value.trim();
+ const is_active = document.getElementById('is-active').checked;
+ await sendRequest(`${API_URL}/set_user_active_status`, 'PUT', { username, is_active });
+ alert('User active status updated successfully.');
+}
+
+/**
+ * Helper function to send API requests.
+ */
+async function sendRequest(url, method, body) {
+ const res = await fetch(url, {
+ method,
+ headers: {
+ 'Content-Type': 'application/json',
+ ...authHeaders(),
+ },
+ body: JSON.stringify(body),
});
- // Handle the Set Active Status form submission
- const setActiveStatusForm = document.getElementById('set-active-status-form');
- const setActiveStatusError = document.getElementById('set-active-status-error');
- setActiveStatusForm.addEventListener('submit', async (e) => {
- e.preventDefault();
- setActiveStatusError.textContent = ''; // Clear any previous error messages
-
- // Retrieve the username and active status
- const username = document.getElementById('active-status-username').value.trim();
- const is_active = document.getElementById('is-active').checked;
-
- try {
- // Send a PUT request to update the user's active status
- const res = await fetch(`${API_URL}/set_user_active_status`, {
- method: 'PUT',
- headers: {
- 'Content-Type': 'application/json',
- ...authHeaders() // Include the authentication headers
- },
- body: JSON.stringify({ username, is_active }) // Send the username and active status as JSON
- });
-
- if (!res.ok) {
- const data = await res.json();
- setActiveStatusError.textContent = data.detail || 'Failed to update active status.'; // Show an error message
- return;
- }
-
- alert('User active status updated successfully.'); // Show a success message
- } catch (err) {
- setActiveStatusError.textContent = 'Error updating active status.'; // Show a generic error message
- }
- });
-});
+ if (!res.ok) {
+ const data = await res.json();
+ throw new Error(data.detail || 'Request failed.');
+ }
+}