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,117 @@
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@model WizardOvensStepDto
|
||||
@{
|
||||
ViewData["Title"] = "Setup Wizard — Equipment & Ovens";
|
||||
var progress = ViewBag.Progress as WizardProgressDto ?? new WizardProgressDto();
|
||||
int step = ViewBag.Step as int? ?? 14;
|
||||
}
|
||||
@section Styles { @await Html.PartialAsync("_WizardStyles") }
|
||||
|
||||
<div class="wizard-layout">
|
||||
@await Html.PartialAsync("_WizardProgress", progress)
|
||||
|
||||
<div class="wizard-content">
|
||||
<div class="wizard-step-header">
|
||||
<span class="wizard-step-badge">Step @step of @WizardProgressDto.TotalSteps</span>
|
||||
<h2><i class="bi bi-fire me-2"></i>Equipment & Ovens</h2>
|
||||
<p class="text-secondary">Register your ovens so the Oven Scheduler can track capacity and plan batches. You can add other equipment (spray booths, compressors) from the Equipment section later.</p>
|
||||
</div>
|
||||
|
||||
<form asp-action="PostStep14" method="post">
|
||||
@Html.AntiForgeryToken()
|
||||
<input type="hidden" name="OvensJson" id="ovensJson" value="[]" />
|
||||
|
||||
<div class="wizard-card">
|
||||
<h5 class="wizard-card-title">Named Ovens</h5>
|
||||
<p class="text-secondary small mb-3">
|
||||
Each oven entry appears in the Oven Scheduler's capacity planner. Set the maximum load (sq ft) and typical cycle time so the scheduler can estimate how many batches fit in a day.
|
||||
</p>
|
||||
|
||||
<div id="ovensList"></div>
|
||||
|
||||
<button type="button" class="btn btn-outline-primary btn-sm mt-2" onclick="addOven()">
|
||||
<i class="bi bi-plus-circle me-1"></i>Add Oven
|
||||
</button>
|
||||
|
||||
<div class="alert alert-info alert-permanent d-flex gap-2 mt-3 mb-0" role="alert">
|
||||
<i class="bi bi-info-circle flex-shrink-0 mt-1"></i>
|
||||
<div class="small">
|
||||
You can skip this step and configure equipment later from <strong>Operations → Equipment</strong>. However, the Oven Scheduler requires at least one named oven to create batches.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@await Html.PartialAsync("_WizardFooter", step)
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
var ovens = [];
|
||||
|
||||
function renderOvens() {
|
||||
var container = document.getElementById('ovensList');
|
||||
if (ovens.length === 0) {
|
||||
container.innerHTML = '<p class="text-secondary small py-2">No ovens added yet. You can skip this step and add equipment later.</p>';
|
||||
} else {
|
||||
container.innerHTML = ovens.map(function (o, idx) {
|
||||
return `<div class="wz-item-row">
|
||||
<div class="row g-2 align-items-end">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small fw-semibold mb-1">Oven Name <span class="text-danger">*</span></label>
|
||||
<input class="form-control form-control-sm" value="${escHtml(o.label)}" onchange="updateOven(${idx},'label',this.value)" placeholder="e.g. Main Oven, Oven #2" />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small fw-semibold mb-1">Cost/hr ($)</label>
|
||||
<input class="form-control form-control-sm" type="number" min="0" step="0.01" value="${o.costPerHour || 0}" onchange="updateOvenNum(${idx},'costPerHour',this.value)" />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small fw-semibold mb-1">Max Load (sq ft)</label>
|
||||
<input class="form-control form-control-sm" type="number" min="0" step="0.1" value="${o.maxLoadSqFt || ''}" onchange="updateOvenNum(${idx},'maxLoadSqFt',this.value)" placeholder="e.g. 80" />
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label small fw-semibold mb-1">Cycle Time (min)</label>
|
||||
<input class="form-control form-control-sm" type="number" min="1" step="1" value="${o.defaultCycleMinutes || ''}" onchange="updateOvenNum(${idx},'defaultCycleMinutes',this.value)" placeholder="e.g. 25" />
|
||||
</div>
|
||||
<div class="col-md-2 text-end">
|
||||
<button type="button" class="btn btn-outline-danger btn-sm" onclick="removeOven(${idx})" title="Remove">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
document.getElementById('ovensJson').value = JSON.stringify(ovens);
|
||||
}
|
||||
|
||||
function addOven() {
|
||||
ovens.push({ label: '', costPerHour: 0, maxLoadSqFt: null, defaultCycleMinutes: null });
|
||||
renderOvens();
|
||||
var inputs = document.querySelectorAll('.wz-item-row:last-child input');
|
||||
if (inputs.length) inputs[0].focus();
|
||||
}
|
||||
|
||||
function updateOven(idx, field, value) {
|
||||
ovens[idx][field] = value;
|
||||
document.getElementById('ovensJson').value = JSON.stringify(ovens);
|
||||
}
|
||||
|
||||
function updateOvenNum(idx, field, value) {
|
||||
ovens[idx][field] = value === '' ? null : parseFloat(value);
|
||||
document.getElementById('ovensJson').value = JSON.stringify(ovens);
|
||||
}
|
||||
|
||||
function removeOven(idx) {
|
||||
ovens.splice(idx, 1);
|
||||
renderOvens();
|
||||
}
|
||||
|
||||
function escHtml(str) {
|
||||
return (str || '').toString().replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
||||
}
|
||||
|
||||
renderOvens();
|
||||
</script>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user