Compare commits

...

2 Commits

Author SHA1 Message Date
henry a96a3c3a82 Added more controls to UI
Fixed issue with queue infinite looping if next user in queue was revoked
2025-05-14 02:03:27 -04:00
henry 261490ec74 UI improvements 2025-05-14 01:17:47 -04:00
6 changed files with 81 additions and 29 deletions

View File

@ -7,9 +7,11 @@
<PageTitle>AccessQueue Playground</PageTitle> <PageTitle>AccessQueue Playground</PageTitle>
<Button Color="ButtonColor.Success" @onclick="AddUser">Add User</Button> <p>
<Button Color="ButtonColor.Primary" @onclick="Refresh">Refresh</Button> <Button Color="ButtonColor.Success" @onclick="AddUser">Add User</Button>
<Button Color="ButtonColor.Danger" @onclick="RevokeAllAccess">Revoke All</Button>
<Button Color="ButtonColor.Warning" @onclick="Reset">Reset Data</Button>
</p>
@if (Status != null) @if (Status != null)
{ {
<h2>Users with access</h2> <h2>Users with access</h2>
@ -23,23 +25,33 @@
</GridColumn> </GridColumn>
<GridColumn TItem="User" HeaderText="Active"> <GridColumn TItem="User" HeaderText="Active">
<ChildContent> <ChildContent>
<Switch Value="context.Active" ValueExpression="() => context.Active" ValueChanged="(_) => ToggleUserActive(context.Id)" /> <Switch Value="context.Active" ValueExpression="() => context.Active" ValueChanged="(value) => SetUserActive(context.Id, value)" />
</ChildContent>
</GridColumn>
<GridColumn TItem="User" HeaderText="Revoke">
<ChildContent>
<Button Color="ButtonColor.Danger" @onclick="() => RevokeAccess(context.Id)">Revoke Access</Button>
</ChildContent> </ChildContent>
</GridColumn> </GridColumn>
</GridColumns> </GridColumns>
</Grid> </Grid>
<h2>Users in queue</h2> <h2>Users in queue</h2>
<Grid TItem="User" Data="Status.QueuedUsers" Class="table table-bordered mt-3" AllowSorting> <Grid TItem="User" Data="Status.QueuedUsers" Class="table table-bordered mt-3">
<GridColumns> <GridColumns>
<GridColumn TItem="User" HeaderText="Id" PropertyName="Id" SortKeySelector="item => item.Id"> <GridColumn TItem="User" HeaderText="Id" PropertyName="Id">
@context.Id @context.Id
</GridColumn> </GridColumn>
<GridColumn TItem="User" HeaderText="Queue Postition" PropertyName="LatestResponse?.RequestsAhead" SortKeySelector="item => item.LatestResponse.RequestsAhead"> <GridColumn TItem="User" HeaderText="Queue Postition" PropertyName="LatestResponse?.RequestsAhead">
@(context.LatestResponse?.RequestsAhead ?? 0 + 1) @(context.LatestResponse?.RequestsAhead ?? 0 + 1)
</GridColumn> </GridColumn>
<GridColumn TItem="User" HeaderText="Active"> <GridColumn TItem="User" HeaderText="Active">
<ChildContent> <ChildContent>
<Switch Value="context.Active" ValueExpression="() => context.Active" ValueChanged="(_) => ToggleUserActive(context.Id)" /> <Switch Value="context.Active" ValueExpression="() => context.Active" ValueChanged="(value) => SetUserActive(context.Id, value)" />
</ChildContent>
</GridColumn>
<GridColumn TItem="User" HeaderText="Revoke">
<ChildContent>
<Button Color="ButtonColor.Danger" @onclick="() => RevokeAccess(context.Id)">Revoke Access</Button>
</ChildContent> </ChildContent>
</GridColumn> </GridColumn>
</GridColumns> </GridColumns>
@ -52,7 +64,7 @@
</GridColumn> </GridColumn>
<GridColumn TItem="User" HeaderText="Active"> <GridColumn TItem="User" HeaderText="Active">
<ChildContent> <ChildContent>
<Switch Value="context.Active" ValueExpression="() => context.Active" ValueChanged="(_) => ToggleUserActive(context.Id)" /> <Switch Value="context.Active" ValueExpression="() => context.Active" ValueChanged="(value) => SetUserActive(context.Id, value)" />
</ChildContent> </ChildContent>
</GridColumn> </GridColumn>
</GridColumns> </GridColumns>
@ -77,19 +89,29 @@
}); });
} }
public void Refresh()
{
Status = Manager.GetStatus();
}
public void AddUser() public void AddUser()
{ {
Manager.AddUser(); Manager.AddUser();
Status = Manager.GetStatus(); Status = Manager.GetStatus();
} }
public void ToggleUserActive(Guid userId) public void SetUserActive(Guid userId, bool isActive)
{ {
Manager.ToggleUserActivity(userId); Manager.SetUserActive(userId, isActive);
}
public void RevokeAccess(Guid userId)
{
Manager.RevokeAccess(userId);
}
public void RevokeAllAccess()
{
Manager.RevokeAllAccess();
}
public void Reset()
{
Manager.Reset();
} }
} }

View File

@ -17,9 +17,17 @@ namespace AccessQueuePlayground.Services
{ {
while (!stoppingToken.IsCancellationRequested) while (!stoppingToken.IsCancellationRequested)
{ {
await _accessQueueManager.RecalculateStatus(); try
await Task.Delay(1000, stoppingToken); // Run every second {
await _accessQueueManager.RecalculateStatus();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
await Task.Delay(100, stoppingToken); // Run every second
} }
Console.WriteLine("Stopping now because who tf knows why");
} }
} }
} }

View File

@ -8,7 +8,7 @@ namespace AccessQueuePlayground.Services
public class AccessQueueManager : IAccessQueueManager public class AccessQueueManager : IAccessQueueManager
{ {
private readonly IAccessService _accessService; private readonly IAccessService _accessService;
private readonly ConcurrentDictionary<Guid, User> _users; private ConcurrentDictionary<Guid, User> _users;
private AccessQueueStatus _status; private AccessQueueStatus _status;
public event Action? StatusUpdated; public event Action? StatusUpdated;
@ -32,17 +32,16 @@ namespace AccessQueuePlayground.Services
_users[id] = new User _users[id] = new User
{ {
Id = id, Id = id,
Active = true, Active = false,
}; };
return id; return id;
} }
public void ToggleUserActivity(Guid userId) public void SetUserActive(Guid userId, bool isActive)
{ {
var user = _users[userId]; if (_users.TryGetValue(userId, out var user))
if (user != null)
{ {
user.Active = !user.Active; user.Active = isActive;
} }
} }
@ -76,8 +75,31 @@ namespace AccessQueuePlayground.Services
} }
} }
} }
newStatus.QueuedUsers.Sort((user1, user2) => user1.LatestResponse!.RequestsAhead - user2.LatestResponse!.RequestsAhead);
_status = newStatus; _status = newStatus;
NotifyStatusUpdated(); NotifyStatusUpdated();
} }
public void RevokeAccess(Guid userId)
{
var user = _users[userId];
user.Active = false;
user.LatestResponse = null;
_accessService.RevokeAccess(userId);
}
public void RevokeAllAccess()
{
foreach (var user in _users.Values)
{
RevokeAccess(user.Id);
}
}
public void Reset()
{
RevokeAllAccess();
_users = [];
}
} }
} }

View File

@ -8,7 +8,10 @@ namespace AccessQueuePlayground.Services
public Task RecalculateStatus(); public Task RecalculateStatus();
public AccessQueueStatus GetStatus(); public AccessQueueStatus GetStatus();
public Guid AddUser(); public Guid AddUser();
public void ToggleUserActivity(Guid userId); public void SetUserActive(Guid userId, bool isActive);
public void RevokeAccess(Guid userId);
public void RevokeAllAccess();
public void Reset();
} }
} }

View File

@ -68,11 +68,10 @@ namespace AccessQueueService.Data
int filledSpots = 0; int filledSpots = 0;
while (filledSpots < openSpots && _nowServing < _nextUnusedTicket) while (filledSpots < openSpots && _nowServing < _nextUnusedTicket)
{ {
if (_accessQueue.TryGetValue(_nowServing, out var nextUser)) if (_accessQueue.TryGetValue(_nowServing++, out var nextUser))
{ {
_accessQueue.Remove(_nowServing); _accessQueue.Remove(_nowServing);
_queueNumbers.Remove(nextUser.UserId); _queueNumbers.Remove(nextUser.UserId);
_nowServing++;
if (nextUser.LastActive < activeCutoff) if (nextUser.LastActive < activeCutoff)
{ {
// User is inactive, throw away their ticket // User is inactive, throw away their ticket

View File

@ -8,8 +8,6 @@ namespace AccessQueueService.Services
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
private readonly IAccessQueueRepo _accessQueueRepo; private readonly IAccessQueueRepo _accessQueueRepo;
//private readonly Dictionary<Guid, AccessTicket> _accessTickets = new();
//private readonly Queue<AccessTicket> _accessQueue = new();
private readonly SemaphoreSlim _queueLock = new(1, 1); private readonly SemaphoreSlim _queueLock = new(1, 1);
private readonly int EXP_SECONDS; private readonly int EXP_SECONDS;
private readonly int ACT_SECONDS; private readonly int ACT_SECONDS;