@@ -44,7 +44,7 @@ <h1 class="text-3xl font-bold text-gray-800 dark:text-gray-200">Pulse Setup</h1>
44
44
</ div >
45
45
46
46
<!-- Configuration Form -->
47
- < form id ="config-form " class ="space-y-6 " autocomplete ="off ">
47
+ < form id ="config-form " class ="space-y-6 " autocomplete ="off " onsubmit =" return false; " >
48
48
<!-- Primary Proxmox VE Configuration -->
49
49
< div class ="border border-gray-200 dark:border-gray-700 rounded-lg p-6 ">
50
50
< h2 class ="text-xl font-semibold mb-4 text-gray-800 dark:text-gray-200 "> Primary Proxmox VE Server</ h2 >
@@ -274,9 +274,13 @@ <h3 class="text-sm font-medium text-red-800 dark:text-red-200">Configuration Err
274
274
< div id ="success-message " class ="hidden bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg p-4 ">
275
275
< div class ="flex ">
276
276
< div class ="flex-shrink-0 ">
277
- < svg class ="h-5 w-5 text-green-400 " viewBox ="0 0 20 20 " fill ="currentColor ">
277
+ < svg id =" success-icon " class ="h-5 w-5 text-green-400 " viewBox ="0 0 20 20 " fill ="currentColor ">
278
278
< path fill-rule ="evenodd " d ="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z " clip-rule ="evenodd " />
279
279
</ svg >
280
+ < svg id ="loading-icon " class ="hidden animate-spin h-5 w-5 text-green-400 " xmlns ="http://www.w3.org/2000/svg " fill ="none " viewBox ="0 0 24 24 ">
281
+ < circle class ="opacity-25 " cx ="12 " cy ="12 " r ="10 " stroke ="currentColor " stroke-width ="4 "> </ circle >
282
+ < path class ="opacity-75 " fill ="currentColor " d ="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z "> </ path >
283
+ </ svg >
280
284
</ div >
281
285
< div class ="ml-3 ">
282
286
< h3 class ="text-sm font-medium text-green-800 dark:text-green-200 "> Success!</ h3 >
@@ -291,7 +295,7 @@ <h3 class="text-sm font-medium text-green-800 dark:text-green-200">Success!</h3>
291
295
class ="flex-1 bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 ">
292
296
Save Configuration
293
297
</ button >
294
- < button type ="button " onclick ="testConnection() "
298
+ < button type ="button " onclick ="testConnection(event ) "
295
299
class ="flex-1 bg-gray-600 hover:bg-gray-700 text-white font-medium py-2 px-4 rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 ">
296
300
Test Connection
297
301
</ button >
@@ -335,12 +339,27 @@ <h3 class="text-sm font-medium text-green-800 dark:text-green-200">Success!</h3>
335
339
successDiv . classList . add ( 'hidden' ) ;
336
340
}
337
341
338
- function showSuccess ( message = 'Configuration saved. Redirecting to dashboard...' ) {
342
+ function showSuccess ( message = 'Configuration saved. Redirecting to dashboard...' , showButton = false , showLoading = false ) {
339
343
const errorDiv = document . getElementById ( 'error-message' ) ;
340
344
const successDiv = document . getElementById ( 'success-message' ) ;
341
345
const successText = document . getElementById ( 'success-text' ) ;
346
+ const successIcon = document . getElementById ( 'success-icon' ) ;
347
+ const loadingIcon = document . getElementById ( 'loading-icon' ) ;
348
+
349
+ // Toggle icons
350
+ if ( showLoading ) {
351
+ successIcon . classList . add ( 'hidden' ) ;
352
+ loadingIcon . classList . remove ( 'hidden' ) ;
353
+ } else {
354
+ successIcon . classList . remove ( 'hidden' ) ;
355
+ loadingIcon . classList . add ( 'hidden' ) ;
356
+ }
342
357
343
- successText . textContent = message ;
358
+ if ( showButton ) {
359
+ successText . innerHTML = message + '<br><button onclick="window.location.href=\'/\'" class="mt-3 bg-green-600 hover:bg-green-700 text-white font-medium py-2 px-4 rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800">Continue to Pulse</button>' ;
360
+ } else {
361
+ successText . textContent = message ;
362
+ }
344
363
errorDiv . classList . add ( 'hidden' ) ;
345
364
successDiv . classList . remove ( 'hidden' ) ;
346
365
}
@@ -350,7 +369,8 @@ <h3 class="text-sm font-medium text-green-800 dark:text-green-200">Success!</h3>
350
369
document . getElementById ( 'success-message' ) . classList . add ( 'hidden' ) ;
351
370
}
352
371
353
- async function testConnection ( ) {
372
+ async function testConnection ( event ) {
373
+ if ( event ) event . preventDefault ( ) ;
354
374
const formData = new FormData ( document . getElementById ( 'config-form' ) ) ;
355
375
const config = {
356
376
proxmox : {
@@ -367,7 +387,7 @@ <h3 class="text-sm font-medium text-green-800 dark:text-green-200">Success!</h3>
367
387
}
368
388
369
389
hideMessages ( ) ;
370
- const button = event . target ;
390
+ const button = event && event . target ? event . target : document . querySelector ( 'button[onclick*="testConnection"]' ) ;
371
391
button . disabled = true ;
372
392
button . textContent = 'Testing...' ;
373
393
@@ -381,7 +401,7 @@ <h3 class="text-sm font-medium text-green-800 dark:text-green-200">Success!</h3>
381
401
const result = await response . json ( ) ;
382
402
383
403
if ( response . ok && result . success ) {
384
- showSuccess ( 'Connection test successful!' ) ;
404
+ showSuccess ( 'Connection test successful!' , false , false ) ;
385
405
setTimeout ( ( ) => hideMessages ( ) , 3000 ) ;
386
406
} else {
387
407
showError ( result . error || 'Connection test failed' ) ;
@@ -394,8 +414,25 @@ <h3 class="text-sm font-medium text-green-800 dark:text-green-200">Success!</h3>
394
414
}
395
415
}
396
416
397
- document . getElementById ( 'config-form' ) . addEventListener ( 'submit' , async ( e ) => {
417
+ // Ensure DOM is loaded before attaching event handlers
418
+ if ( document . readyState === 'loading' ) {
419
+ document . addEventListener ( 'DOMContentLoaded' , setupFormHandlers ) ;
420
+ } else {
421
+ setupFormHandlers ( ) ;
422
+ }
423
+
424
+ function setupFormHandlers ( ) {
425
+ console . log ( 'Setting up form handlers...' ) ;
426
+
427
+ const form = document . getElementById ( 'config-form' ) ;
428
+ if ( ! form ) {
429
+ console . error ( 'Config form not found!' ) ;
430
+ return ;
431
+ }
432
+
433
+ form . addEventListener ( 'submit' , async ( e ) => {
398
434
e . preventDefault ( ) ;
435
+ console . log ( 'Form submit event triggered' ) ;
399
436
400
437
const formData = new FormData ( e . target ) ;
401
438
const config = {
@@ -406,6 +443,12 @@ <h3 class="text-sm font-medium text-green-800 dark:text-green-200">Success!</h3>
406
443
tokenSecret : formData . get ( 'proxmox-token-secret' )
407
444
}
408
445
} ;
446
+
447
+ // Validate required fields
448
+ if ( ! config . proxmox . host || ! config . proxmox . tokenId || ! config . proxmox . tokenSecret ) {
449
+ showError ( 'Please fill in all required Proxmox fields' ) ;
450
+ return ;
451
+ }
409
452
410
453
// Add PBS config if provided
411
454
if ( formData . get ( 'pbs-host' ) ) {
@@ -448,6 +491,7 @@ <h3 class="text-sm font-medium text-green-800 dark:text-green-200">Success!</h3>
448
491
449
492
// Debug: Log what we're sending
450
493
console . log ( 'Saving configuration:' , JSON . stringify ( config , null , 2 ) ) ;
494
+ console . log ( 'Making POST request to /api/config...' ) ;
451
495
452
496
try {
453
497
const response = await fetch ( '/api/config' , {
@@ -456,24 +500,86 @@ <h3 class="text-sm font-medium text-green-800 dark:text-green-200">Success!</h3>
456
500
body : JSON . stringify ( config )
457
501
} ) ;
458
502
503
+ console . log ( 'Save response status:' , response . status , 'ok:' , response . ok ) ;
459
504
const result = await response . json ( ) ;
505
+ console . log ( 'Save result:' , result ) ;
460
506
461
507
if ( response . ok && result . success ) {
462
- showSuccess ( ) ;
508
+ showSuccess ( 'Configuration saved! Applying settings...' , false , true ) ;
509
+
510
+ // Wait a moment for the server to start reloading
463
511
setTimeout ( ( ) => {
464
- window . location . href = '/' ;
512
+ checkServerReady ( ) ;
465
513
} , 2000 ) ;
466
514
} else {
467
515
showError ( result . error || 'Failed to save configuration' ) ;
468
516
button . disabled = false ;
469
517
button . textContent = 'Save Configuration' ;
470
518
}
471
519
} catch ( error ) {
520
+ console . error ( 'Save configuration error:' , error ) ;
472
521
showError ( 'Failed to save configuration: ' + error . message ) ;
473
522
button . disabled = false ;
474
523
button . textContent = 'Save Configuration' ;
475
524
}
476
- } ) ;
525
+ } ) ;
526
+
527
+ // Load config on page load
528
+ loadExistingConfig ( ) ;
529
+ }
530
+
531
+ // Check if server is ready after configuration save
532
+ async function checkServerReady ( attempts = 0 ) {
533
+ const maxAttempts = 15 ; // 30 seconds total (15 * 2 seconds)
534
+
535
+ try {
536
+ const response = await fetch ( '/api/health' ) ;
537
+
538
+ if ( response . ok ) {
539
+ const health = await response . json ( ) ;
540
+ console . log ( 'Health check response:' , health ) ;
541
+
542
+ // Check if the server has successfully loaded configuration
543
+ // Look for signs that API clients are initialized and not in placeholder mode
544
+ if ( health . system && health . system . configPlaceholder === false ) {
545
+ showSuccess ( 'Configuration applied successfully!' , true , false ) ;
546
+
547
+ // Re-enable the save button
548
+ const button = document . getElementById ( 'save-button' ) ;
549
+ button . disabled = false ;
550
+ button . textContent = 'Save Configuration' ;
551
+ return ;
552
+ }
553
+ }
554
+ } catch ( error ) {
555
+ console . log ( 'Health check failed:' , error ) ;
556
+ }
557
+
558
+ // If we haven't exceeded max attempts, try again
559
+ if ( attempts < maxAttempts ) {
560
+ const messages = [
561
+ 'Applying configuration...' ,
562
+ 'Initializing connections...' ,
563
+ 'Connecting to Proxmox servers...' ,
564
+ 'Verifying credentials...' ,
565
+ 'Loading server data...'
566
+ ] ;
567
+ const messageIndex = Math . min ( Math . floor ( attempts / 3 ) , messages . length - 1 ) ;
568
+ showSuccess ( `${ messages [ messageIndex ] } (${ Math . floor ( ( attempts / maxAttempts ) * 100 ) } %)` , false , true ) ;
569
+
570
+ setTimeout ( ( ) => {
571
+ checkServerReady ( attempts + 1 ) ;
572
+ } , 2000 ) ;
573
+ } else {
574
+ // After 30 seconds, show the button anyway
575
+ showSuccess ( 'Configuration saved! The server is taking longer than expected to initialize.' , true , false ) ;
576
+
577
+ // Re-enable the save button
578
+ const button = document . getElementById ( 'save-button' ) ;
579
+ button . disabled = false ;
580
+ button . textContent = 'Save Configuration' ;
581
+ }
582
+ }
477
583
478
584
// Load existing configuration if available
479
585
async function loadExistingConfig ( ) {
@@ -534,9 +640,6 @@ <h3 class="text-sm font-medium text-green-800 dark:text-green-200">Success!</h3>
534
640
console . error ( 'Failed to load existing configuration:' , error ) ;
535
641
}
536
642
}
537
-
538
- // Load config on page load
539
- loadExistingConfig ( ) ;
540
643
</ script >
541
644
</ body >
542
645
</ html >
0 commit comments