Files
PowderCoatingLogix/src/PowderCoating.Web/Views/Appointments/Details.cshtml
T
spouliot a0bdd2b5b4 Sweep all .cshtml files for encoding corruption; add pre-commit guard
Replace all corruption variants with HTML entities across 226 view files:
- 3-char UTF-8-as-Win1252 sequences (ae-corruption)
- Standalone smart/curly quotes that break C# Razor expressions
- Partially re-corrupted variants where the 3rd byte was normalised to ASCII

tools/Fix-Encoding.ps1: re-runnable sweep; uses [char] code points so the
script itself never contains a literal non-ASCII character; supports -DryRun

.githooks/pre-commit: blocks commits containing the ae-corruption byte
signature (xc3xa2xe2x82xac); git core.hooksPath = .githooks so the
hook is repo-committed and active for all future work on this machine.

Build clean; 225 unit tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 21:37:10 -04:00

324 lines
13 KiB
Plaintext

@model PowderCoating.Application.DTOs.Appointment.AppointmentDto
@{
ViewData["Title"] = $"Appointment {Model.AppointmentNumber}";
ViewData["PageIcon"] = "bi-calendar-event";
ViewData["PageHelpTitle"] = "Appointment Details";
ViewData["PageHelpContent"] = "View all details for this appointment. Edit to update status or record actual times. Deleting permanently removes the record &mdash; consider setting status to Cancelled instead to preserve history.";
}
<div class="d-flex justify-content-end gap-2 mb-4">
<a asp-action="Edit" asp-route-id="@Model.Id" class="btn btn-outline-primary">
<i class="bi bi-pencil"></i> Edit
</a>
<a asp-action="Index" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left"></i> Back to List
</a>
</div><div class="row g-4">
<!-- Left Column - Customer & Schedule Info -->
<div class="col-lg-6">
<!-- Customer Information -->
<div class="card mb-3">
<div class="card-header bg-primary text-white">
<h5 class="mb-0"><i class="bi bi-person-circle me-2"></i>Customer Information</h5>
</div>
<div class="card-body">
@if (Model.CustomerId.HasValue && !string.IsNullOrEmpty(Model.CustomerName))
{
<div class="row mb-3">
<div class="col-sm-4 text-muted">Customer:</div>
<div class="col-sm-8">
<strong>@Model.CustomerName</strong>
</div>
</div>
@if (!string.IsNullOrEmpty(Model.CustomerPhone))
{
<div class="row mb-3">
<div class="col-sm-4 text-muted">Phone:</div>
<div class="col-sm-8">
<a href="tel:@Model.CustomerPhone">@Model.CustomerPhone</a>
</div>
</div>
}
@if (!string.IsNullOrEmpty(Model.CustomerEmail))
{
<div class="row">
<div class="col-sm-4 text-muted">Email:</div>
<div class="col-sm-8">
<a href="mailto:@Model.CustomerEmail">@Model.CustomerEmail</a>
</div>
</div>
}
}
else
{
<div class="alert alert-info alert-permanent mb-0">
<i class="bi bi-info-circle me-2"></i>
<strong>Internal Appointment</strong><br />
<small>This appointment is not associated with a customer.</small>
</div>
}
</div>
</div>
<!-- Schedule Information -->
<div class="card mb-3">
<div class="card-header bg-success text-white">
<h5 class="mb-0"><i class="bi bi-calendar-event me-2"></i>Schedule</h5>
</div>
<div class="card-body">
<div class="row mb-3">
<div class="col-sm-4 text-muted">Date:</div>
<div class="col-sm-8">
<strong>@Model.ScheduledStartTime.ToString("dddd, MMMM dd, yyyy")</strong>
</div>
</div>
@if (Model.IsAllDay)
{
<div class="row mb-3">
<div class="col-sm-4 text-muted">Time:</div>
<div class="col-sm-8">
<span class="badge bg-secondary">All Day</span>
</div>
</div>
}
else
{
<div class="row mb-3">
<div class="col-sm-4 text-muted">Start Time:</div>
<div class="col-sm-8">@Model.ScheduledStartTime.ToString("h:mm tt")</div>
</div>
<div class="row mb-3">
<div class="col-sm-4 text-muted">End Time:</div>
<div class="col-sm-8">@Model.ScheduledEndTime.ToString("h:mm tt")</div>
</div>
<div class="row">
<div class="col-sm-4 text-muted">Duration:</div>
<div class="col-sm-8">
@{
var duration = Model.ScheduledEndTime - Model.ScheduledStartTime;
var hours = (int)duration.TotalHours;
var minutes = duration.Minutes;
}
@if (hours > 0)
{
<span>@hours hour@(hours != 1 ? "s" : "")</span>
}
@if (minutes > 0)
{
<span>@minutes minute@(minutes != 1 ? "s" : "")</span>
}
</div>
</div>
}
@if (!string.IsNullOrEmpty(Model.Location))
{
<hr />
<div class="row">
<div class="col-sm-4 text-muted">Location:</div>
<div class="col-sm-8">
<i class="bi bi-geo-alt"></i> @Model.Location
</div>
</div>
}
</div>
</div>
<!-- Actual Times (if recorded) -->
@if (Model.ActualStartTime.HasValue || Model.ActualEndTime.HasValue)
{
<div class="card">
<div class="card-header bg-info text-white">
<h5 class="mb-0"><i class="bi bi-clock-history me-2"></i>Actual Times</h5>
</div>
<div class="card-body">
@if (Model.ActualStartTime.HasValue)
{
<div class="row mb-3">
<div class="col-sm-4 text-muted">Arrived:</div>
<div class="col-sm-8">@Model.ActualStartTime.Value.ToString("h:mm tt")</div>
</div>
}
@if (Model.ActualEndTime.HasValue)
{
<div class="row">
<div class="col-sm-4 text-muted">Completed:</div>
<div class="col-sm-8">@Model.ActualEndTime.Value.ToString("h:mm tt")</div>
</div>
}
</div>
</div>
}
</div>
<!-- Right Column - Appointment Details -->
<div class="col-lg-6">
<!-- Appointment Information -->
<div class="card mb-3">
<div class="card-header bg-secondary text-white">
<h5 class="mb-0"><i class="bi bi-info-circle me-2"></i>Appointment Details</h5>
</div>
<div class="card-body">
<div class="row mb-3">
<div class="col-sm-4 text-muted">Type:</div>
<div class="col-sm-8">
<span class="badge bg-@Model.TypeColorClass">
@if (!string.IsNullOrEmpty(Model.TypeIconClass))
{
<i class="@Model.TypeIconClass me-1"></i>
}
@Model.TypeDisplayName
</span>
</div>
</div>
<div class="row mb-3">
<div class="col-sm-4 text-muted">Status:</div>
<div class="col-sm-8">
<span class="badge bg-@Model.StatusColorClass">
@if (!string.IsNullOrEmpty(Model.StatusIconClass))
{
<i class="@Model.StatusIconClass me-1"></i>
}
@Model.StatusDisplayName
</span>
</div>
</div>
@if (Model.JobId.HasValue)
{
<div class="row mb-3">
<div class="col-sm-4 text-muted">Linked Job:</div>
<div class="col-sm-8">
<a asp-controller="Jobs" asp-action="Details" asp-route-id="@Model.JobId">
@Model.JobNumber
</a>
</div>
</div>
}
@if (!string.IsNullOrEmpty(Model.AssignedWorkerName))
{
<div class="row">
<div class="col-sm-4 text-muted">Assigned To:</div>
<div class="col-sm-8">
<span class="badge bg-info">
<i class="bi bi-person"></i> @Model.AssignedWorkerName
</span>
</div>
</div>
}
</div>
</div>
<!-- Description & Notes -->
@if (!string.IsNullOrEmpty(Model.Description) || !string.IsNullOrEmpty(Model.Notes))
{
<div class="card mb-3">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-file-text me-2"></i>Description & Notes</h5>
</div>
<div class="card-body">
@if (!string.IsNullOrEmpty(Model.Description))
{
<div class="mb-3">
<strong>Description:</strong>
<p class="mb-0 mt-1">@Model.Description</p>
</div>
}
@if (!string.IsNullOrEmpty(Model.Notes))
{
<div>
<strong>Internal Notes:</strong>
<p class="mb-0 mt-1 text-muted">@Model.Notes</p>
</div>
}
</div>
</div>
}
<!-- Reminder Settings -->
@if (Model.IsReminderEnabled)
{
<div class="card mb-3">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-bell me-2"></i>Reminder</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-sm-4 text-muted">Reminder:</div>
<div class="col-sm-8">
<i class="bi bi-check-circle text-success"></i>
@Model.ReminderMinutesBefore minutes before
</div>
</div>
</div>
</div>
}
<!-- Audit Information -->
<div class="card">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-clock-history me-2"></i>History</h5>
</div>
<div class="card-body">
<div class="row mb-2">
<div class="col-sm-4 text-muted">Created:</div>
<div class="col-sm-8">
@Model.CreatedAt.ToString("MMM dd, yyyy h:mm tt")
@if (!string.IsNullOrEmpty(Model.CreatedBy))
{
<br /><small class="text-muted">by @Model.CreatedBy</small>
}
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Actions -->
<div class="card mt-4">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<a asp-action="Edit" asp-route-id="@Model.Id" class="btn btn-primary">
<i class="bi bi-pencil"></i> Edit Appointment
</a>
<a asp-action="Calendar" class="btn btn-outline-secondary">
<i class="bi bi-calendar3"></i> View Calendar
</a>
</div>
<div>
<button type="button" class="btn btn-outline-danger" onclick="deleteAppointment(@Model.Id, '@Model.AppointmentNumber')">
<i class="bi bi-trash"></i> Delete
</button>
</div>
</div>
</div>
</div>
@section Scripts {
<script>
function deleteAppointment(id, appointmentNumber) {
if (confirm(`Are you sure you want to delete appointment ${appointmentNumber}?\n\nThis action cannot be undone.`)) {
const form = document.createElement('form');
form.method = 'POST';
form.action = '@Url.Action("Delete")';
const idInput = document.createElement('input');
idInput.type = 'hidden';
idInput.name = 'id';
idInput.value = id;
form.appendChild(idInput);
const tokenInput = document.createElement('input');
tokenInput.type = 'hidden';
tokenInput.name = '__RequestVerificationToken';
tokenInput.value = '@Html.AntiForgeryToken()'.match(/value="([^"]+)"/)[1];
form.appendChild(tokenInput);
document.body.appendChild(form);
form.submit();
}
}
</script>
}