Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Admin] Component: List event details - UI component #219 #266

Merged
merged 12 commits into from
Sep 6, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@page "/admin/events"

<PageTitle>AdminEvents</PageTitle>

<h1>AdminEvents</h1>

<p>This component demonstrates showing admin events.</p>

<AdminEventsComponent @rendermode="InteractiveServer"/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<div class="admin-event-active-state">
<div class="@GetActiveClass(IsActive)"></div>
</div>

@code {
[Parameter]
public required bool IsActive { get; set; }

private string GetActiveClass(bool? isActive)
{
if (!isActive.HasValue)
{
return "deactivated";
}

return isActive.Value ? "activated" : "deactivated";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.admin-event-active-state {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
}

.activated {
width: 10px;
height: 10px;
background-color: green;
border-radius: 50%;
display: inline-block;
}

.deactivated {
width: 10px;
height: 10px;
background-color: red;
border-radius: 50%;
display: inline-block;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
@using AzureOpenAIProxy.PlaygroundApp.Models

<div id="admin-events-component">
@if (eventDetails == null)
{
<p><em>Loading...</em></p>
}
else
{
<div id="admin-events-table">
<FluentDataGrid Items="@eventDetails" Pagination="@pagination" >
<PropertyColumn Class="fluent-datagrid-cell" Property="@(p => p.Title)" Align="@Align.Center" Sortable="true" />
<PropertyColumn Class="fluent-datagrid-cell" Property="@(p => p.DateStart)" Format="yyyy-MM-dd" Align="@Align.Center" Sortable="true" />
<PropertyColumn Class="fluent-datagrid-cell" Property="@(p => p.DateEnd)" Format="yyyy-MM-dd" Align="@Align.Center" Sortable="true" />
<PropertyColumn Class="fluent-datagrid-cell" Property="@(p => p.TimeZone)" Align="@Align.Center" Sortable="true" />
<PropertyColumn Class="fluent-datagrid-cell" Property="@(p => p.OrganizerName)" Align="@Align.Center" Sortable="true" />
<PropertyColumn Class="fluent-datagrid-cell" Property="@(p => p.CoorganizerName)" Align="@Align.Center" Sortable="true" />
<PropertyColumn Class="fluent-datagrid-cell" Property="@(p => p.MaxTokenCap)" Align="@Align.Center" Sortable="true" />
<PropertyColumn Class="fluent-datagrid-cell" Property="@(p => p.DailyRequestCap)" Align="@Align.Center" Sortable="true" />
<TemplateColumn Class="fluent-datagrid-cell" Title="Active" Align="@Align.Center">
<AdminEventIsActiveComponent IsActive="@(((AdminEventDetails)@context).IsActive)" />
</TemplateColumn>
<TemplateColumn Class="fluent-datagrid-cell" Title="Actions" Align="@Align.Center">
<FluentButton aria-label="Edit item" IconEnd="@(new Icons.Regular.Size16.Edit())" />
<FluentButton aria-label="Delete item" IconEnd="@(new Icons.Regular.Size16.Delete())" />
</TemplateColumn>
</FluentDataGrid>
</div>

<div class="page-button-box">
@if (pagination.TotalItemCount.HasValue)
{
for (var pageIndex = 0; pageIndex <= pagination.LastPageIndex; pageIndex++)
{
var capturedIndex = pageIndex;
<FluentButton class="page-button" @onclick="@(() => GoToPageAsync(capturedIndex))" Appearance="@PageButtonAppearance(capturedIndex)"
aria-current="@AriaCurrentValue(capturedIndex)">
@(capturedIndex + 1)
</FluentButton>
}
}
</div>
}
</div>

@code {
private IQueryable<AdminEventDetails>? eventDetails;
private PaginationState pagination = new PaginationState { ItemsPerPage = 10 };

protected override async Task OnInitializedAsync()
{
// Simulate asynchronous loading to demonstrate streaming rendering
await Task.Delay(100);

var startDate = DateOnly.FromDateTime(DateTime.Now);

// make dummy data
eventDetails = Enumerable.Range(1, 150).Select(index => new AdminEventDetails
{
EventId = Guid.NewGuid(),
Title = $"event title #{index}",
Summary = "dummy summary",
Description = "dummy description",
DateStart = DateTimeOffset.Now,
DateEnd = DateTimeOffset.Now.AddDays(7 + index),
TimeZone = "KST",
IsActive = index % 3 == 0,
OrganizerName = $"Charlie_{index}",
OrganizerEmail = $"user_{index}@gmail.com",
CoorganizerName = $"Bravo_{index}",
CoorganizerEmail = $"support_{index}@gmail.com",
MaxTokenCap = (100 + index) * 100,
DailyRequestCap = index * 10
}).AsQueryable();

pagination.TotalItemCountChanged += (sender, eventArgs) => StateHasChanged();
}

private async Task GoToPageAsync(int pageIndex)
{
await pagination.SetCurrentPageIndexAsync(pageIndex);
}

private Appearance PageButtonAppearance(int pageIndex)
=> pagination.CurrentPageIndex == pageIndex ? Appearance.Accent : Appearance.Neutral;

private string? AriaCurrentValue(int pageIndex)
=> pagination.CurrentPageIndex == pageIndex ? "page" : null;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.fluent-datagrid-cell {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}

.page-button-box {
display: flex;
justify-content: center;
align-items: center;
margin-top: 20px;
}

.page-button {
margin-left: 10px;
}
3 changes: 2 additions & 1 deletion src/AzureOpenAIProxy.PlaygroundApp/Components/_Imports.razor
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@

@using AzureOpenAIProxy.PlaygroundApp
@using AzureOpenAIProxy.PlaygroundApp.Components
@using AzureOpenAIProxy.PlaygroundApp.Components.UI
@using AzureOpenAIProxy.PlaygroundApp.Components.UI
@using AzureOpenAIProxy.PlaygroundApp.Components.UI.Admin
60 changes: 60 additions & 0 deletions src/AzureOpenAIProxy.PlaygroundApp/Models/AdminEventDetails.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System.Text.Json.Serialization;

namespace AzureOpenAIProxy.PlaygroundApp.Models;

/// <summary>
/// This represent the event detail data for response by admin event endpoint.
/// </summary>
public class AdminEventDetails : EventDetails
{
/// <summary>
/// Gets or sets the event description.
/// </summary>
public string? Description { get; set; }

/// <summary>
/// Gets or sets the event start date.
/// </summary>
[JsonRequired]
public DateTimeOffset DateStart { get; set; }

/// <summary>
/// Gets or sets the event end date.
/// </summary>
[JsonRequired]
public DateTimeOffset DateEnd { get; set; }

/// <summary>
/// Gets or sets the event start to end date timezone.
/// </summary>
[JsonRequired]
public string TimeZone { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the event active status.
/// </summary>
[JsonRequired]
public bool IsActive { get; set; }

/// <summary>
/// Gets or sets the event organizer name.
/// </summary>
[JsonRequired]
public string OrganizerName { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the event organizer email.
/// </summary>
[JsonRequired]
public string OrganizerEmail { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the event coorganizer name.
/// </summary>
public string? CoorganizerName { get; set; }

/// <summary>
/// Gets or sets the event coorganizer email.
/// </summary>
public string? CoorganizerEmail { get; set; }
}
39 changes: 39 additions & 0 deletions src/AzureOpenAIProxy.PlaygroundApp/Models/EventDetails.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.Text.Json.Serialization;

namespace AzureOpenAIProxy.PlaygroundApp.Models;

/// <summary>
/// This represents the event's detailed data for response by EventEndpoint.
/// </summary>
public class EventDetails
{
/// <summary>
/// Gets or sets the event id.
/// </summary>
[JsonRequired]
public Guid EventId { get; set; }

/// <summary>
/// Gets or sets the event title name.
/// </summary>
[JsonRequired]
public string Title { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the event summary.
/// </summary>
[JsonRequired]
public string Summary { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the Azure OpenAI Service request max token capacity.
/// </summary>
[JsonRequired]
public int MaxTokenCap { get; set; }

/// <summary>
/// Gets or sets the Azure OpenAI Service daily request capacity.
/// </summary>
[JsonRequired]
public int DailyRequestCap { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using FluentAssertions;

using Microsoft.Playwright;
using Microsoft.Playwright.NUnit;

namespace AzureOpenAIProxy.PlaygroundApp.Tests.Pages;

[Parallelizable(ParallelScope.Self)]
[TestFixture]
[Property("Category", "Integration")]
public class AdminEventsPageTests : PageTest
{
public override BrowserNewContextOptions ContextOptions() => new()
{
IgnoreHTTPSErrors = true,
};

[SetUp]
public async Task Setup()
{
await Page.GotoAsync("https://localhost:5001/admin/events");
await Page.WaitForLoadStateAsync(LoadState.NetworkIdle);
}

[Test]
public async Task Given_Events_Page_When_Navigated_Then_It_Should_Have_ListEventDetailsComponent()
{
// Act
var adminEventsComponent = await Page.QuerySelectorAsync("#admin-events-component");

// Assert
adminEventsComponent.Should().NotBeNull();
}

[Test]
public async Task Given_Events_Page_When_Navigated_Then_It_Should_Have_EventDetailsTable()
{
// wait for construct table
await Task.Delay(2000);

// Act
var adminEventsTable = await Page.QuerySelectorAsync("#admin-events-table");

// Assert
adminEventsTable.Should().NotBeNull();
}
}
Loading