Restore all zeroed views + add bulk gift certificate creation
The HTML entity sweep script had a bug where it wrote empty files for any
view that contained no target Unicode characters, zeroing out 215 view files.
All views restored from the pre-sweep commit (cefdf3e).
Bulk gift certificate feature:
- BulkCreateGiftCertificateDto with Quantity (1-500), Amount, Reason, Expiry, Notes
- GenerateBulkGiftCertificatePdfAsync on IPdfService / PdfService: one Letter page
per cert, reusing the same purple/gold branded ComposeGiftCertificateContent helper
- GiftCertificatesController: BulkCreate GET/POST, BulkResult GET, BulkDownloadPdf POST
- Views: BulkCreate.cshtml (form with live total preview), BulkResult.cshtml (table +
Download All PDF button that POSTs cert IDs to avoid URL length limits)
- gift-certificate-bulk.js: live preview + spinner/disable on submit
- Index.cshtml: Bulk Create button added alongside New Certificate
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
@using PowderCoating.Core.Entities
|
||||
@model Announcement
|
||||
@{
|
||||
var planConfigs = (dynamic)ViewBag.PlanConfigs;
|
||||
var companies = (dynamic)ViewBag.Companies;
|
||||
}
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-12">
|
||||
<label class="form-label fw-medium">Title <span class="text-danger">*</span></label>
|
||||
<input asp-for="Title" class="form-control" placeholder="e.g. Scheduled maintenance tonight" required />
|
||||
<span asp-validation-for="Title" class="text-danger small"></span>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label fw-medium">Message <span class="text-danger">*</span></label>
|
||||
<textarea asp-for="Message" class="form-control" rows="3"
|
||||
placeholder="The platform will be offline for maintenance on Saturday from 2–4 AM ET." required></textarea>
|
||||
<span asp-validation-for="Message" class="text-danger small"></span>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label fw-medium">Type</label>
|
||||
<select asp-for="Type" class="form-select">
|
||||
<option value="info">Info (blue)</option>
|
||||
<option value="success">Success (green)</option>
|
||||
<option value="warning">Warning (yellow)</option>
|
||||
<option value="danger">Danger (red)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label fw-medium">Target Audience</label>
|
||||
<select asp-for="Target" class="form-select" id="targetSelect" onchange="toggleTarget()">
|
||||
<option value="All">All companies</option>
|
||||
<option value="Plan">Specific plan</option>
|
||||
<option value="Company">Specific company</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div id="planTargetGroup" style="display:none">
|
||||
<label class="form-label fw-medium">Plan</label>
|
||||
<select asp-for="TargetPlan" class="form-select">
|
||||
<option value="">— select —</option>
|
||||
@foreach (var p in planConfigs)
|
||||
{
|
||||
<option value="@p.Plan">@p.DisplayName</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div id="companyTargetGroup" style="display:none">
|
||||
<label class="form-label fw-medium">Company</label>
|
||||
<select asp-for="TargetCompanyId" class="form-select">
|
||||
<option value="">— select —</option>
|
||||
@foreach (var c in companies)
|
||||
{
|
||||
<option value="@c.Id">@c.CompanyName</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-medium">Starts At</label>
|
||||
<input asp-for="StartsAt" type="datetime-local" class="form-control"
|
||||
value="@Model.StartsAt.Tz(ViewBag.CompanyTimeZone as string).ToString("yyyy-MM-ddTHH:mm")" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label class="form-label fw-medium">Expires At <span class="text-muted fw-normal">(optional)</span></label>
|
||||
<input asp-for="ExpiresAt" type="datetime-local" class="form-control"
|
||||
value="@(Model.ExpiresAt.HasValue ? Model.ExpiresAt.Value.Tz(ViewBag.CompanyTimeZone as string).ToString("yyyy-MM-ddTHH:mm") : "")" />
|
||||
<div class="form-text">Leave blank to never expire.</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="form-check form-switch mt-2">
|
||||
<input asp-for="IsActive" class="form-check-input" type="checkbox" />
|
||||
<label asp-for="IsActive" class="form-check-label">Active (visible to users)</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="form-check form-switch mt-2">
|
||||
<input asp-for="IsDismissible" class="form-check-input" type="checkbox" />
|
||||
<label asp-for="IsDismissible" class="form-check-label">Dismissible by users</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Live preview *@
|
||||
<div class="col-12">
|
||||
<label class="form-label fw-medium text-muted">Preview</label>
|
||||
<div id="announcementPreview" class="alert mb-0" role="alert">
|
||||
<strong id="previewTitle">@Model.Title</strong>
|
||||
<span id="previewMessage"> — @Model.Message</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function toggleTarget() {
|
||||
const v = document.getElementById('targetSelect').value;
|
||||
document.getElementById('planTargetGroup').style.display = v === 'Plan' ? '' : 'none';
|
||||
document.getElementById('companyTargetGroup').style.display = v === 'Company' ? '' : 'none';
|
||||
}
|
||||
toggleTarget();
|
||||
|
||||
// Live preview
|
||||
const typeMap = { info: 'alert-info', success: 'alert-success', warning: 'alert-warning', danger: 'alert-danger' };
|
||||
function updatePreview() {
|
||||
const type = document.getElementById('Type').value;
|
||||
const preview = document.getElementById('announcementPreview');
|
||||
preview.className = 'alert mb-0 ' + (typeMap[type] || 'alert-info');
|
||||
document.getElementById('previewTitle').textContent = document.getElementById('Title').value || 'Title';
|
||||
document.getElementById('previewMessage').textContent = ' — ' + (document.getElementById('Message').value || 'Message');
|
||||
}
|
||||
document.getElementById('Type')?.addEventListener('change', updatePreview);
|
||||
document.getElementById('Title')?.addEventListener('input', updatePreview);
|
||||
document.getElementById('Message')?.addEventListener('input', updatePreview);
|
||||
updatePreview();
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user