diff --git a/AccessQueuePlayground/Components/Layout/MainLayout.razor b/AccessQueuePlayground/Components/Layout/MainLayout.razor
index e21aaad..ee658cd 100644
--- a/AccessQueuePlayground/Components/Layout/MainLayout.razor
+++ b/AccessQueuePlayground/Components/Layout/MainLayout.razor
@@ -14,6 +14,9 @@
About
+
+ Config
+
Source
diff --git a/AccessQueuePlayground/Components/Pages/Config.razor b/AccessQueuePlayground/Components/Pages/Config.razor
new file mode 100644
index 0000000..19bd521
--- /dev/null
+++ b/AccessQueuePlayground/Components/Pages/Config.razor
@@ -0,0 +1,104 @@
+@page "/config"
+@inject AccessQueuePlayground.Services.IAccessQueueManager QueueManager
+@using BlazorBootstrap
+
+Access Queue Configuration
+
+
+
+
+
+
+
+ @if (!isCapacityLimitValid)
+ {
+
Please enter a positive integer.
+ }
+
+
+
+
+ @if (!isActivitySecondsValid)
+ {
+
Please enter a positive integer.
+ }
+
+
+
+
+ @if (!isExpirationSecondsValid)
+ {
+
Please enter a positive integer.
+ }
+
+
+
+
+
+ @if (successMessage != null)
+ {
+ @successMessage
+ }
+
+
+@code {
+ private AccessQueueService.Models.AccessQueueConfig config = new();
+ private string? capacityLimitInput;
+ private string? activitySecondsInput;
+ private string? expirationSecondsInput;
+ private bool rollingExpirationSwitch;
+ private bool isCapacityLimitValid = true;
+ private bool isActivitySecondsValid = true;
+ private bool isExpirationSecondsValid = true;
+ private string? successMessage;
+
+ protected override void OnInitialized()
+ {
+ var current = QueueManager.GetConfig();
+ config = current.Clone();
+ capacityLimitInput = config.CapacityLimit?.ToString();
+ activitySecondsInput = config.ActivitySeconds?.ToString();
+ expirationSecondsInput = config.ExpirationSeconds?.ToString();
+ rollingExpirationSwitch = config.RollingExpiration ?? true;
+ ValidateInputs();
+ }
+
+ private bool IsFormValid => isCapacityLimitValid && isActivitySecondsValid && isExpirationSecondsValid;
+
+ private void ValidateInputs()
+ {
+ isCapacityLimitValid = int.TryParse(capacityLimitInput, out var cap) && cap > 0;
+ isActivitySecondsValid = int.TryParse(activitySecondsInput, out var act) && act > 0;
+ isExpirationSecondsValid = int.TryParse(expirationSecondsInput, out var exp) && exp > 0;
+ }
+
+ private async Task HandleValidSubmit()
+ {
+ ValidateInputs();
+ if (!IsFormValid)
+ return;
+ config.CapacityLimit = int.Parse(capacityLimitInput!);
+ config.ActivitySeconds = int.Parse(activitySecondsInput!);
+ config.ExpirationSeconds = int.Parse(expirationSecondsInput!);
+ config.RollingExpiration = rollingExpirationSwitch;
+ await Task.Run(() => QueueManager.UpdateConfig(config));
+ successMessage = "Configuration updated successfully.";
+ }
+
+ private void OnInputChanged(ChangeEventArgs e, string field)
+ {
+ switch (field)
+ {
+ case nameof(config.CapacityLimit):
+ capacityLimitInput = e.Value?.ToString();
+ break;
+ case nameof(config.ActivitySeconds):
+ activitySecondsInput = e.Value?.ToString();
+ break;
+ case nameof(config.ExpirationSeconds):
+ expirationSecondsInput = e.Value?.ToString();
+ break;
+ }
+ ValidateInputs();
+ }
+}