-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* Feat: 어드민 UI 컴포넌트 Page 추가 * Test: 어드민 UI 컴포넌트 테스트 추가 * Feat: 버튼 추가 * Fix: 피드백 적용 - AzureOpenAIProxy.sln 파일 이전 내용으로 되돌리기 - AzureOpenAIProxy.PlaygroundApp.Tests/Pages/NewEventDetailsTests.cs 테스트 코드 삭제 * Update PlaygroundApp Model 최신화 * Refactor: 컴포넌트 수정 - Id 파라미터 추가 - 버튼 Id 추가 - TextFieldType 추가 - Id 값 kebab-casing 수정 * Feat: NodaTime을 사용하여 Time Zone Option 추가 * Feat: 기본값 및 이벤트 설정 - 기본 날짜/시간 설정 추가 - 이벤트 추가/취소 버튼 이벤트 바인딩 추가 - Max Token Cap, Daily Request Cap 값 바인딩 * Refactor: 불필요한 코드 정리 * Refactor: Time Zone Select 높이 수정 * Refactor: CSS 적용 방식 수정 - 외부 스타일 시트 적용 * Revert "Refactor: CSS 적용 방식 수정 - 외부 스타일 시트 적용" This reverts commit 197d7ee. * Refactor: 이벤트 종료 날짜 기본값 수정 (오늘 기준 다음날로 적용) * Refactor: NewEventDetailsComponent.razor * Refactor: Remove @temp~ variables * Fix: FluentDatePicker, FluentTimePicker ValueChanged error fix * Refactor: NewEventDetailsComponent.razor * Test: Add NewEventDetailsComponent.razor test * Test: Add input test * Fix: delete inject * Feat: Get local browser timezone * Refactor: Add JS error handling * Refactor: NewEventDetailsComponent.razor * Test: Add init timezone test * Fix: Browser Timezone > System Timezone * Fix: Test error fix (Now > UtcNow) * Fix: add attribute Culture to FluentDatePicker * Fix: Add culture info in OnAfterRenderAsync * Feat: Convert from Windows timezone to IANA timezone using TimeZoneConverter * Test: Convert from Windows timezone to IANA timezone using TimeZoneConverter * Fix: Check OS to get timezone * Test: Refactoring NewEventDetailsPageTests * Test: Refactoring NewEventDetailsPageTests * Refactor: Refactoring NewEventDetailsComponent and test - Delete TimeZoneConverter - Split input event datetime test * Test: Add NewEventDetailsPageTests.cs to AppHost test
- Loading branch information
Showing
8 changed files
with
536 additions
and
97 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
181 changes: 181 additions & 0 deletions
181
src/AzureOpenAIProxy.PlaygroundApp/Components/UI/Admin/NewEventDetailsComponent.razor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
@using AzureOpenAIProxy.PlaygroundApp.Clients | ||
@using AzureOpenAIProxy.PlaygroundApp.Models; | ||
|
||
@using System.Globalization | ||
|
||
@using NodaTime | ||
@using NodaTime.Extensions | ||
@using NodaTime.TimeZones | ||
|
||
<FluentLayout Id="@Id"> | ||
@if (adminEventDetails == null) | ||
{ | ||
<p><em>Loading...</em></p> | ||
} | ||
else | ||
{ | ||
<FluentHeader>New Event</FluentHeader> | ||
<FluentBodyContent> | ||
<section> | ||
<h2>Event Infomation</h2> | ||
|
||
<FluentStack Class="create-fluent-stack" Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center"> | ||
<FluentLabel For="event-title" Class="create-input-label">Title</FluentLabel> | ||
<FluentTextField Id="event-title" Name="title" TextFieldType="TextFieldType.Text" Required /> | ||
</FluentStack> | ||
|
||
<FluentStack Class="create-fluent-stack" Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center"> | ||
<FluentLabel For="event-summary" Class="create-input-label">Summary</FluentLabel> | ||
<FluentTextField id="event-summary" TextFieldType="TextFieldType.Text" Required /> | ||
</FluentStack> | ||
|
||
<FluentStack Class="create-fluent-stack" Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center"> | ||
<FluentLabel For="event-description" Class="create-input-label">Description</FluentLabel> | ||
<FluentTextArea Id="event-description" Style="width:300px" /> | ||
</FluentStack> | ||
|
||
<FluentStack Class="create-fluent-stack" Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center"> | ||
<FluentLabel For="event-start-date" Class="create-input-label">Event Start Date</FluentLabel> | ||
<FluentDatePicker Id="event-start-date" Value="@adminEventDetails.DateStart.DateTime" ValueChanged="@(e => adminEventDetails.DateStart = e.Value)" Culture="System.Globalization.CultureInfo.CurrentCulture" /> | ||
<FluentTimePicker Id="event-start-time" Value="@adminEventDetails.DateStart.DateTime" ValueChanged="@(e => adminEventDetails.DateStart = e.Value)" /> | ||
</FluentStack> | ||
|
||
<FluentStack Class="create-fluent-stack" Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center"> | ||
<FluentLabel For="event-end-date" Class="create-input-label">Event End Date</FluentLabel> | ||
<FluentDatePicker Id="event-end-date" Value="@adminEventDetails.DateEnd.DateTime" ValueChanged="@(e => adminEventDetails.DateEnd = e.Value)" Culture="System.Globalization.CultureInfo.CurrentCulture" /> | ||
<FluentTimePicker Id="event-end-time" Value="@adminEventDetails.DateEnd.DateTime" ValueChanged="@(e => adminEventDetails.DateEnd = e.Value)" /> | ||
</FluentStack> | ||
|
||
<FluentStack Class="create-fluent-stack" Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center"> | ||
<FluentLabel For="event-timezone" Class="create-input-label">Time Zone</FluentLabel> | ||
<FluentSelect Id="event-timezone" @bind-Value="@adminEventDetails.TimeZone" Height="500px" TOption="string" Required> | ||
@foreach (var timeZone in timeZoneList) | ||
{ | ||
<FluentOption Value="@timeZone.Id">@timeZone.Id</FluentOption> | ||
} | ||
</FluentSelect> | ||
</FluentStack> | ||
</section> | ||
|
||
<section> | ||
<h2>Event Organizer</h2> | ||
|
||
<FluentStack Class="create-fluent-stack" Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center"> | ||
<FluentLabel For="event-organizer-name" Class="create-input-label">Organizer Name</FluentLabel> | ||
<FluentTextField Id="event-organizer-name" TextFieldType="TextFieldType.Text" Required /> | ||
</FluentStack> | ||
|
||
|
||
<FluentStack Class="create-fluent-stack" Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center"> | ||
<FluentLabel For="event-organizer-email" Class="create-input-label">Organizer Email</FluentLabel> | ||
<FluentTextField Id="event-organizer-email" TextFieldType="TextFieldType.Email" Required /> | ||
</FluentStack> | ||
</section> | ||
|
||
<section> | ||
<h2>Event Coorganizers</h2> | ||
|
||
<FluentStack Class="create-fluent-stack" Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center"> | ||
<FluentLabel For="event-coorgnizer-name" Class="create-input-label">Coorgnizer Name</FluentLabel> | ||
<FluentTextField Id="event-coorgnizer-name" TextFieldType="TextFieldType.Text" Required /> | ||
</FluentStack> | ||
|
||
<FluentStack Class="create-fluent-stack" Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center"> | ||
<FluentLabel For="event-coorgnizer-email" Class="create-input-label">Coorgnizer Email</FluentLabel> | ||
<FluentTextField Id="event-coorgnizer-email" TextFieldType="TextFieldType.Email" Required /> | ||
</FluentStack> | ||
</section> | ||
|
||
<section> | ||
<h2>Event Configuration</h2> | ||
|
||
<FluentStack Class="create-fluent-stack" Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center"> | ||
<FluentLabel For="event-max-token-cap" Class="create-input-label">Max Token Cap</FluentLabel> | ||
<FluentNumberField Id="event-max-token-cap" @bind-Value="adminEventDetails.MaxTokenCap" Required /> | ||
</FluentStack> | ||
|
||
<FluentStack Class="create-fluent-stack" Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center"> | ||
<FluentLabel For="event-daily-request-cap" Class="create-input-label">Daily Request Cap</FluentLabel> | ||
<FluentNumberField Id="event-daily-request-cap" @bind-Value="adminEventDetails.DailyRequestCap" Required /> | ||
</FluentStack> | ||
</section> | ||
|
||
<section class="button-section"> | ||
<FluentButton Id="admin-event-detail-add" Appearance="Appearance.Accent" Class="button" OnClick="AddEvent">Add Event</FluentButton> | ||
<FluentButton Id="admin-event-detail-cancel" Appearance="Appearance.Outline" Class="button" OnClick="CancelEvent">Cancel</FluentButton> | ||
</section> | ||
</FluentBodyContent> | ||
} | ||
</FluentLayout> | ||
|
||
|
||
@code { | ||
private List<DateTimeZone>? timeZoneList; | ||
private AdminEventDetails? adminEventDetails; | ||
private DateTimeOffset currentTime = DateTimeOffset.UtcNow; | ||
|
||
[Parameter] | ||
public string? Id { get; set; } | ||
|
||
protected override async Task OnInitializedAsync() | ||
Check warning on line 120 in src/AzureOpenAIProxy.PlaygroundApp/Components/UI/Admin/NewEventDetailsComponent.razor
|
||
{ | ||
adminEventDetails = adminEventDetails == null ? new() : adminEventDetails; | ||
|
||
timeZoneList = DateTimeZoneProviders.Tzdb.GetAllZones().ToList(); | ||
|
||
CultureInfo customCulture = (CultureInfo)CultureInfo.CurrentCulture.Clone(); | ||
customCulture.DateTimeFormat.ShortDatePattern = "yyyy-MM-dd"; | ||
customCulture.DateTimeFormat.ShortTimePattern = "HH:mm"; | ||
|
||
CultureInfo.DefaultThreadCurrentCulture = customCulture; | ||
CultureInfo.DefaultThreadCurrentUICulture = customCulture; | ||
} | ||
|
||
protected override async Task OnAfterRenderAsync(bool firstRender) | ||
{ | ||
if (firstRender) | ||
{ | ||
var timezoneId = GetIanaTimezoneId(); | ||
currentTime = GetCurrentDateTimeOffset(timezoneId); | ||
|
||
adminEventDetails.DateStart = currentTime.AddHours(1).AddMinutes(-currentTime.Minute); | ||
adminEventDetails.DateEnd = currentTime.AddDays(1).AddHours(1).AddMinutes(-currentTime.Minute); | ||
adminEventDetails.TimeZone = timezoneId; | ||
|
||
await InvokeAsync(StateHasChanged); | ||
} | ||
} | ||
|
||
private async Task AddEvent() | ||
{ | ||
await Task.CompletedTask; | ||
} | ||
|
||
private async Task CancelEvent() | ||
{ | ||
await Task.CompletedTask; | ||
} | ||
|
||
private string GetIanaTimezoneId() | ||
{ | ||
string timezoneId = TimeZoneInfo.Local.Id; | ||
|
||
if (OperatingSystem.IsWindows()) | ||
{ | ||
if (TimeZoneInfo.TryConvertWindowsIdToIanaId(timezoneId, out var ianaTimezoneId)) | ||
{ | ||
timezoneId = ianaTimezoneId; | ||
} | ||
} | ||
|
||
return timezoneId; | ||
} | ||
|
||
private DateTimeOffset GetCurrentDateTimeOffset(string timezoneId) | ||
{ | ||
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timezoneId); | ||
|
||
return TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, timeZoneInfo); | ||
} | ||
} | ||
|
25 changes: 25 additions & 0 deletions
25
src/AzureOpenAIProxy.PlaygroundApp/Components/UI/Admin/NewEventDetailsComponent.razor.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
section { | ||
margin-bottom: 100px | ||
} | ||
|
||
::deep .create-input-label { | ||
width: 200px; | ||
--type-ramp-base-font-size: 22px; | ||
} | ||
|
||
::deep .create-fluent-stack { | ||
height: 100px; | ||
} | ||
|
||
.button-section { | ||
display: flex; | ||
justify-content: center; | ||
gap: 50px; | ||
} | ||
|
||
.button { | ||
width: 150px; | ||
height: 50px; | ||
font-size: 16px; | ||
margin: 0 10px; | ||
} |
118 changes: 59 additions & 59 deletions
118
src/AzureOpenAIProxy.PlaygroundApp/Models/AdminEventDetails.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,60 +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; } | ||
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; } | ||
} |
Oops, something went wrong.