Files
PowderCoatingLogix/src/PowderCoating.Web/Views/Shared/_AiQuickQuoteWidget.cshtml
T
spouliot 4ec55e7290 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>
2026-05-14 20:09:22 -04:00

249 lines
9.3 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Antiforgery
@{
var token = Antiforgery.GetAndStoreTokens(Context).RequestToken;
}
<!-- AI Quick Quote Widget -->
<div id="qq-widget" class="qq-widget" aria-live="polite" aria-label="AI Quick Quote">
<!-- Trigger button -->
<button id="qq-btn" class="qq-trigger" title="Get a quick phone estimate" aria-label="Open AI Quick Quote">
<i class="bi bi-lightning-charge-fill fs-5"></i>
<span class="qq-label">Quick Quote</span>
</button>
<!-- Panel -->
<div id="qq-panel" class="qq-panel" role="dialog" aria-modal="true" aria-label="AI Quick Quote" hidden>
<div class="qq-header">
<div class="d-flex align-items-center gap-2">
<i class="bi bi-lightning-charge-fill text-warning"></i>
<span class="fw-semibold">AI Quick Quote</span>
<span class="badge bg-secondary" style="font-size:0.65rem;">Beta</span>
</div>
<button id="qq-close" class="btn btn-sm btn-outline-secondary py-0 px-2" title="Close" aria-label="Close Quick Quote">
<i class="bi bi-x-lg"></i>
</button>
</div>
<!-- Step 1: Input -->
<div id="qq-step-input" class="qq-body">
<p class="text-muted mb-3" style="font-size:0.82rem;">
Describe what the customer needs and get an instant estimate.
</p>
<div class="mb-3">
<label class="form-label fw-semibold" style="font-size:0.85rem;" for="qq-description">
What does the customer have?
</label>
<textarea id="qq-description"
class="form-control form-control-sm"
rows="4"
maxlength="600"
placeholder="e.g. 4 car wheels done in Alexandrite with an Alien Silver base, about 18 inch diameter…"></textarea>
</div>
<div class="row g-2 mb-3">
<div class="col-6">
<label class="form-label" style="font-size:0.82rem;" for="qq-qty">Quantity</label>
<input type="number" id="qq-qty" class="form-control form-control-sm" value="1" min="1" max="999" />
</div>
<div class="col-6">
<label class="form-label" style="font-size:0.82rem;" for="qq-coats">Coats</label>
<input type="number" id="qq-coats" class="form-control form-control-sm" value="1" min="1" max="5" />
</div>
</div>
<div id="qq-input-error" class="alert alert-danger alert-permanent d-none py-2" style="font-size:0.82rem;"></div>
<button id="qq-analyze-btn" class="btn btn-primary w-100">
<i class="bi bi-lightning-charge-fill me-1"></i> Get Estimate
</button>
</div>
<!-- Step 2: Results -->
<div id="qq-step-results" class="qq-body d-none">
<!-- AI estimates -->
<div id="qq-result-card" class="card border-0 bg-light mb-3">
<div class="card-body p-3">
<div class="fw-semibold mb-2" id="qq-res-description" style="font-size:0.9rem;"></div>
<div class="row g-2 text-center mb-2">
<div class="col-4">
<div class="small text-muted">Sq Ft</div>
<div class="fw-semibold" id="qq-res-sqft"></div>
</div>
<div class="col-4">
<div class="small text-muted">Complexity</div>
<div class="fw-semibold" id="qq-res-complexity"></div>
</div>
<div class="col-4">
<div class="small text-muted">Labor</div>
<div class="fw-semibold" id="qq-res-minutes"></div>
</div>
</div>
<div class="d-flex justify-content-between align-items-center border-top pt-2">
<div>
<div class="small text-muted">Estimate</div>
<div class="fs-5 fw-bold text-success" id="qq-res-price"></div>
<div id="qq-res-oven" class="text-muted" style="font-size:0.73rem;"></div>
</div>
<div class="text-end">
<div class="small text-muted">Confidence</div>
<span id="qq-res-confidence" class="badge"></span>
</div>
</div>
<div id="qq-res-reasoning" class="mt-2 text-muted" style="font-size:0.78rem;"></div>
</div>
</div>
<!-- Powder / color stock status -->
<div id="qq-powder-section" class="d-none mb-3">
<div class="fw-semibold mb-1" style="font-size:0.82rem;">Powder Stock</div>
<div id="qq-powder-list"></div>
</div>
<!-- Save inputs -->
<div class="mb-2">
<label class="form-label" style="font-size:0.82rem;" for="qq-reference">
Reference <span class="text-muted">(caller name or memo)</span>
</label>
<input type="text" id="qq-reference" class="form-control form-control-sm"
placeholder="e.g. John 4 wheels" maxlength="100" />
</div>
<div id="qq-save-error" class="alert alert-danger alert-permanent d-none py-2 mb-2" style="font-size:0.82rem;"></div>
<div class="d-flex gap-2">
<button id="qq-back-btn" class="btn btn-outline-secondary btn-sm flex-shrink-0">
<i class="bi bi-arrow-left"></i>
</button>
<button id="qq-save-btn" class="btn btn-success btn-sm flex-grow-1">
<i class="bi bi-floppy me-1"></i> Save as Draft Quote
</button>
</div>
</div>
<!-- Typing indicator (shared between steps) -->
<div id="qq-loading" class="qq-loading d-none">
<div class="qq-typing-dot"></div>
<div class="qq-typing-dot"></div>
<div class="qq-typing-dot"></div>
<span class="ms-2 text-muted" style="font-size:0.82rem;">Analyzing…</span>
</div>
</div>
</div>
<input type="hidden" id="qq-token" value="@token" />
<script src="~/js/ai-quick-quote.js" asp-append-version="true"></script>
<style>
.qq-widget {
position: fixed;
bottom: 134px; /* sits above the AI Help button at 80px */
right: 24px;
z-index: 1050;
font-family: inherit;
}
.qq-trigger {
display: flex;
align-items: center;
gap: 6px;
background: #1e3a8a;
color: #fff;
border: none;
border-radius: 50px;
padding: 10px 18px;
box-shadow: 0 4px 14px rgba(0,0,0,0.35);
cursor: pointer;
font-size: 0.9rem;
font-weight: 500;
transition: transform 0.15s, box-shadow 0.15s, background 0.15s;
}
.qq-trigger:hover {
transform: translateY(-2px);
box-shadow: 0 6px 18px rgba(0,0,0,0.4);
background: #1d4ed8;
color: #fff;
}
.qq-panel {
position: absolute;
bottom: 60px;
right: 0;
width: 380px;
display: flex;
flex-direction: column;
background: var(--bs-body-bg, #fff);
border: 1px solid var(--bs-border-color, #dee2e6);
border-radius: 12px;
box-shadow: 0 8px 30px rgba(0,0,0,0.15);
overflow: hidden;
}
.qq-panel[hidden] { display: none !important; }
.qq-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 14px;
background: var(--bs-secondary-bg, #f8f9fa);
border-bottom: 1px solid var(--bs-border-color, #dee2e6);
flex-shrink: 0;
}
.qq-body {
padding: 14px;
overflow-y: auto;
max-height: 480px;
}
.qq-loading {
display: flex;
align-items: center;
padding: 12px 14px;
border-top: 1px solid var(--bs-border-color, #dee2e6);
flex-shrink: 0;
}
.qq-typing-dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--bs-secondary-color, #6c757d);
animation: qq-bounce 1.2s infinite ease-in-out;
flex-shrink: 0;
}
.qq-typing-dot:nth-child(2) { animation-delay: 0.2s; margin: 0 4px; }
.qq-typing-dot:nth-child(3) { animation-delay: 0.4s; }
@@keyframes qq-bounce {
0%, 80%, 100% { transform: scale(0.6); opacity: 0.5; }
40% { transform: scale(1); opacity: 1; }
}
.qq-powder-badge {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 0.78rem;
padding: 3px 8px;
border-radius: 20px;
border: 1px solid;
margin: 2px;
}
@@media (max-width: 480px) {
.qq-widget { bottom: 80px; right: 16px; }
.qq-panel { width: calc(100vw - 32px); right: 0; bottom: 54px; }
.qq-label { display: none; }
.qq-trigger { padding: 10px 14px; }
}
</style>