199 lines
7.9 KiB
Plaintext
199 lines
7.9 KiB
Plaintext
@page "/members"
|
|
@using NimbusFlow.Frontend.Services
|
|
@using NimbusFlow.Frontend.Models
|
|
@inject IApiService ApiService
|
|
@inject NavigationManager Navigation
|
|
@inject IJSRuntime JSRuntime
|
|
|
|
<PageTitle>Members</PageTitle>
|
|
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h1 class="nimbus-page-title">
|
|
<i class="bi bi-people-fill me-3"></i>Members
|
|
</h1>
|
|
<a href="/members/create" class="btn btn-nimbus-primary">
|
|
<i class="bi bi-person-plus-fill me-2"></i>Add Member
|
|
</a>
|
|
</div>
|
|
|
|
@if (loading)
|
|
{
|
|
<div class="text-center py-5">
|
|
<div class="spinner-border nimbus-spinner" role="status" style="width: 3rem; height: 3rem;">
|
|
<span class="visually-hidden">Loading...</span>
|
|
</div>
|
|
<p class="mt-3 text-muted">Loading members...</p>
|
|
</div>
|
|
}
|
|
else if (members.Any())
|
|
{
|
|
<!-- Filter Controls -->
|
|
<div class="row mb-3">
|
|
<div class="col-md-4">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" @bind="showInactiveMembers" id="showInactiveCheck">
|
|
<label class="form-check-label" for="showInactiveCheck">
|
|
Show inactive members
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-12">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Classification</th>
|
|
<th>Email</th>
|
|
<th>Phone</th>
|
|
<th>Status</th>
|
|
<th>Last Accepted</th>
|
|
<th>Decline Streak</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach (var member in filteredMembers)
|
|
{
|
|
<tr>
|
|
<td>
|
|
<strong>@member.FullName</strong>
|
|
</td>
|
|
<td>
|
|
<span class="badge badge-nimbus-classification">@member.ClassificationName</span>
|
|
</td>
|
|
<td>@member.Email</td>
|
|
<td>@member.PhoneNumber</td>
|
|
<td>
|
|
@if (member.IsActive == 1)
|
|
{
|
|
<span class="badge badge-nimbus-active">
|
|
<i class="bi bi-check-circle-fill me-1"></i>Active
|
|
</span>
|
|
}
|
|
else
|
|
{
|
|
<span class="badge badge-nimbus-inactive">
|
|
<i class="bi bi-x-circle-fill me-1"></i>Inactive
|
|
</span>
|
|
}
|
|
</td>
|
|
<td>
|
|
@if (member.LastAcceptedAt.HasValue)
|
|
{
|
|
@member.LastAcceptedAt.Value.ToString("MMM dd, yyyy")
|
|
}
|
|
else
|
|
{
|
|
<span class="text-muted">Never</span>
|
|
}
|
|
</td>
|
|
<td>
|
|
@if (member.DeclineStreak > 0)
|
|
{
|
|
<span class="badge badge-nimbus-pending">
|
|
<i class="bi bi-exclamation-triangle-fill me-1"></i>@member.DeclineStreak
|
|
</span>
|
|
}
|
|
else
|
|
{
|
|
<span class="text-muted">—</span>
|
|
}
|
|
</td>
|
|
<td>
|
|
<div class="btn-group" role="group">
|
|
<a href="/members/@member.MemberId" class="btn btn-sm btn-nimbus-secondary me-1">
|
|
<i class="bi bi-eye-fill me-1"></i>View
|
|
</a>
|
|
<a href="/members/@member.MemberId/edit" class="btn btn-sm btn-outline-warning">
|
|
<i class="bi bi-pencil-fill me-1"></i>Edit
|
|
</a>
|
|
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmDelete(member)">Delete</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="row mt-3">
|
|
<div class="col-md-12">
|
|
<small class="text-muted">
|
|
Showing @filteredMembers.Count() of @members.Count members
|
|
</small>
|
|
</div>
|
|
</div>
|
|
}
|
|
else
|
|
{
|
|
<div class="text-center">
|
|
<div class="alert alert-info">
|
|
<h4>No Members Found</h4>
|
|
<p>There are currently no members in the system.</p>
|
|
<a href="/members/create" class="btn btn-primary">Add Your First Member</a>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
@code {
|
|
private List<Member> members = new();
|
|
private bool loading = true;
|
|
private bool showInactiveMembers = false;
|
|
|
|
private IEnumerable<Member> filteredMembers =>
|
|
showInactiveMembers ? members : members.Where(m => m.IsActive == 1);
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
await LoadMembers();
|
|
}
|
|
|
|
private async Task LoadMembers()
|
|
{
|
|
try
|
|
{
|
|
loading = true;
|
|
members = await ApiService.GetMembersAsync();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Handle error (could show toast notification)
|
|
Console.WriteLine($"Error loading members: {ex.Message}");
|
|
}
|
|
finally
|
|
{
|
|
loading = false;
|
|
}
|
|
}
|
|
|
|
private async Task ConfirmDelete(Member member)
|
|
{
|
|
var confirmed = await JSRuntime.InvokeAsync<bool>("confirm", $"Are you sure you want to delete {member.FullName}?");
|
|
if (confirmed)
|
|
{
|
|
try
|
|
{
|
|
var success = await ApiService.DeleteMemberAsync(member.MemberId);
|
|
if (success)
|
|
{
|
|
await LoadMembers(); // Refresh the list
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Error deleting member: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
} |