Initial code commit.

This commit is contained in:
2026-02-03 10:44:31 +08:00
parent 8927c5ae0e
commit d69fe2cc1f
99 changed files with 10839 additions and 0 deletions

View File

@@ -0,0 +1,210 @@
@page "/contacts"
@inject HttpClient Http
@inject IDialogService DialogService
@inject ISnackbar Snackbar
@inject BoomerangService Boomerang
<PageTitle>Contact List</PageTitle>
<MudGrid Class="py-4">
<MudItem xs="12">
<MudText Typo="Typo.h3">Contact List</MudText>
</MudItem>
<MudItem xs="12">
<MudText Typo="Typo.body1">Behold! Below is the list of all registered users in this application.</MudText>
<MudText Typo="Typo.body1" Color="Color.Error">(All data will be deleted for every 10 minutes)</MudText>
</MudItem>
<MudItem xs="12" Class="d-flex gap-4">
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Add" OnClick="OnButtonCreateClicked">Create</MudButton>
<MudSpacer />
<MudButton Variant="Variant.Filled" StartIcon="@Icons.Material.Filled.Refresh" OnClick="OnButtonRefreshClicked">Refresh</MudButton>
</MudItem>
<MudItem xs="12">
@if (contacts != null && contacts.Length > 0)
{
<MudTable Items="@contacts" Hover Breakpoint="Breakpoint.Sm" Loading="@loading" LoadingProgressColor="Color.Info" Class="py-3">
<HeaderContent>
<MudTh>Id</MudTh>
<MudTh>Username</MudTh>
<MudTh>Phone</MudTh>
<MudTh>Email</MudTh>
<MudTh>Skill Sets</MudTh>
<MudTh>Hobby</MudTh>
<MudTh>Action</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Id">@context.Id</MudTd>
<MudTd DataLabel="Username">@context.UserName</MudTd>
<MudTd DataLabel="Phone">@context.Phone</MudTd>
<MudTd DataLabel="Email">@context.Email</MudTd>
<MudTd DataLabel="Skill Sets">@context.SkillSets</MudTd>
<MudTd DataLabel="Hobby">@context.Hobby</MudTd>
<MudTd>
<MudTooltip Text="Edit" Placement="Placement.Top" Arrow>
<MudIconButton Icon="@Icons.Material.Filled.Edit" OnClick="() => OnEditContactClicked(context.Id)" Color="Color.Warning" Size="Size.Small" />
</MudTooltip>
<MudTooltip Text="Delete" Placement="Placement.Top" Arrow>
<MudIconButton Icon="@Icons.Material.Filled.Delete" OnClick="() => OnDeleteContactClicked(context.Id)" Color="Color.Error" Size="Size.Small" />
</MudTooltip>
</MudTd>
</RowTemplate>
<PagerContent>
<MudTablePager />
</PagerContent>
</MudTable>
}
else
{
<MudText Typo="Typo.h5">No item to display. Click on the 'Create' button to add.</MudText>
}
</MudItem>
</MudGrid>
<MudOverlay Visible="loading" DarkBackground ZIndex="9999">
<MudProgressCircular Color="Color.Primary" Indeterminate />
</MudOverlay>
@code {
private GetAllContactsResponse[]? contacts;
private bool loading;
protected override async Task OnInitializedAsync()
{
await LoadTableAsync();
await Boomerang.AddVariableAsync("PageName", "Contacts");
await Boomerang.SendBeaconAsync();
}
private async Task LoadTableAsync()
{
loading = true;
var response = await Http.GetAsync("api/v1/contacts");
var result = await response.ToResult<List<GetAllContactsResponse>>();
if (result.Succeeded)
{
contacts = result.Data.ToArray();
}
else
{
contacts = null;
foreach (var message in result.Messages)
{
Snackbar.Add(message, Severity.Error);
}
}
loading = false;
}
private async Task OnButtonRefreshClicked()
{
await LoadTableAsync();
}
private async Task OnButtonCreateClicked()
{
var dialogOptions = new DialogOptions { BackdropClick = false, CloseButton = true };
var dialog = await DialogService.ShowAsync<CreateContactDialog>("Create New Contact", dialogOptions);
var dialogResult = await dialog.Result;
if (dialogResult!.Canceled)
{
return;
}
loading = true;
StateHasChanged();
var contactData = (AddEditContactCommand)dialogResult.Data!;
var response = await Http.PostAsJsonAsync("api/v1/contacts", contactData);
var result = await response.ToResult<int>();
if (result.Succeeded)
{
Snackbar.Add(result.Messages[0], Severity.Success);
await LoadTableAsync();
}
else
{
foreach (var message in result.Messages)
{
Snackbar.Add(message, Severity.Error);
}
loading = false;
}
}
async Task OnEditContactClicked(int id)
{
var dialogParams = new DialogParameters { ["Id"] = id };
var dialogOptions = new DialogOptions { BackdropClick = false, CloseButton = true };
var dialog = await DialogService.ShowAsync<EditContactDialog>("Edit Contact", dialogParams, dialogOptions);
var dialogResult = await dialog.Result;
if (dialogResult!.Canceled)
{
return;
}
loading = true;
StateHasChanged();
var contactData = (AddEditContactCommand)dialogResult.Data!;
var response = await Http.PostAsJsonAsync("api/v1/contacts", contactData);
var result = await response.ToResult<int>();
if (result.Succeeded)
{
Snackbar.Add(result.Messages[0], Severity.Success);
await LoadTableAsync();
}
else
{
foreach (var message in result.Messages)
{
Snackbar.Add(message, Severity.Error);
}
loading = false;
}
}
async Task OnDeleteContactClicked(int id)
{
var dialogResult = await DialogService.ShowMessageBox("Delete Contact", "Confirm to delete the item? This action cannot be undone.", yesText: "Delete!", cancelText: "No");
if (dialogResult == null)
{
return;
}
loading = true;
StateHasChanged();
var deleteResponse = await Http.DeleteAsync($"api/v1/contacts/{id}");
if (deleteResponse.IsSuccessStatusCode)
{
var result = await deleteResponse.ToResult();
if (result!.Succeeded)
{
Snackbar.Add("Contact deleted successfully.", Severity.Success);
await LoadTableAsync();
}
else
{
Snackbar.Add(result.Messages[0], Severity.Error);
}
}
else
{
Snackbar.Add("An error has occurred.", Severity.Error);
loading = false;
}
}
}

View File

@@ -0,0 +1,60 @@
<MudDialog>
<DialogContent>
<MudForm @ref="form" Model="request" @bind-IsValid="success" @bind-Errors="errors">
<MudTextField Label="Username" Required @bind-Value="request.UserName" />
<MudTextField Label="Email" Required @bind-Value="request.Email" />
<MudTextField Label="Phone" Required @bind-Value="request.Phone" MaxLength="20" />
<MudTextField Label="Skill Sets" Required @bind-Value="request.SkillSets" />
<MudTextField Label="Hobby" Required @bind-Value="request.Hobby" />
</MudForm>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel">Cancel</MudButton>
<MudButton OnClick="Submit">Ok</MudButton>
</DialogActions>
</MudDialog>
@code {
private static readonly string[] SampleUserNames = new[]
{
"alex99",
"samwise",
"lunaStar",
"maverick",
"nova",
"pixelPro",
"kanu",
"tay",
"zorin",
"echo"
};
private static readonly Random _rnd = new();
[CascadingParameter]
private IMudDialogInstance MudDialog { get; set; } = null!;
private AddEditContactCommand request = new();
private MudForm? form;
private bool success;
private string[] errors = { };
private void Cancel() => MudDialog.Cancel();
private async Task Submit()
{
await form!.Validate();
if (!success)
{
return;
}
MudDialog.Close(request);
}
protected override Task OnInitializedAsync()
{
// Assign a random username from the in-memory list when the dialog opens
request.UserName = SampleUserNames[_rnd.Next(SampleUserNames.Length)];
return base.OnInitializedAsync();
}
}

View File

@@ -0,0 +1,74 @@
@using Sufi.Demo.PeopleDirectory.Application.Features.Contacts.Queries.GetById
@inject HttpClient Http
<MudDialog>
<DialogContent>
@if (showAlert)
{
<MudAlert Severity="Severity.Error">@alertMessage</MudAlert>
}
<MudForm @ref="form" Model="request" @bind-IsValid="success" @bind-Errors="errors">
<MudTextField Label="Username" Required="true" @bind-Value="request.UserName" />
<MudTextField Label="Email" Required="true" @bind-Value="request.Email" />
<MudTextField Label="Phone" Required="true" @bind-Value="request.Phone" />
<MudTextField Label="Skill Sets" Required="true" @bind-Value="request.SkillSets" />
<MudTextField Label="Hobby" Required="true" @bind-Value="request.Hobby" />
</MudForm>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel">Cancel</MudButton>
<MudButton OnClick="Submit">Ok</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter]
private IMudDialogInstance MudDialog { get; set; } = null!;
[Parameter]
public int Id { get; set; }
private AddEditContactCommand request = new();
private MudForm? form;
private bool success;
private string[] errors = { };
private bool showAlert;
private string? alertMessage;
protected override async Task OnInitializedAsync()
{
var response = await Http.GetAsync($"api/v1/contacts/{Id}");
var result = await response.ToResult<GetContactByIdResponse>();
if (result.Succeeded)
{
var contact = result.Data;
request.Id = Id;
request.UserName = contact!.UserName;
request.Email = contact.Email;
request.Phone = contact.Phone;
request.SkillSets = contact.SkillSets;
request.Hobby = contact.Hobby;
showAlert = false;
alertMessage = "";
}
else
{
showAlert = true;
alertMessage = string.Join(',', result.Messages);
}
}
private void Cancel() => MudDialog.Cancel();
private async Task Submit()
{
await form!.Validate();
if (!success)
{
return;
}
MudDialog!.Close(request);
}
}

View File

@@ -0,0 +1,22 @@
@page "/"
@inject BoomerangService Boomerang
<PageTitle>Index - Demo App</PageTitle>
<MudGrid Class="py-4">
<MudItem xs="12">
<MudText Typo="Typo.h3">Welcome to this demo app.</MudText>
</MudItem>
<MudItem xs="12">
<MudText Typo="Typo.body1">Please view the contact list page.</MudText>
</MudItem>
</MudGrid>
@code {
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await Boomerang.AddVariableAsync("PageName", "Index");
await Boomerang.SendBeaconAsync();
}
}