feat(frontend+backend): add theming to the blazor frontend
This commit is contained in:
@@ -7,8 +7,10 @@
|
||||
<base href="/" />
|
||||
<link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="app.css" />
|
||||
<link rel="stylesheet" href="css/nimbusflow.css" />
|
||||
<link rel="stylesheet" href="NimbusFlow.Frontend.styles.css" />
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet" />
|
||||
<HeadOutlet />
|
||||
</head>
|
||||
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<div class="page">
|
||||
<div class="sidebar">
|
||||
<div class="sidebar nimbus-sidebar">
|
||||
<NavMenu />
|
||||
</div>
|
||||
|
||||
<main>
|
||||
<div class="top-row px-4">
|
||||
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
|
||||
</div>
|
||||
|
||||
<article class="content px-4">
|
||||
@Body
|
||||
</article>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<div class="top-row ps-3 navbar navbar-dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="">NimbusFlow</a>
|
||||
<a class="navbar-brand" href="">
|
||||
<i class="bi bi-cloud nimbus-brand-icon" aria-hidden="true"></i>NimbusFlow
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,27 +10,27 @@
|
||||
|
||||
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
|
||||
<nav class="flex-column">
|
||||
<div class="nav-item px-3">
|
||||
<div class="nav-item">
|
||||
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
|
||||
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Dashboard
|
||||
<i class="bi bi-house-door-fill me-2" aria-hidden="true"></i> Dashboard
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<div class="nav-item">
|
||||
<NavLink class="nav-link" href="members">
|
||||
<span class="bi bi-people-fill-nav-menu" aria-hidden="true"></span> Members
|
||||
<i class="bi bi-people-fill me-2" aria-hidden="true"></i> Members
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<div class="nav-item">
|
||||
<NavLink class="nav-link" href="schedules">
|
||||
<span class="bi bi-calendar-event-fill-nav-menu" aria-hidden="true"></span> Schedules
|
||||
<i class="bi bi-calendar-event-fill me-2" aria-hidden="true"></i> Schedules
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<div class="nav-item">
|
||||
<NavLink class="nav-link" href="services">
|
||||
<span class="bi bi-calendar-plus-fill-nav-menu" aria-hidden="true"></span> Services
|
||||
<i class="bi bi-calendar-plus-fill me-2" aria-hidden="true"></i> Services
|
||||
</NavLink>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -5,90 +5,71 @@
|
||||
|
||||
<PageTitle>NimbusFlow Dashboard</PageTitle>
|
||||
|
||||
<h1>NimbusFlow Dashboard</h1>
|
||||
<h1 class="nimbus-page-title">
|
||||
<i class="bi bi-speedometer2 me-3"></i>NimbusFlow Dashboard
|
||||
</h1>
|
||||
|
||||
<div class="row">
|
||||
<div class="row g-4">
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-primary mb-3">
|
||||
<div class="card-header">Active Members</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">@activeMemberCount</h4>
|
||||
<div class="card nimbus-dashboard-card card-members mb-3">
|
||||
<div class="card-header d-flex align-items-center">
|
||||
<i class="bi bi-people-fill me-2"></i>
|
||||
Active Members
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
<h2 class="card-title mb-0">@activeMemberCount</h2>
|
||||
<small class="opacity-75">Currently Active</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-success mb-3">
|
||||
<div class="card-header">Pending Schedules</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">@pendingScheduleCount</h4>
|
||||
<div class="card nimbus-dashboard-card card-schedules mb-3">
|
||||
<div class="card-header d-flex align-items-center">
|
||||
<i class="bi bi-calendar-event-fill me-2"></i>
|
||||
Pending Schedules
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
<h2 class="card-title mb-0">@pendingScheduleCount</h2>
|
||||
<small class="opacity-75">Awaiting Response</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-warning mb-3">
|
||||
<div class="card-header">Upcoming Services</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">@upcomingServiceCount</h4>
|
||||
<div class="card nimbus-dashboard-card card-services mb-3">
|
||||
<div class="card-header d-flex align-items-center">
|
||||
<i class="bi bi-calendar-plus-fill me-2"></i>
|
||||
Upcoming Services
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-info mb-3">
|
||||
<div class="card-header">Total Classifications</div>
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">@classificationCount</h4>
|
||||
<div class="card-body text-center">
|
||||
<h2 class="card-title mb-0">@upcomingServiceCount</h2>
|
||||
<small class="opacity-75">This Week</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Recent Schedules</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if (recentSchedules.Any())
|
||||
{
|
||||
<div class="list-group">
|
||||
@foreach (var schedule in recentSchedules.Take(5))
|
||||
{
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h6 class="mb-1">@($"{schedule.Member?.FullName}")</h6>
|
||||
<small class="badge @GetStatusBadgeClass(schedule.Status)">@schedule.Status</small>
|
||||
</div>
|
||||
<p class="mb-1">Service: @schedule.Service?.ServiceDate.ToString("MMM dd, yyyy")</p>
|
||||
<small>Scheduled: @schedule.ScheduledAt.ToString("MMM dd, yyyy HH:mm")</small>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-muted">No recent schedules found.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Quick Actions</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-grid gap-2">
|
||||
<a href="/schedules/create" class="btn btn-primary">Schedule Next Member</a>
|
||||
<a href="/members/create" class="btn btn-success">Add New Member</a>
|
||||
<a href="/services/create" class="btn btn-warning">Create New Service</a>
|
||||
<a href="/schedules" class="btn btn-info">View All Schedules</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-5">
|
||||
<div class="col-md-12">
|
||||
<div class="nimbus-quick-actions">
|
||||
<h5><i class="bi bi-lightning-charge me-2"></i>Quick Actions</h5>
|
||||
|
||||
<a href="/schedules/schedule-next" class="btn btn-nimbus-primary me-3 mb-2">
|
||||
<i class="bi bi-calendar-plus-fill me-2"></i>Schedule Next Member
|
||||
</a>
|
||||
|
||||
<a href="/members" class="btn btn-success me-3 mb-2">
|
||||
<i class="bi bi-person-plus-fill me-2"></i>Add New Member
|
||||
</a>
|
||||
|
||||
<a href="/services" class="btn btn-outline-warning me-3 mb-2">
|
||||
<i class="bi bi-gear-wide-connected me-2"></i>Create New Service
|
||||
</a>
|
||||
|
||||
<a href="/schedules" class="btn btn-outline-secondary me-3 mb-2">
|
||||
<i class="bi bi-list-check me-2"></i>View All Schedules
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -97,8 +78,6 @@
|
||||
private int activeMemberCount = 0;
|
||||
private int pendingScheduleCount = 0;
|
||||
private int upcomingServiceCount = 0;
|
||||
private int classificationCount = 0;
|
||||
private List<Schedule> recentSchedules = new();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
@@ -109,14 +88,11 @@
|
||||
activeMemberCount = members.Count(m => m.IsActive == 1);
|
||||
|
||||
var schedules = await ApiService.GetSchedulesAsync();
|
||||
recentSchedules = schedules.OrderByDescending(s => s.ScheduledAt).ToList();
|
||||
pendingScheduleCount = schedules.Count(s => s.Status == "pending");
|
||||
|
||||
var services = await ApiService.GetServicesAsync();
|
||||
upcomingServiceCount = services.Count(s => s.ServiceDate >= DateTime.Today);
|
||||
|
||||
var classifications = await ApiService.GetClassificationsAsync();
|
||||
classificationCount = classifications.Count;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -124,15 +100,4 @@
|
||||
Console.WriteLine($"Error loading dashboard data: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private string GetStatusBadgeClass(string status)
|
||||
{
|
||||
return status switch
|
||||
{
|
||||
"pending" => "bg-warning",
|
||||
"accepted" => "bg-success",
|
||||
"declined" => "bg-danger",
|
||||
_ => "bg-secondary"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,22 +8,37 @@
|
||||
<PageTitle>Members</PageTitle>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1>Members</h1>
|
||||
<a href="/members/create" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> Add Member
|
||||
<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">
|
||||
<div class="spinner-border" role="status">
|
||||
<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">
|
||||
@@ -50,18 +65,22 @@ else if (members.Any())
|
||||
<strong>@member.FullName</strong>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-secondary">@member.ClassificationName</span>
|
||||
<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 bg-success">Active</span>
|
||||
<span class="badge badge-nimbus-active">
|
||||
<i class="bi bi-check-circle-fill me-1"></i>Active
|
||||
</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge bg-danger">Inactive</span>
|
||||
<span class="badge badge-nimbus-inactive">
|
||||
<i class="bi bi-x-circle-fill me-1"></i>Inactive
|
||||
</span>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@@ -77,17 +96,23 @@ else if (members.Any())
|
||||
<td>
|
||||
@if (member.DeclineStreak > 0)
|
||||
{
|
||||
<span class="badge bg-warning">@member.DeclineStreak</span>
|
||||
<span class="badge badge-nimbus-pending">
|
||||
<i class="bi bi-exclamation-triangle-fill me-1"></i>@member.DeclineStreak
|
||||
</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="text-muted">0</span>
|
||||
<span class="text-muted">—</span>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group" role="group">
|
||||
<a href="/members/@member.MemberId" class="btn btn-sm btn-outline-primary">View</a>
|
||||
<a href="/members/@member.MemberId/edit" class="btn btn-sm btn-outline-warning">Edit</a>
|
||||
<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>
|
||||
@@ -101,19 +126,12 @@ else if (members.Any())
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-12">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<small class="text-muted">
|
||||
Showing @filteredMembers.Count() of @members.Count members
|
||||
</small>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" @bind="showInactiveMembers" id="showInactive">
|
||||
<label class="form-check-label" for="showInactive">
|
||||
Show inactive members
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<small class="text-muted">
|
||||
Showing @filteredMembers.Count() of @members.Count members
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -7,9 +7,11 @@
|
||||
<PageTitle>Schedules</PageTitle>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1>Schedules</h1>
|
||||
<a href="/schedules/create" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> Schedule Member
|
||||
<h1 class="nimbus-page-title">
|
||||
<i class="bi bi-calendar3 me-3"></i>Schedules
|
||||
</h1>
|
||||
<a href="/schedules/create" class="btn btn-nimbus-primary">
|
||||
<i class="bi bi-calendar-plus-fill me-2"></i>Schedule Member
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -69,7 +71,7 @@ else if (schedules.Any())
|
||||
@schedule.Service?.ServiceDate.ToString("MMM dd, yyyy")
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-info">@schedule.Service?.ServiceTypeName</span>
|
||||
<span class="badge" style="background-color: var(--nimbus-gold); color: var(--nimbus-navy);">@schedule.Service?.ServiceTypeName</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge @GetStatusBadgeClass(schedule.Status)">
|
||||
@@ -95,18 +97,21 @@ else if (schedules.Any())
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group" role="group">
|
||||
@if (schedule.Status == "pending")
|
||||
{
|
||||
<button class="btn btn-sm btn-success" @onclick="() => AcceptSchedule(schedule.ScheduleId)">
|
||||
Accept
|
||||
</button>
|
||||
<button class="btn btn-sm btn-warning" @onclick="() => ShowDeclineModal(schedule)">
|
||||
Decline
|
||||
</button>
|
||||
}
|
||||
<a href="/schedules/@schedule.ScheduleId" class="btn btn-sm btn-outline-primary">View</a>
|
||||
<button class="btn btn-sm btn-success me-1"
|
||||
disabled="@(schedule.Status != "pending")"
|
||||
@onclick="() => AcceptSchedule(schedule.ScheduleId)">
|
||||
<i class="bi bi-check-circle-fill me-1"></i>Accept
|
||||
</button>
|
||||
<button class="btn btn-sm btn-warning me-1"
|
||||
disabled="@(schedule.Status != "pending")"
|
||||
@onclick="() => ShowDeclineModal(schedule)">
|
||||
<i class="bi bi-x-circle-fill me-1"></i>Decline
|
||||
</button>
|
||||
<a href="/schedules/@schedule.ScheduleId" class="btn btn-sm btn-nimbus-secondary me-1">
|
||||
<i class="bi bi-eye-fill me-1"></i>View
|
||||
</a>
|
||||
<button class="btn btn-sm btn-outline-danger" @onclick="() => ConfirmRemove(schedule)">
|
||||
Remove
|
||||
<i class="bi bi-trash-fill me-1"></i>Remove
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
@@ -219,10 +224,10 @@ else
|
||||
{
|
||||
return status switch
|
||||
{
|
||||
"pending" => "bg-warning text-dark",
|
||||
"accepted" => "bg-success",
|
||||
"declined" => "bg-danger",
|
||||
_ => "bg-secondary"
|
||||
"pending" => "badge-nimbus-pending",
|
||||
"accepted" => "badge-nimbus-accepted",
|
||||
"declined" => "badge-nimbus-declined",
|
||||
_ => "badge-nimbus-inactive"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
<PageTitle>Services</PageTitle>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1>Services</h1>
|
||||
<a href="/services/create" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> Create Service
|
||||
<h1 class="nimbus-page-title">
|
||||
<i class="bi bi-gear-fill me-3"></i>Services
|
||||
</h1>
|
||||
<a href="/services/create" class="btn btn-nimbus-primary">
|
||||
<i class="bi bi-gear-wide-connected me-2"></i>Create Service
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -69,7 +71,7 @@ else if (services.Any())
|
||||
<strong>@service.ServiceDate.ToString("MMM dd, yyyy (dddd)")</strong>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge @GetServiceTypeBadgeClass(service.ServiceTypeName)">
|
||||
<span class="badge" style="background-color: var(--nimbus-gold); color: var(--nimbus-navy);">
|
||||
@service.ServiceTypeName
|
||||
</span>
|
||||
</td>
|
||||
@@ -115,11 +117,17 @@ else if (services.Any())
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group" role="group">
|
||||
<a href="/services/@service.ServiceId" class="btn btn-sm btn-outline-primary">View</a>
|
||||
<a href="/services/@service.ServiceId" class="btn btn-sm btn-nimbus-secondary me-1">
|
||||
<i class="bi bi-eye-fill me-1"></i>View
|
||||
</a>
|
||||
@if (service.ServiceDate >= DateTime.Today)
|
||||
{
|
||||
<a href="/schedules/create?serviceId=@service.ServiceId" class="btn btn-sm btn-success">Schedule</a>
|
||||
<a href="/services/@service.ServiceId/edit" class="btn btn-sm btn-outline-warning">Edit</a>
|
||||
<a href="/schedules/create?serviceId=@service.ServiceId" class="btn btn-sm btn-success me-1">
|
||||
<i class="bi bi-calendar-plus-fill me-1"></i>Schedule
|
||||
</a>
|
||||
<a href="/services/@service.ServiceId/edit" class="btn btn-sm btn-outline-warning">
|
||||
<i class="bi bi-pencil-fill me-1"></i>Edit
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</td>
|
||||
@@ -225,14 +233,4 @@ else
|
||||
}
|
||||
}
|
||||
|
||||
private string GetServiceTypeBadgeClass(string? serviceTypeName)
|
||||
{
|
||||
return serviceTypeName switch
|
||||
{
|
||||
"9AM" => "bg-info",
|
||||
"11AM" => "bg-primary",
|
||||
"6PM" => "bg-dark",
|
||||
_ => "bg-secondary"
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user