Add formula template walkthrough and UX improvements

- 7-step guided walkthrough modal (concept, output modes, fields, formula,
  testing, box example, cylinder example); auto-shown first time the tab
  opens with no templates; always accessible via "How it works" button
- Variable pill badges below formula input showing all valid field names + rate;
  update live as fields are added/renamed; clickable to insert at cursor
- Fix: Add Field no longer shows validation error on blank new rows; validation
  only fires once the user has typed something
- Help article: added Common Formula Patterns section with box, cylinder, and
  flat panel worked examples (fields table + formula + expected output)
- HelpKnowledgeBase updated with pattern examples and walkthrough note

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-24 11:40:54 -04:00
parent 4650ba3d4d
commit d28e639d1b
4 changed files with 415 additions and 17 deletions
@@ -2064,9 +2064,14 @@
<div class="card shadow-sm mt-3">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0"><i class="bi bi-calculator me-2"></i>Custom Formula Item Templates</h5>
<button type="button" class="btn btn-primary btn-sm" onclick="cfShowCreate()">
<i class="bi bi-plus-circle me-1"></i>New Template
</button>
<div class="d-flex gap-2">
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="cfShowWalkthrough()">
<i class="bi bi-question-circle me-1"></i>How it works
</button>
<button type="button" class="btn btn-primary btn-sm" onclick="cfShowCreate()">
<i class="bi bi-plus-circle me-1"></i>New Template
</button>
</div>
</div>
<div class="card-body">
<p class="text-muted small mb-3">
@@ -2097,6 +2102,34 @@
</div>
</div>
<!-- Custom Formula Walkthrough Modal -->
<div class="modal fade" id="cfWalkthroughModal" tabindex="-1" aria-labelledby="cfWalkthroughLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header border-0 pb-0">
<h5 class="modal-title" id="cfWalkthroughLabel">
<i class="bi bi-calculator text-info me-2"></i>Custom Formula Templates &mdash; How It Works
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body pt-2">
<!-- Step progress dots -->
<div class="d-flex justify-content-center gap-2 mb-4" id="cfWalkthroughDots"></div>
<!-- Step content -->
<div id="cfWalkthroughContent"></div>
</div>
<div class="modal-footer border-0">
<button type="button" class="btn btn-outline-secondary" id="cfWtPrevBtn" onclick="cfWalkthroughNav(-1)">
<i class="bi bi-arrow-left me-1"></i>Back
</button>
<button type="button" class="btn btn-primary" id="cfWtNextBtn" onclick="cfWalkthroughNav(1)">
Next<i class="bi bi-arrow-right ms-1"></i>
</button>
</div>
</div>
</div>
</div>
<!-- Custom Formula Template Modal -->
<div class="modal fade" id="cfModal" tabindex="-1" aria-labelledby="cfModalLabel" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-scrollable">
@@ -2138,7 +2171,10 @@
<div class="mb-3">
<label class="form-label">Formula <span class="text-danger">*</span></label>
<input type="text" id="cfFormula" class="form-control font-monospace" placeholder="e.g. 2*(L*W + L*H + W*H)/144 * rate" />
<div class="form-text">NCalc expression. Available variables: field names + <code>rate</code>.</div>
<div class="form-text mt-1">
<span class="me-1">Available variables for this formula:</span>
<span id="cfVariablePills"></span>
</div>
</div>
<div class="mb-3">
<label class="form-label">Notes</label>
@@ -3438,9 +3474,16 @@
</script>
<script src="~/js/company-settings-custom-formulas.js" asp-append-version="true"></script>
<script>
// Load formula templates when the tab is first shown
document.querySelector('[data-bs-target="#custom-formulas"]').addEventListener('shown.bs.tab', () => {
if (!window._cfLoaded) { cfLoadTemplates(); window._cfLoaded = true; }
// Load formula templates when the tab is first shown; auto-show walkthrough if no templates yet
document.querySelector('[data-bs-target="#custom-formulas"]').addEventListener('shown.bs.tab', async () => {
if (!window._cfLoaded) {
await cfLoadTemplates();
window._cfLoaded = true;
if (!localStorage.getItem('cfWalkthroughSeen')) {
const hasTemplates = document.querySelectorAll('#cfTemplatesBody tr[data-id]').length > 0;
if (!hasTemplates) cfShowWalkthrough();
}
}
});
</script>
@@ -91,6 +91,56 @@
</div>
</div>
<div class="card border-0 shadow-sm mb-4">
<div class="card-body">
<h2 class="h5">Common formula patterns</h2>
<p>These ready-to-use templates cover the most common fabricated item shapes. Copy the fields and formula exactly, then adjust the Default Rate for your shop.</p>
<h3 class="h6 mt-3 mb-2"><i class="bi bi-box me-1"></i>6-Sided Box (roof curbs, enclosures)</h3>
<p class="text-muted small mb-2">Calculates the total outer surface area of all six faces.</p>
<div class="table-responsive mb-2">
<table class="table table-sm table-bordered">
<thead class="table-light"><tr><th>Variable name</th><th>Label</th><th>Default</th></tr></thead>
<tbody>
<tr><td><code>l_in</code></td><td>Length (inches)</td><td>24</td></tr>
<tr><td><code>w_in</code></td><td>Width (inches)</td><td>24</td></tr>
<tr><td><code>h_in</code></td><td>Height (inches)</td><td>12</td></tr>
</tbody>
</table>
</div>
<pre class="bg-light p-2 rounded mb-1">2*(l_in*w_in + l_in*h_in + w_in*h_in) / 144 * rate</pre>
<p class="text-muted small mb-3">Output mode: <strong>Fixed Rate</strong> &mdash; suggested rate: your $/sqft coating price. A 24&times;24&times;12 box at $3.50/sqft = $28.00.</p>
<h3 class="h6 mt-3 mb-2"><i class="bi bi-circle me-1"></i>Cylinder (pipe ends, round housings)</h3>
<p class="text-muted small mb-2">Lateral surface plus two circular end caps.</p>
<div class="table-responsive mb-2">
<table class="table table-sm table-bordered">
<thead class="table-light"><tr><th>Variable name</th><th>Label</th><th>Default</th></tr></thead>
<tbody>
<tr><td><code>d_in</code></td><td>Diameter (inches)</td><td>12</td></tr>
<tr><td><code>h_in</code></td><td>Height (inches)</td><td>18</td></tr>
</tbody>
</table>
</div>
<pre class="bg-light p-2 rounded mb-1">(3.14159 * d_in * h_in + 2 * 3.14159 * Pow(d_in/2, 2)) / 144 * rate</pre>
<p class="text-muted small mb-3">Output mode: <strong>Fixed Rate</strong> &mdash; a 12&Prime; diameter &times; 18&Prime; tall cylinder at $3.50/sqft &asymp; $10.21.</p>
<h3 class="h6 mt-3 mb-2"><i class="bi bi-layout-text-window me-1"></i>Flat Panel</h3>
<p class="text-muted small mb-2">Simple rectangular sheet &mdash; one face only.</p>
<div class="table-responsive mb-2">
<table class="table table-sm table-bordered">
<thead class="table-light"><tr><th>Variable name</th><th>Label</th><th>Default</th></tr></thead>
<tbody>
<tr><td><code>l_in</code></td><td>Length (inches)</td><td>24</td></tr>
<tr><td><code>w_in</code></td><td>Width (inches)</td><td>12</td></tr>
</tbody>
</table>
</div>
<pre class="bg-light p-2 rounded mb-1">l_in * w_in / 144 * rate</pre>
<p class="text-muted small mb-0">Output mode: <strong>Fixed Rate</strong> &mdash; a 24&Prime;&times;12&Prime; panel at $3.50/sqft = $7.00.</p>
</div>
</div>
<div class="card border-0 shadow-sm mb-4">
<div class="card-body">
<h2 class="h5">NCalc formula reference</h2>
@@ -101,12 +151,6 @@
<li>Variable names must start with a letter and contain only letters, digits, or underscores.</li>
<li>The reserved variable <code>rate</code> is pre-populated from the template&rsquo;s Default Rate.</li>
</ul>
<p class="mb-0">Examples:</p>
<ul class="mb-0">
<li><code>2*(l*w + l*h + w*h) / 144 * rate</code> &mdash; box surface area (inches &rarr; sqft &rarr; dollars)</li>
<li><code>Pow(diameter_in / 2, 2) * 3.14159 / 144 * rate</code> &mdash; circular face area</li>
<li><code>(l_ft * w_ft) * rate</code> &mdash; flat panel in feet</li>
</ul>
</div>
</div>
</div>