Initial commit

This commit is contained in:
2026-04-23 21:38:24 -04:00
commit 63e12a9636
1762 changed files with 1672620 additions and 0 deletions
@@ -0,0 +1,181 @@
@model PowderCoating.Application.DTOs.Common.PagedResult<PowderCoating.Application.DTOs.Job.JobListDto>
@using PowderCoating.Core.Enums
@{
ViewData["Title"] = $"Job History - {ViewBag.CustomerName}";
ViewData["PageIcon"] = "bi-briefcase";
}
<div class="d-flex justify-content-end gap-2 mb-4">
<a asp-action="Create" asp-controller="Jobs" asp-route-customerId="@ViewBag.CustomerId" class="btn btn-primary">
<i class="bi bi-plus-circle me-2"></i>New Job
</a>
<a asp-action="Details" asp-route-id="@ViewBag.CustomerId" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left me-2"></i>Back to Customer
</a>
</div>
@if (!string.IsNullOrEmpty(ViewBag.SearchTerm))
{
<div class="alert alert-info alert-permanent d-flex justify-content-between align-items-center">
<div>
<i class="bi bi-funnel me-2"></i>
Showing <strong>@Model.TotalCount</strong> job(s) matching "<strong>@ViewBag.SearchTerm</strong>"
</div>
<a href="@Url.Action("JobHistory", new { id = ViewBag.CustomerId })" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-x me-1"></i>Clear Filter
</a>
</div>
}
<div class="card border-0 shadow-sm">
<div class="card-header bg-white border-0 py-3">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0 fw-semibold">
@Model.TotalCount job(s) total
</h5>
<form asp-action="JobHistory" asp-route-id="@ViewBag.CustomerId" method="get" class="d-flex gap-2">
<div class="input-group" style="width: 350px;">
<span class="input-group-text bg-white border-end-0">
<i class="bi bi-search text-muted"></i>
</span>
<input type="text" name="searchTerm" class="form-control border-start-0"
placeholder="Search by job #, description, PO, status..."
value="@ViewBag.SearchTerm"
aria-label="Search jobs">
@if (!string.IsNullOrEmpty(ViewBag.SearchTerm))
{
<a href="@Url.Action("JobHistory", new { id = ViewBag.CustomerId })" class="btn btn-outline-secondary" title="Clear search">
<i class="bi bi-x-lg"></i>
</a>
}
<button type="submit" class="btn btn-primary">
<i class="bi bi-search"></i>
</button>
</div>
</form>
</div>
</div>
<div class="card-body p-0">
@if (!Model.Items.Any())
{
<div class="text-center py-5">
<i class="bi bi-inbox" style="font-size: 4rem; color: #d1d5db;"></i>
<h5 class="mt-3 text-muted">No jobs found</h5>
@if (!string.IsNullOrEmpty(ViewBag.SearchTerm))
{
<p class="text-muted mb-4">No jobs match your search criteria</p>
}
else
{
<p class="text-muted mb-4">This customer has no jobs yet</p>
<a asp-action="Create" asp-controller="Jobs" asp-route-customerId="@ViewBag.CustomerId" class="btn btn-primary">
<i class="bi bi-plus-circle me-2"></i>Create First Job
</a>
}
</div>
}
else
{
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead>
<tr>
<th sortable="JobNumber" current-sort="@ViewBag.SortColumn" current-direction="@ViewBag.SortDirection" class="ps-4">Job Number</th>
<th>Description</th>
<th sortable="Status" current-sort="@ViewBag.SortColumn" current-direction="@ViewBag.SortDirection">Status</th>
<th sortable="Priority" current-sort="@ViewBag.SortColumn" current-direction="@ViewBag.SortDirection">Priority</th>
<th sortable="ScheduledDate" current-sort="@ViewBag.SortColumn" current-direction="@ViewBag.SortDirection">Scheduled</th>
<th sortable="DueDate" current-sort="@ViewBag.SortColumn" current-direction="@ViewBag.SortDirection">Due Date</th>
<th sortable="FinalPrice" current-sort="@ViewBag.SortColumn" current-direction="@ViewBag.SortDirection">Price</th>
<th class="text-end pe-4">Actions</th>
</tr>
</thead>
<tbody>
@foreach (var job in Model.Items)
{
<tr style="cursor: pointer;" onclick="window.location.href='@Url.Action("Details", "Jobs", new { id = job.Id })'">
<td class="ps-4">
<div class="d-flex align-items-center gap-2">
<div class="rounded-circle d-flex align-items-center justify-content-center"
style="width: 40px; height: 40px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; font-weight: 600;">
<i class="bi bi-briefcase"></i>
</div>
<div>
<div class="fw-semibold">@job.JobNumber</div>
<small class="text-muted">Created @job.CreatedAt.ToString("MMM dd, yyyy")</small>
</div>
</div>
</td>
<td>@job.Description</td>
<td>
<span class="badge bg-@job.StatusColorClass bg-opacity-10 text-@job.StatusColorClass">
@job.StatusDisplayName
</span>
</td>
<td>
<span class="badge bg-@job.PriorityColorClass">@job.PriorityDisplayName</span>
</td>
<td>
@if (job.ScheduledDate.HasValue)
{
<span>@job.ScheduledDate.Value.ToString("MMM dd, yyyy")</span>
}
else
{
<span class="text-muted">Not scheduled</span>
}
</td>
<td>
@if (job.DueDate.HasValue)
{
var isOverdue = job.DueDate.Value < DateTime.Now && job.StatusCode != "COMPLETED" && job.StatusCode != "READYFORPICKUP" && job.StatusCode != "DELIVERED";
<span class="@(isOverdue ? "text-danger fw-semibold" : "")">
@job.DueDate.Value.ToString("MMM dd, yyyy")
@if (isOverdue)
{
<i class="bi bi-exclamation-triangle ms-1"></i>
}
</span>
}
else
{
<span class="text-muted">Not set</span>
}
</td>
<td>
<span class="fw-semibold">@job.FinalPrice.ToString("C")</span>
</td>
<td class="text-end pe-4" onclick="event.stopPropagation()">
<div class="btn-group btn-group-sm">
<a asp-controller="Jobs" asp-action="Details" asp-route-id="@job.Id" class="btn btn-outline-primary" title="View Details">
<i class="bi bi-eye"></i>
</a>
<a asp-controller="Jobs" asp-action="Edit" asp-route-id="@job.Id" class="btn btn-outline-warning" title="Edit">
<i class="bi bi-pencil"></i>
</a>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
}
</div>
@if (Model.TotalCount > 0)
{
@await Html.PartialAsync("_Pagination", Model)
}
</div>
@section Scripts {
<script>
function changePageSize(size) {
const url = new URL(window.location.href);
url.searchParams.set('pageSize', size);
url.searchParams.set('pageNumber', '1');
window.location.href = url.toString();
}
</script>
}