Add Formula Library ratings, Job Profitability report, and Quote Revision History improvements
- Formula Library ratings: thumbs up/down per company per formula; toggle on/off; sorts by net score; own formulas not rateable; FormulaLibraryRating entity + migration AddFormulaLibraryRatings - Job Profitability report: actual labor cost (logged hours x StandardLaborRate) vs powder cost vs billed price per job; gross margin % color-coded; time-tracked-only filter; totals footer - Quote Revision History: track Total price changes on every save; log Sent/Resent events with recipient email; replace flat table with grouped timeline UI (icons per event type, total-change badge on header) - Setup Wizard: cap CompletedCount at TotalSteps so old 10-step data no longer shows 10/5 - Formula Library card: fix badge overflow on long titles; add Rate: label to make voting buttons discoverable - Help docs and AI knowledge base updated for all three features Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -91,32 +91,32 @@
|
||||
<div class="card h-100 border-0 shadow-sm formula-card @(item.IsOwnFormula ? "border-start border-warning border-3" : item.AlreadyImported ? "border-start border-success border-3" : "")">
|
||||
<div class="card-body d-flex flex-column">
|
||||
|
||||
@* Header row *@
|
||||
<div class="d-flex align-items-start gap-2 mb-2">
|
||||
<div class="flex-grow-1 min-w-0">
|
||||
@* Header row — min-w-0 on both sides prevents long titles from pushing badges off-card *@
|
||||
<div class="d-flex align-items-start gap-2 mb-2" style="min-width:0">
|
||||
<div class="flex-grow-1" style="min-width:0;overflow:hidden">
|
||||
<h6 class="fw-semibold mb-0 text-truncate" title="@item.Name">@item.Name</h6>
|
||||
<small class="text-muted">
|
||||
<small class="text-muted text-truncate d-block">
|
||||
<i class="bi bi-building me-1"></i>@item.SourceCompanyName
|
||||
</small>
|
||||
</div>
|
||||
<div class="d-flex flex-column align-items-end gap-1 flex-shrink-0">
|
||||
<div class="d-flex flex-column align-items-end gap-1" style="flex-shrink:0;max-width:50%">
|
||||
@if (item.OutputMode == "FixedRate")
|
||||
{
|
||||
<span class="badge bg-primary-subtle text-primary border border-primary-subtle">Fixed Rate</span>
|
||||
<span class="badge bg-primary-subtle text-primary border border-primary-subtle text-nowrap">Fixed Rate</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge bg-info-subtle text-info border border-info-subtle">Surface Area</span>
|
||||
<span class="badge bg-info-subtle text-info border border-info-subtle text-nowrap">Surface Area</span>
|
||||
}
|
||||
@if (item.IsOwnFormula)
|
||||
{
|
||||
<span class="badge bg-warning-subtle text-warning border border-warning-subtle">
|
||||
<span class="badge bg-warning-subtle text-warning border border-warning-subtle text-nowrap">
|
||||
<i class="bi bi-star-fill me-1"></i>Your Formula
|
||||
</span>
|
||||
}
|
||||
else if (item.AlreadyImported)
|
||||
{
|
||||
<span class="badge bg-success-subtle text-success border border-success-subtle">
|
||||
<span class="badge bg-success-subtle text-success border border-success-subtle text-nowrap">
|
||||
<i class="bi bi-check-lg me-1"></i>Imported
|
||||
</span>
|
||||
}
|
||||
@@ -156,21 +156,51 @@
|
||||
}
|
||||
|
||||
@* Footer row *@
|
||||
<div class="d-flex align-items-center justify-content-between mt-auto pt-2 border-top">
|
||||
<small class="text-muted">
|
||||
<i class="bi bi-download me-1"></i>@item.ImportCount import@(item.ImportCount == 1 ? "" : "s")
|
||||
</small>
|
||||
<div class="d-flex align-items-center justify-content-between mt-auto pt-2 border-top gap-2">
|
||||
@* Left: import count + rating buttons *@
|
||||
<div class="d-flex align-items-center gap-2 flex-wrap">
|
||||
<small class="text-muted text-nowrap">
|
||||
<i class="bi bi-download me-1"></i>@item.ImportCount import@(item.ImportCount == 1 ? "" : "s")
|
||||
</small>
|
||||
@if (!item.IsOwnFormula)
|
||||
{
|
||||
<div class="d-flex align-items-center gap-1"
|
||||
data-rating-group="@item.Id"
|
||||
title="Rate this formula">
|
||||
<span class="text-muted small me-1">Rate:</span>
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-vote @(item.MyVote == true ? "btn-success active-vote" : "btn-outline-secondary")"
|
||||
data-item-id="@item.Id"
|
||||
data-is-positive="true"
|
||||
title="Helpful"
|
||||
aria-label="Thumbs up">
|
||||
<i class="bi bi-hand-thumbs-up"></i>
|
||||
<span class="vote-up-count ms-1">@item.ThumbsUp</span>
|
||||
</button>
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-vote @(item.MyVote == false ? "btn-danger active-vote" : "btn-outline-secondary")"
|
||||
data-item-id="@item.Id"
|
||||
data-is-positive="false"
|
||||
title="Not helpful"
|
||||
aria-label="Thumbs down">
|
||||
<i class="bi bi-hand-thumbs-down"></i>
|
||||
<span class="vote-down-count ms-1">@item.ThumbsDown</span>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@* Right: action button *@
|
||||
@if (item.IsOwnFormula)
|
||||
{
|
||||
<a asp-controller="CompanySettings" asp-action="Index" asp-fragment="custom-formulas"
|
||||
class="btn btn-sm btn-outline-warning">
|
||||
class="btn btn-sm btn-outline-warning flex-shrink-0">
|
||||
<i class="bi bi-gear me-1"></i><span>Manage</span>
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button"
|
||||
class="btn btn-sm @(item.AlreadyImported ? "btn-outline-success" : "btn-outline-primary") btn-import"
|
||||
class="btn btn-sm @(item.AlreadyImported ? "btn-outline-success" : "btn-outline-primary") btn-import flex-shrink-0"
|
||||
data-item-id="@item.Id"
|
||||
data-item-name="@item.Name">
|
||||
@if (item.AlreadyImported)
|
||||
|
||||
Reference in New Issue
Block a user