11<!DOCTYPE html>
22< html lang ="en ">
3+
34< head >
45 < meta charset ="UTF-8 ">
56 < meta name ="viewport " content ="width=device-width, initial-scale=1.0 ">
165166 color : var (--text-muted );
166167 font-size : 0.8rem ;
167168 }
168-
169+
169170 # selected-file-name {
170171 display : block;
171172 margin-top : 1rem ;
206207 color : var (--success-color );
207208 border : 1px solid rgba (16 , 185 , 129 , 0.2 );
208209 }
209-
210+
210211 .progress-bar {
211212 width : 100% ;
212213 height : 6px ;
216217 overflow : hidden;
217218 display : none;
218219 }
219-
220+
220221 .progress-fill {
221222 height : 100% ;
222223 background : var (--primary-color );
228229 display : inline-block;
229230 width : 1.25rem ;
230231 height : 1.25rem ;
231- border : 2px solid rgba (255 , 255 , 255 , 0.3 );
232+ border : 2px solid rgba (255 , 255 , 255 , 0.3 );
232233 border-radius : 50% ;
233234 border-top-color : white;
234235 animation : spin 1s ease-in-out infinite;
238239 transform : translate (-50% , -50% );
239240 display : none;
240241 }
241-
242+
242243 .btn .loading {
243244 color : transparent;
244245 }
245-
246+
246247 .btn .loading .loader {
247248 display : block;
248249 }
264265 }
265266
266267 @keyframes spin {
267- to { transform : translate (-50% , -50% ) rotate (360deg ); }
268+ to {
269+ transform : translate (-50% , -50% ) rotate (360deg );
270+ }
268271 }
269272 </ style >
270273 <!-- Importing Inter Font -->
271274 < link href ="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap " rel ="stylesheet ">
272275</ head >
276+
273277< body >
274278
275279 <!-- Authentication View -->
278282 < h1 id ="auth-title "> Welcome Back</ h1 >
279283 < p id ="auth-subtitle "> Sign in to upload files</ p >
280284 </ div >
281-
285+
282286 < form id ="auth-form ">
283287 < div class ="form-group ">
284288 < label for ="email "> Email Address</ label >
@@ -304,11 +308,12 @@ <h1 id="auth-title">Welcome Back</h1>
304308 < h1 > Upload File</ h1 >
305309 < p > Secure direct upload to S3</ p >
306310 </ div >
307-
311+
308312 < div class ="file-upload-wrapper " id ="drop-zone ">
309313 < input type ="file " id ="file-input " required >
310314 < div class ="file-upload-icon ">
311- < svg width ="48 " height ="48 " viewBox ="0 0 24 24 " fill ="none " stroke ="currentColor " stroke-width ="1.5 " stroke-linecap ="round " stroke-linejoin ="round ">
315+ < svg width ="48 " height ="48 " viewBox ="0 0 24 24 " fill ="none " stroke ="currentColor " stroke-width ="1.5 "
316+ stroke-linecap ="round " stroke-linejoin ="round ">
312317 < path d ="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4 "> </ path >
313318 < polyline points ="17 8 12 3 7 8 "> </ polyline >
314319 < line x1 ="12 " y1 ="3 " x2 ="12 " y2 ="15 "> </ line >
@@ -323,17 +328,21 @@ <h1>Upload File</h1>
323328 < span > Start Upload</ span >
324329 < div class ="loader "> </ div >
325330 </ button >
326-
331+
327332 < div class ="progress-bar " id ="progress-container ">
328333 < div class ="progress-fill " id ="progress-bar "> </ div >
329334 </ div >
330335
331336 < div id ="upload-status " class ="status-message "> </ div >
337+
338+ < button type ="button " class ="btn " id ="logout-btn " style ="background: var(--surface-color); color: var(--text-muted); border: 1px solid var(--border-color); margin-top: 1rem; ">
339+ Logout
340+ </ button >
332341 </ div >
333342
334343 < script >
335344 const API_BASE = 'http://localhost:8080/api' ;
336-
345+
337346 // DOM Elements
338347 const authView = document . getElementById ( 'auth-view' ) ;
339348 const uploadView = document . getElementById ( 'upload-view' ) ;
@@ -346,9 +355,9 @@ <h1>Upload File</h1>
346355 const toggleAuthBtn = document . getElementById ( 'toggle-auth' ) ;
347356 const authTitle = document . getElementById ( 'auth-title' ) ;
348357 const authSubtitle = document . getElementById ( 'auth-subtitle' ) ;
349-
358+
350359 let isLoginMode = true ;
351-
360+
352361 const fileInput = document . getElementById ( 'file-input' ) ;
353362 const dropZone = document . getElementById ( 'drop-zone' ) ;
354363 const fileNameDisplay = document . getElementById ( 'selected-file-name' ) ;
@@ -379,7 +388,7 @@ <h1>Upload File</h1>
379388 uploadView . classList . remove ( 'hidden' ) ;
380389 }
381390 } ;
382-
391+
383392 // Check initially
384393 checkAuth ( ) ;
385394
@@ -426,9 +435,9 @@ <h1>Upload File</h1>
426435 // Both login and register return { user, token }
427436 document . cookie = `token=${ data . token } ; path=/; max-age=3600` ;
428437 localStorage . setItem ( 'token' , data . token ) ;
429-
438+
430439 showStatus ( authStatus , isLoginMode ? 'Login successful!' : 'Registration successful!' , false ) ;
431-
440+
432441 setTimeout ( ( ) => {
433442 authView . classList . add ( 'hidden' ) ;
434443 uploadView . classList . remove ( 'hidden' ) ;
@@ -496,7 +505,7 @@ <h1>Upload File</h1>
496505 // 1. Get Presigned URL
497506 const urlResponse = await fetch ( `${ API_BASE } /s3/upload` , {
498507 method : 'POST' ,
499- headers : {
508+ headers : {
500509 'Content-Type' : 'application/json' ,
501510 'Authorization' : `Bearer ${ localStorage . getItem ( 'token' ) || '' } `
502511 } ,
@@ -526,25 +535,25 @@ <h1>Upload File</h1>
526535 await new Promise ( ( resolve , reject ) => {
527536 progressContainer . style . display = 'block' ;
528537 const xhr = new XMLHttpRequest ( ) ;
529-
538+
530539 xhr . upload . addEventListener ( "progress" , ( event ) => {
531540 if ( event . lengthComputable ) {
532541 const percentComplete = ( event . loaded / event . total ) * 100 ;
533542 progressBar . style . width = percentComplete + '%' ;
534543 }
535544 } ) ;
536-
545+
537546 xhr . addEventListener ( "load" , ( ) => {
538547 if ( xhr . status >= 200 && xhr . status < 300 ) {
539548 resolve ( xhr . status ) ;
540549 } else {
541550 reject ( new Error ( `S3 responded with status ${ xhr . status } ` ) ) ;
542551 }
543552 } ) ;
544-
553+
545554 xhr . addEventListener ( "error" , ( ) => reject ( new Error ( "Network error during upload" ) ) ) ;
546555 xhr . addEventListener ( "abort" , ( ) => reject ( new Error ( "Upload aborted" ) ) ) ;
547-
556+
548557 xhr . open ( "PUT" , url , true ) ;
549558 xhr . setRequestHeader ( "Content-Type" , selectedFile . type || 'application/octet-stream' ) ;
550559 xhr . send ( selectedFile ) ;
@@ -554,24 +563,24 @@ <h1>Upload File</h1>
554563 try {
555564 await fetch ( `${ API_BASE } /s3/process` , {
556565 method : 'POST' ,
557- headers : {
566+ headers : {
558567 'Content-Type' : 'application/json' ,
559568 'Authorization' : `Bearer ${ localStorage . getItem ( 'token' ) || '' } `
560569 } ,
561570 credentials : 'include' ,
562571 body : JSON . stringify ( { fileId } )
563572 } ) ;
564- } catch ( e ) {
573+ } catch ( e ) {
565574 console . error ( "Failed to notify backend for processing" , e ) ;
566575 }
567576
568577 showStatus ( uploadStatus , `Upload successful & processing started! File ID: ${ fileId } ` , false ) ;
569-
578+
570579 // Reset file selection
571580 selectedFile = null ;
572581 fileInput . value = '' ;
573582 fileNameDisplay . textContent = '' ;
574-
583+
575584 } catch ( error ) {
576585 if ( error . message !== 'Unauthorized' ) {
577586 showStatus ( uploadStatus , error . message , true ) ;
@@ -583,6 +592,23 @@ <h1>Upload File</h1>
583592 fileInput . disabled = false ;
584593 }
585594 } ) ;
595+
596+ // Handle Logout
597+ const logoutBtn = document . getElementById ( 'logout-btn' ) ;
598+ if ( logoutBtn ) {
599+ logoutBtn . addEventListener ( 'click' , ( ) => {
600+ document . cookie = 'token=; Max-Age=0; path=/;' ;
601+ localStorage . removeItem ( 'token' ) ;
602+ uploadView . classList . add ( 'hidden' ) ;
603+ authView . classList . remove ( 'hidden' ) ;
604+ // Clear inputs
605+ emailInput . value = '' ;
606+ passwordInput . value = '' ;
607+ hideStatus ( authStatus ) ;
608+ hideStatus ( uploadStatus ) ;
609+ } ) ;
610+ }
586611 </ script >
587612</ body >
588- </ html >
613+
614+ </ html >
0 commit comments