4ec55e7290
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>
123 lines
5.4 KiB
Plaintext
123 lines
5.4 KiB
Plaintext
@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>
|