Hide email controls when no email on file; show SMS hint for quote/job events

- Quotes Create/Edit: hide 'Send via email' checkbox when customer has no
  email; show badge 'send via SMS from details' or 'SMS consent required'
  when customer has a mobile number. JS responds to customer dropdown change.
- Quotes Details: hide 'Send Quote via Email' button and approval email
  checkbox; hide SMS button when no mobile; show consent-required note.
- Jobs Details (Mark Complete modal): hide email checkbox; show
  'SMS notification will be sent' badge or consent-required note.
- Jobs Index (status modal): hide email row when customer has no email.
- Jobs Edit: hide 'Notify customer if status changes' when no email.
- Invoices Details: hide Send/Re-send buttons when no email (vs. disabled).

DTOs: added CustomerEmail + CustomerNotifyByEmail to JobDto/JobListDto;
added CustomerNotifyByEmail/CustomerMobilePhone/CustomerNotifyBySms to
QuoteDto. Mapped in JobProfile and QuotesController customer blocks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 17:32:08 -04:00
parent d3863c713b
commit acbd9f60be
1190 changed files with 338016 additions and 88 deletions
@@ -1840,13 +1840,28 @@
}
</div>
<div class="modal-footer justify-content-between">
<div class="form-check form-switch mb-0">
<input class="form-check-input" type="checkbox" role="switch"
id="completeJobSendEmail" name="SendEmailToCustomer" value="true"
@(ViewBag.EmailDefaultOnComplete == true ? "checked" : "") />
<label class="form-check-label small" for="completeJobSendEmail">
<i class="bi bi-envelope me-1"></i>Notify customer via email
</label>
<div class="d-flex align-items-center gap-2 flex-wrap">
@if (!string.IsNullOrWhiteSpace(Model.CustomerEmail))
{
<div class="form-check form-switch mb-0">
<input class="form-check-input" type="checkbox" role="switch"
id="completeJobSendEmail" name="SendEmailToCustomer" value="true"
@(ViewBag.EmailDefaultOnComplete == true ? "checked" : "") />
<label class="form-check-label small" for="completeJobSendEmail">
<i class="bi bi-envelope me-1"></i>Notify customer via email
</label>
</div>
}
@if (Model.CustomerNotifyBySms && !string.IsNullOrWhiteSpace(Model.CustomerMobilePhone))
{
<span class="badge bg-info text-white">
<i class="bi bi-phone me-1"></i>SMS notification will be sent
</span>
}
else if (!string.IsNullOrWhiteSpace(Model.CustomerMobilePhone) && !Model.CustomerNotifyBySms)
{
<span class="text-muted small"><i class="bi bi-phone-slash me-1"></i>SMS consent required</span>
}
</div>
<div>
<button type="button" class="btn btn-secondary me-2" data-bs-dismiss="modal">Cancel</button>
+11 -8
View File
@@ -274,14 +274,17 @@
@{
var sendEmailDefault = (bool)(ViewBag.EmailDefaultOnStatusChange ?? Model.SendEmailOnStatusChange);
}
<div class="form-check form-switch mb-0">
<input class="form-check-input" type="checkbox" role="switch"
id="SendEmailOnStatusChange" name="SendEmailOnStatusChange" value="true"
checked="@(sendEmailDefault ? "checked" : null)" />
<label class="form-check-label small" for="SendEmailOnStatusChange">
<i class="bi bi-envelope me-1"></i>Notify customer if status changes
</label>
</div>
@if (!string.IsNullOrWhiteSpace(ViewBag.CustomerEmail as string))
{
<div class="form-check form-switch mb-0">
<input class="form-check-input" type="checkbox" role="switch"
id="SendEmailOnStatusChange" name="SendEmailOnStatusChange" value="true"
checked="@(sendEmailDefault ? "checked" : null)" />
<label class="form-check-label small" for="SendEmailOnStatusChange">
<i class="bi bi-envelope me-1"></i>Notify customer if status changes
</label>
</div>
}
<div class="d-flex gap-2">
<a asp-action="Details" asp-route-id="@Model.Id" class="btn btn-outline-secondary btn-lg">
<i class="bi bi-x-circle me-1"></i>Cancel
+20 -12
View File
@@ -196,6 +196,7 @@
data-status-id="@job.JobStatusId"
data-status-name="@job.StatusDisplayName"
data-customer-notify="@job.CustomerNotifyByEmail.ToString().ToLower()"
data-customer-email="@(string.IsNullOrWhiteSpace(job.CustomerEmail) ? "false" : "true")"
title="Click to change status">
<span class="pcl-chip-dot"></span>@job.StatusDisplayName
</span>
@@ -504,16 +505,18 @@
<option value="">Loading statuses...</option>
</select>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch"
id="statusModalSendEmail"
@(ViewBag.EmailDefaultOnStatusChange == true ? "checked" : "") />
<label class="form-check-label small" for="statusModalSendEmail">
<i class="bi bi-envelope me-1"></i>Notify customer via email
</label>
</div>
<div id="statusModalEmailOptOutNote" class="alert alert-warning alert-permanent py-1 px-2 mt-2 small" style="display:none;">
<i class="bi bi-bell-slash me-1"></i>This customer has email notifications turned off.
<div id="statusModalEmailRow">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch"
id="statusModalSendEmail"
@(ViewBag.EmailDefaultOnStatusChange == true ? "checked" : "") />
<label class="form-check-label small" for="statusModalSendEmail">
<i class="bi bi-envelope me-1"></i>Notify customer via email
</label>
</div>
<div id="statusModalEmailOptOutNote" class="alert alert-warning alert-permanent py-1 px-2 mt-2 small" style="display:none;">
<i class="bi bi-bell-slash me-1"></i>This customer has email notifications turned off.
</div>
</div>
</div>
<div class="modal-footer">
@@ -750,20 +753,25 @@
const jobNumber = this.getAttribute('data-job-number');
const statusName = this.getAttribute('data-status-name');
const customerNotify = this.getAttribute('data-customer-notify') !== 'false';
const customerHasEmail = this.getAttribute('data-customer-email') !== 'false';
// Update modal content
document.getElementById('modalStatusJobNumber').textContent = jobNumber;
document.getElementById('modalCurrentStatus').textContent = statusName;
document.getElementById('statusSelect').value = currentJobStatusId;
// Show email controls only when the customer has an email address on file
const emailRow = document.getElementById('statusModalEmailRow');
if (emailRow) emailRow.style.display = customerHasEmail ? '' : 'none';
// Reflect customer email opt-out preference
const emailCheckbox = document.getElementById('statusModalSendEmail');
const emailOptOutNote = document.getElementById('statusModalEmailOptOutNote');
if (emailCheckbox) {
if (emailCheckbox && customerHasEmail) {
emailCheckbox.disabled = !customerNotify;
if (!customerNotify) emailCheckbox.checked = false;
}
if (emailOptOutNote) emailOptOutNote.style.display = customerNotify ? 'none' : 'block';
if (emailOptOutNote) emailOptOutNote.style.display = (customerHasEmail && !customerNotify) ? 'block' : 'none';
// Show modal
const modal = new bootstrap.Modal(document.getElementById('statusModal'));