Files
PowderCoatingLogix/src/PowderCoating.Web/Views/Customers/Details.cshtml
T
spouliot a9048dea2e Show email and SMS notification status on customer list and details
Added notification preference indicators to both views so staff can
see at a glance whether a customer has email/SMS enabled without
opening edit mode.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-02 20:27:28 -04:00

517 lines
28 KiB
Plaintext

@model PowderCoating.Application.DTOs.Customer.CustomerDto
@{
ViewData["Title"] = !string.IsNullOrWhiteSpace(Model.CompanyName)
? Model.CompanyName
: $"{Model.ContactFirstName} {Model.ContactLastName}".Trim();
ViewData["PageIcon"] = "bi-person-circle";
}
<div class="row justify-content-center">
<div class="col-lg-10">
<div class="d-flex justify-content-end gap-2 mb-4">
<div class="d-flex gap-2">
<a asp-action="Activity" asp-route-id="@Model.Id" class="btn btn-outline-info">
<i class="bi bi-clock-history me-2"></i>View Activity
</a>
<a asp-action="Edit" asp-route-id="@Model.Id" class="btn btn-warning">
<i class="bi bi-pencil me-2"></i>Edit
</a>
<a asp-action="Index" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left me-2"></i>Back to List
</a>
</div>
</div>
<!-- Status Banner -->
<div class="alert @(Model.IsActive ? "alert-success" : "alert-danger") alert-permanent d-flex align-items-center mb-4">
<i class="bi @(Model.IsActive ? "bi-check-circle" : "bi-x-circle") me-2" style="font-size: 1.5rem;"></i>
<div>
<strong>Status:</strong> @(Model.IsActive ? "Active Customer" : "Inactive Customer")
@if (!Model.IsActive)
{
<span class="ms-2">- This customer is currently inactive</span>
}
</div>
</div>
<div class="row g-4">
<!-- Left Column -->
<div class="col-lg-8">
<!-- Company Information -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-white border-0 py-3">
<h5 class="mb-0 fw-semibold">
<i class="bi bi-building me-2 text-primary"></i>Company Information
</h5>
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-8">
<label class="text-muted small mb-1">Company Name</label>
<p class="fw-semibold mb-0">@Model.CompanyName</p>
</div>
<div class="col-md-4">
<label class="text-muted small mb-1">Customer Type</label>
<p class="mb-0">
@if (Model.IsCommercial)
{
<span class="badge bg-primary bg-opacity-10 text-primary">
<i class="bi bi-building me-1"></i>Commercial
</span>
}
else
{
<span class="badge bg-secondary bg-opacity-10 text-secondary">
<i class="bi bi-person me-1"></i>Individual
</span>
}
</p>
</div>
</div>
</div>
</div>
<!-- Contact Information -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-white border-0 py-3">
<h5 class="mb-0 fw-semibold">
<i class="bi bi-person me-2 text-primary"></i>Contact Information
</h5>
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-6">
<label class="text-muted small mb-1">Contact Name</label>
<p class="mb-0">
@if (!string.IsNullOrEmpty(Model.ContactFirstName) || !string.IsNullOrEmpty(Model.ContactLastName))
{
<span>@Model.ContactFirstName @Model.ContactLastName</span>
}
else
{
<span class="text-muted">Not provided</span>
}
</p>
</div>
<div class="col-md-6">
<label class="text-muted small mb-1">Email</label>
<p class="mb-0">
<a href="mailto:@Model.Email" class="text-decoration-none">
<i class="bi bi-envelope me-1"></i>@Model.Email
</a>
</p>
</div>
<div class="col-md-6">
<label class="text-muted small mb-1">Phone</label>
<p class="mb-0">
@if (!string.IsNullOrEmpty(Model.Phone))
{
<a href="tel:@Model.Phone" class="text-decoration-none">
<i class="bi bi-telephone me-1"></i>@Model.Phone
</a>
}
else
{
<span class="text-muted">Not provided</span>
}
</p>
</div>
<div class="col-md-6">
<label class="text-muted small mb-1">Mobile Phone</label>
<p class="mb-0">
@if (!string.IsNullOrEmpty(Model.MobilePhone))
{
<a href="tel:@Model.MobilePhone" class="text-decoration-none">
<i class="bi bi-phone me-1"></i>@Model.MobilePhone
</a>
}
else
{
<span class="text-muted">Not provided</span>
}
</p>
</div>
<div class="col-12">
<label class="text-muted small mb-1">Notifications</label>
<div class="d-flex gap-2">
@if (Model.NotifyByEmail)
{
<span class="badge bg-success bg-opacity-10 text-success border border-success border-opacity-25">
<i class="bi bi-envelope-fill me-1"></i>Email on
</span>
}
else
{
<span class="badge bg-secondary bg-opacity-10 text-secondary border border-secondary border-opacity-25">
<i class="bi bi-envelope-slash me-1"></i>Email off
</span>
}
@if (Model.NotifyBySms)
{
<span class="badge bg-success bg-opacity-10 text-success border border-success border-opacity-25">
<i class="bi bi-chat-fill me-1"></i>SMS on
</span>
}
else
{
<span class="badge bg-secondary bg-opacity-10 text-secondary border border-secondary border-opacity-25">
<i class="bi bi-chat-slash me-1"></i>SMS off
</span>
}
</div>
</div>
</div>
</div>
</div>
<!-- Address Information -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-white border-0 py-3">
<h5 class="mb-0 fw-semibold">
<i class="bi bi-geo-alt me-2 text-primary"></i>Address
</h5>
</div>
<div class="card-body">
@if (!string.IsNullOrEmpty(Model.Address))
{
<p class="mb-2">@Model.Address</p>
<p class="mb-0">
@if (!string.IsNullOrEmpty(Model.City))
{
<span>@Model.City</span>
}
@if (!string.IsNullOrEmpty(Model.State))
{
<span>, @Model.State</span>
}
@if (!string.IsNullOrEmpty(Model.ZipCode))
{
<span> @Model.ZipCode</span>
}
</p>
@if (!string.IsNullOrEmpty(Model.Country))
{
<p class="mb-0 text-muted">@Model.Country</p>
}
}
else
{
<p class="text-muted mb-0">No address provided</p>
}
</div>
</div>
<!-- Business Information -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-white border-0 py-3">
<h5 class="mb-0 fw-semibold">
<i class="bi bi-briefcase me-2 text-primary"></i>Business Information
</h5>
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-6">
<label class="text-muted small mb-1">Tax ID / EIN</label>
<p class="mb-0">@(Model.TaxId ?? "Not provided")</p>
</div>
<div class="col-md-6">
<label class="text-muted small mb-1">Payment Terms</label>
<p class="mb-0">@(Model.PaymentTerms ?? "Not set")</p>
</div>
<div class="col-md-6">
<label class="text-muted small mb-1">Credit Limit</label>
<p class="mb-0 fw-semibold">@Model.CreditLimit.ToString("C")</p>
</div>
<div class="col-md-6">
<label class="text-muted small mb-1">Pricing Tier</label>
<p class="mb-0">@(Model.PricingTierName ?? "Standard")</p>
</div>
<div class="col-md-6">
<label class="text-muted small mb-1">Tax Status</label>
<p class="mb-0">
@if (Model.IsTaxExempt)
{
<span class="badge bg-success">
<i class="bi bi-check-circle"></i> Tax Exempt
</span>
}
else
{
<span class="badge bg-secondary">Taxable</span>
}
</p>
</div>
@if (Model.HasTaxExemptCertificate)
{
<div class="col-md-12">
<label class="text-muted small mb-1">Tax Exempt Certificate</label>
<div class="alert alert-success d-flex justify-content-between align-items-center mb-0 mt-2">
<div>
<i class="bi bi-file-earmark-check me-2"></i>
<strong>File on record:</strong> @Model.TaxExemptCertificateFileName
</div>
<a asp-action="TaxExemptCertificate" asp-route-id="@Model.Id" class="btn btn-sm btn-outline-dark" target="_blank">
<i class="bi bi-download"></i> Download
</a>
</div>
</div>
}
@if (Model.IsTaxExempt && !Model.HasTaxExemptCertificate)
{
<div class="col-md-12">
<label class="text-muted small mb-1">Tax Exempt Certificate</label>
<div class="alert alert-warning mb-0 mt-2">
<i class="bi bi-exclamation-triangle me-2"></i>
<strong>No certificate on file.</strong> Please upload a tax exempt certificate to complete the record.
</div>
</div>
}
</div>
</div>
</div>
<!-- Notes -->
@if (!string.IsNullOrEmpty(Model.GeneralNotes))
{
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-white border-0 py-3">
<h5 class="mb-0 fw-semibold">
<i class="bi bi-journal-text me-2 text-primary"></i>Notes
</h5>
</div>
<div class="card-body">
<p class="mb-0" style="white-space: pre-wrap;">@Model.GeneralNotes</p>
</div>
</div>
}
</div>
<!-- Right Column - Statistics -->
<div class="col-lg-4">
<!-- Financial Summary -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-white border-0 py-3 d-flex justify-content-between align-items-center">
<h5 class="mb-0 fw-semibold">
<i class="bi bi-currency-dollar me-2 text-primary"></i>Financial Summary
</h5>
</div>
<div class="card-body">
<div class="mb-3">
<label class="text-muted small mb-1">Outstanding Balance</label>
<h3 class="mb-0 @(Model.CurrentBalance > 0 ? "text-danger" : "text-success")">
@Model.CurrentBalance.ToString("C")
</h3>
</div>
@if (Model.CreditLimit > 0)
{
<div class="mb-3">
<label class="text-muted small mb-1">Credit Limit</label>
<p class="mb-0 fw-semibold">@Model.CreditLimit.ToString("C")</p>
</div>
<div class="mb-3">
<label class="text-muted small mb-1">Available Credit</label>
<p class="mb-0 fw-semibold text-success">
@((Model.CreditLimit - Model.CurrentBalance).ToString("C"))
</p>
</div>
}
<hr class="my-2" />
<div class="d-flex justify-content-between align-items-center">
<div>
<label class="text-muted small mb-1">Store Credit Balance</label>
<h4 class="mb-0 @(Model.CreditBalance > 0 ? "text-success fw-bold" : "text-muted")">
@Model.CreditBalance.ToString("C")
</h4>
<small class="text-muted">Available for future invoices</small>
</div>
@if (User.IsInRole("SuperAdmin") || User.IsInRole("Administrator"))
{
<button type="button" class="btn btn-sm btn-success" data-bs-toggle="modal" data-bs-target="#addCreditModal">
<i class="bi bi-plus-circle me-1"></i>Add Credit
</button>
}
</div>
</div>
</div>
<!-- Store Credit History -->
@{
var creditMemos = ViewBag.CreditMemos as List<PowderCoating.Core.Entities.CreditMemo>;
}
@if (creditMemos != null && creditMemos.Count > 0)
{
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-white border-0 py-3">
<h5 class="mb-0 fw-semibold">
<i class="bi bi-credit-card me-2 text-primary"></i>Store Credit History
</h5>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-sm table-hover mb-0">
<thead class="table-light">
<tr>
<th class="ps-3">Memo #</th>
<th>Issued</th>
<th class="text-end">Amount</th>
<th class="text-end pe-3">Remaining</th>
</tr>
</thead>
<tbody>
@foreach (var memo in creditMemos)
{
<tr>
<td class="ps-3">
<span class="fw-semibold small">@memo.MemoNumber</span>
<div class="text-muted" style="font-size:0.75rem;">@memo.Reason</div>
</td>
<td class="small text-muted align-middle">@memo.IssueDate.Tz(ViewBag.CompanyTimeZone as string).ToString("MM/dd/yy")</td>
<td class="text-end align-middle small">@memo.Amount.ToString("C")</td>
<td class="text-end pe-3 align-middle">
@if (memo.RemainingBalance > 0)
{
<span class="badge bg-success">@memo.RemainingBalance.ToString("C")</span>
}
else
{
<span class="badge bg-secondary">Used</span>
}
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
}
<!-- Activity -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-white border-0 py-3">
<h5 class="mb-0 fw-semibold">
<i class="bi bi-clock-history me-2 text-primary"></i>Activity
</h5>
</div>
<div class="card-body">
<div class="mb-3">
<label class="text-muted small mb-1">Last Contact</label>
<p class="mb-0">
@if (Model.LastContactDate.HasValue)
{
<span>@Model.LastContactDate.Value.ToString("MMMM dd, yyyy")</span>
}
else
{
<span class="text-muted">No contact recorded</span>
}
</p>
</div>
<div>
<label class="text-muted small mb-1">Customer Since</label>
<p class="mb-0">@Model.CreatedAt.ToString("MMMM dd, yyyy")</p>
</div>
</div>
</div>
<!-- Quick Actions -->
<div class="card border-0 shadow-sm">
<div class="card-header bg-white border-0 py-3">
<h5 class="mb-0 fw-semibold">
<i class="bi bi-lightning me-2 text-primary"></i>Quick Actions
</h5>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<a asp-action="Edit" asp-route-id="@Model.Id" class="btn btn-outline-primary">
<i class="bi bi-pencil me-2"></i>Edit Customer
</a>
<a asp-action="JobHistory" asp-route-id="@Model.Id" class="btn btn-outline-success">
<i class="bi bi-clock-history me-2"></i>Job History
</a>
<a asp-action="Invoices" asp-route-id="@Model.Id" class="btn btn-outline-warning">
<i class="bi bi-receipt me-2"></i>View Invoices
</a>
<a asp-controller="Jobs" asp-action="Create" asp-route-customerId="@Model.Id" class="btn btn-outline-success">
<i class="bi bi-plus-circle me-2"></i>New Job
</a>
<a asp-controller="Quotes" asp-action="Create" asp-route-customerId="@Model.Id" class="btn btn-outline-info">
<i class="bi bi-file-text me-2"></i>New Quote
</a>
@if (User.IsInRole("SuperAdmin") || User.IsInRole("Administrator"))
{
<button type="button" class="btn btn-outline-success" data-bs-toggle="modal" data-bs-target="#addCreditModal">
<i class="bi bi-wallet2 me-2"></i>Add Store Credit
</button>
}
<a asp-action="Delete" asp-route-id="@Model.Id" class="btn btn-outline-danger">
<i class="bi bi-trash me-2"></i>Delete Customer
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Add Store Credit Modal -->
@if (User.IsInRole("SuperAdmin") || User.IsInRole("Administrator"))
{
<div class="modal fade" id="addCreditModal" tabindex="-1" aria-labelledby="addCreditModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<form asp-action="AddCredit" asp-route-id="@Model.Id" method="post">
@Html.AntiForgeryToken()
<div class="modal-header">
<h5 class="modal-title" id="addCreditModalLabel">
<i class="bi bi-wallet2 me-2 text-success"></i>Add Store Credit
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p class="text-muted small mb-3">
Credits can be applied to any future invoice for this customer.
Current balance: <strong class="text-success">@Model.CreditBalance.ToString("C")</strong>
</p>
<div class="mb-3">
<label class="form-label fw-semibold">Amount <span class="text-danger">*</span></label>
<div class="input-group">
<span class="input-group-text">$</span>
<input type="number" name="Amount" class="form-control" step="0.01" min="0.01" max="99999.99" required placeholder="0.00" />
</div>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Reason <span class="text-danger">*</span></label>
<select name="Reason" class="form-select" required id="creditReasonSelect">
<option value="">— Select reason —</option>
<option value="Pre-payment / Deposit">Pre-payment / Deposit</option>
<option value="Gift / Gift Card">Gift / Gift Card</option>
<option value="Overpayment credit">Overpayment credit</option>
<option value="Goodwill credit">Goodwill credit</option>
<option value="Other">Other</option>
</select>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Notes</label>
<textarea name="Notes" class="form-control" rows="2" maxlength="1000" placeholder="Optional details..."></textarea>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Expiry Date <span class="text-muted fw-normal">(optional)</span></label>
<input type="date" name="ExpiryDate" class="form-control" />
<div class="form-text">Leave blank for no expiry.</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-success">
<i class="bi bi-plus-circle me-1"></i>Add Credit
</button>
</div>
</form>
</div>
</div>
</div>
}