Fix subscription expiry logic and HTML entities in page titles
Subscription expiry (SubscriptionExpiryBackgroundService): - Trials with no grace period now go directly Active -> Expired instead of briefly entering GracePeriod for a day, which was causing repeated 'Grace Period Started' admin notification emails - Remove redundant isTrial variable (query already filters to non-Stripe companies, so all processed companies are trials by definition) - Save per-company inside the loop so a single SaveChangesAsync failure no longer discards all other companies' status changes and notification log entries (which was the other cause of repeated emails) HTML entities in page titles (33 views): - Replace – / — with plain ' - ' in ViewData["Title"] C# strings; Razor HTML-encodes these when rendering @ViewData["Title"], causing browsers to display the literal text '–' instead of a dash Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -67,9 +67,9 @@ public class SubscriptionExpiryBackgroundService : BackgroundService
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a DI scope, queries non-Stripe-managed companies with active or grace-period subscriptions,
|
||||
/// and calls <see cref="ProcessCompanyAsync"/> for each. A single <c>SaveChangesAsync</c> at the
|
||||
/// end batches all status mutations into one round-trip. Errors are caught to keep the loop alive.
|
||||
/// Opens a DI scope, queries non-Stripe-managed (trial) companies with active or grace-period
|
||||
/// subscriptions, and calls <see cref="ProcessCompanyAsync"/> for each. Each company is saved
|
||||
/// individually so a single failure does not prevent other companies from being updated.
|
||||
/// </summary>
|
||||
private async Task RunAsync(CancellationToken ct)
|
||||
{
|
||||
@@ -103,15 +103,27 @@ public class SubscriptionExpiryBackgroundService : BackgroundService
|
||||
|
||||
_logger.LogDebug("Found {Count} companies to evaluate.", companies.Count);
|
||||
|
||||
// All companies reaching this point have no StripeSubscriptionId — they are trials.
|
||||
// Paid subscribers are managed by Stripe and filtered out above.
|
||||
var effectiveGraceDays = gracePeriodAppliesToTrials ? gracePeriodDays : 0;
|
||||
|
||||
foreach (var company in companies)
|
||||
{
|
||||
if (ct.IsCancellationRequested) break;
|
||||
var isTrial = string.IsNullOrEmpty(company.StripeSubscriptionId);
|
||||
var effectiveGraceDays = isTrial && !gracePeriodAppliesToTrials ? 0 : gracePeriodDays;
|
||||
await ProcessCompanyAsync(db, emailService, adminNotification, company, today, effectiveGraceDays, ct);
|
||||
try
|
||||
{
|
||||
await ProcessCompanyAsync(db, emailService, adminNotification, company, today, effectiveGraceDays, ct);
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex,
|
||||
"Failed to process subscription expiry for company {Id} ({Name}). Status change was not persisted.",
|
||||
company.Id, company.CompanyName);
|
||||
// Clear EF tracked changes so bad state does not bleed into the next company.
|
||||
db.ChangeTracker.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync(ct);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -121,10 +133,15 @@ public class SubscriptionExpiryBackgroundService : BackgroundService
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates a single company and performs any required status transitions or reminder sends.
|
||||
/// Transition logic: Active past end date → GracePeriod; GracePeriod past grace deadline → Expired + deactivated.
|
||||
/// Transition logic:
|
||||
/// <list type="bullet">
|
||||
/// <item>Active past end date, grace days = 0 → Expired + deactivated immediately (trials).</item>
|
||||
/// <item>Active past end date, grace days > 0 → GracePeriod + grace-period email.</item>
|
||||
/// <item>GracePeriod past grace deadline → Expired + deactivated.</item>
|
||||
/// </list>
|
||||
/// Reminder emails at <see cref="ReminderDays"/> offsets are sent only while the company is still Active.
|
||||
/// Platform admin is notified asynchronously (fire-and-forget) for both grace period start and full expiry
|
||||
/// so that operator action can be taken without delaying the main processing loop.
|
||||
/// Platform admin is notified asynchronously (fire-and-forget) so that operator action can be taken
|
||||
/// without delaying the main processing loop.
|
||||
/// </summary>
|
||||
private async Task ProcessCompanyAsync(
|
||||
ApplicationDbContext db,
|
||||
@@ -153,35 +170,55 @@ public class SubscriptionExpiryBackgroundService : BackgroundService
|
||||
await WriteAuditLogAsync(db, company,
|
||||
$"Auto-expired: grace period ended {gracePeriodDays} days after subscription end {endDate:d}.");
|
||||
|
||||
// Notify platform admin
|
||||
_ = adminNotification.NotifyCompanyExpiredAsync(
|
||||
company.Id, company.CompanyName,
|
||||
company.PrimaryContactEmail ?? string.Empty, expiredDate);
|
||||
}
|
||||
else if (company.SubscriptionStatus == SubscriptionStatus.Active && today > endDate)
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"Company {Id} ({Name}) subscription ended. Entering grace period.",
|
||||
company.Id, company.CompanyName);
|
||||
if (gracePeriodDays == 0)
|
||||
{
|
||||
// No grace period configured — expire immediately without going through GracePeriod.
|
||||
// Trials always land here since gracePeriodAppliesToTrials defaults to false.
|
||||
_logger.LogInformation(
|
||||
"Company {Id} ({Name}) subscription ended with no grace period. Marking Expired and deactivating.",
|
||||
company.Id, company.CompanyName);
|
||||
|
||||
company.SubscriptionStatus = SubscriptionStatus.GracePeriod;
|
||||
company.UpdatedAt = DateTime.UtcNow;
|
||||
company.UpdatedBy = "System";
|
||||
company.SubscriptionStatus = SubscriptionStatus.Expired;
|
||||
company.IsActive = false;
|
||||
company.UpdatedAt = DateTime.UtcNow;
|
||||
company.UpdatedBy = "System";
|
||||
|
||||
await WriteAuditLogAsync(db, company,
|
||||
$"Auto-moved to GracePeriod: subscription ended {endDate:d}. Grace period expires {expiredDate:d}.");
|
||||
await WriteAuditLogAsync(db, company,
|
||||
$"Auto-expired: subscription ended {endDate:d} with no grace period.");
|
||||
|
||||
// Send "grace period started" email to company immediately
|
||||
await SendEmailIfNotSentAsync(db, emailService, company, today,
|
||||
NotificationType.SubscriptionExpiryReminder,
|
||||
daysBeforeExpiry: 0,
|
||||
gracePeriodDays,
|
||||
ct);
|
||||
_ = adminNotification.NotifyCompanyExpiredAsync(
|
||||
company.Id, company.CompanyName,
|
||||
company.PrimaryContactEmail ?? string.Empty, endDate);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"Company {Id} ({Name}) subscription ended. Entering {Days}-day grace period.",
|
||||
company.Id, company.CompanyName, gracePeriodDays);
|
||||
|
||||
// Notify platform admin
|
||||
_ = adminNotification.NotifyCompanyGracePeriodAsync(
|
||||
company.Id, company.CompanyName,
|
||||
company.PrimaryContactEmail ?? string.Empty, expiredDate);
|
||||
company.SubscriptionStatus = SubscriptionStatus.GracePeriod;
|
||||
company.UpdatedAt = DateTime.UtcNow;
|
||||
company.UpdatedBy = "System";
|
||||
|
||||
await WriteAuditLogAsync(db, company,
|
||||
$"Auto-moved to GracePeriod: subscription ended {endDate:d}. Grace period expires {expiredDate:d}.");
|
||||
|
||||
await SendEmailIfNotSentAsync(db, emailService, company, today,
|
||||
NotificationType.SubscriptionExpiryReminder,
|
||||
daysBeforeExpiry: 0,
|
||||
gracePeriodDays,
|
||||
ct);
|
||||
|
||||
_ = adminNotification.NotifyCompanyGracePeriodAsync(
|
||||
company.Id, company.CompanyName,
|
||||
company.PrimaryContactEmail ?? string.Empty, expiredDate);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Reminder emails (only while still Active) ────────────────────
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@model PowderCoating.Application.DTOs.Accounting.AccountLedgerDto
|
||||
@model PowderCoating.Application.DTOs.Accounting.AccountLedgerDto
|
||||
@using PowderCoating.Core.Enums
|
||||
|
||||
@{
|
||||
ViewData["Title"] = $"Ledger — {Model.AccountNumber} {Model.Name}";
|
||||
ViewData["Title"] = $"Ledger - {Model.AccountNumber} {Model.Name}";
|
||||
ViewData["PageIcon"] = "bi-journal-text";
|
||||
ViewData["PageHelpTitle"] = "Account Ledger";
|
||||
ViewData["PageHelpContent"] = "A chronological list of every transaction posted to this account. Click any Reference to open the source record. Debit increases asset and expense accounts; credit increases liability, equity, and revenue accounts. Use the date range or quick buttons (This Month, YTD, etc.) to narrow the view.";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@model PowderCoating.Core.Entities.BankReconciliation
|
||||
@model PowderCoating.Core.Entities.BankReconciliation
|
||||
@using PowderCoating.Web.Controllers
|
||||
@{
|
||||
ViewData["Title"] = $"Reconciliation Report – {Model.Account?.Name}";
|
||||
ViewData["Title"] = $"Reconciliation Report - {Model.Account?.Name}";
|
||||
var clearedDeposits = ViewBag.ClearedDeposits as IEnumerable<PowderCoating.Core.Entities.Payment> ?? Enumerable.Empty<PowderCoating.Core.Entities.Payment>();
|
||||
var clearedPayments = ViewBag.ClearedPayments as List<ReconciliationItem> ?? new();
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@using PowderCoating.Web.Controllers
|
||||
@using PowderCoating.Web.Controllers
|
||||
@model BudgetCreateVm
|
||||
|
||||
@{
|
||||
ViewData["Title"] = $"Edit Budget — {Model.Name}";
|
||||
ViewData["Title"] = $"Edit Budget - {Model.Name}";
|
||||
ViewData["PageIcon"] = "bi-pencil";
|
||||
var months = new[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@model PowderCoating.Application.DTOs.Notification.NotificationTemplateDto
|
||||
@model PowderCoating.Application.DTOs.Notification.NotificationTemplateDto
|
||||
@{
|
||||
ViewData["Title"] = $"Edit Template — {Model.DisplayName}";
|
||||
ViewData["Title"] = $"Edit Template - {Model.DisplayName}";
|
||||
ViewData["PageIcon"] = "bi-envelope-gear";
|
||||
var placeholders = ViewBag.Placeholders as List<(string Placeholder, string Description)>
|
||||
?? new List<(string, string)>();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@model PowderCoating.Application.DTOs.Accounting.CustomerStatementDto
|
||||
@model PowderCoating.Application.DTOs.Accounting.CustomerStatementDto
|
||||
@{
|
||||
ViewData["Title"] = $"Statement – {Model.CustomerName}";
|
||||
ViewData["Title"] = $"Statement - {Model.CustomerName}";
|
||||
}
|
||||
|
||||
<div class="d-flex justify-content-between align-items-start mb-4 flex-wrap gap-2">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@{
|
||||
ViewData["Title"] = "Custom Formula Item Templates — Help";
|
||||
@{
|
||||
ViewData["Title"] = "Custom Formula Item Templates - Help";
|
||||
}
|
||||
|
||||
<div class="row g-4">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@model PowderCoating.Application.DTOs.Inventory.InventoryItemDto
|
||||
@model PowderCoating.Application.DTOs.Inventory.InventoryItemDto
|
||||
@{
|
||||
ViewData["Title"] = $"Label — {Model.Name}";
|
||||
ViewData["Title"] = $"Label - {Model.Name}";
|
||||
Layout = null; // standalone print page
|
||||
}
|
||||
<!DOCTYPE html>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@using PowderCoating.Application.DTOs.Inventory
|
||||
@using PowderCoating.Application.DTOs.Inventory
|
||||
@using PowderCoating.Web.Controllers
|
||||
@{
|
||||
var item = ViewBag.ItemDto as InventoryItemDto;
|
||||
@@ -6,7 +6,7 @@
|
||||
var otherJobs = ViewBag.OtherJobs as List<ScanJobOption> ?? new();
|
||||
var preselectedJobId = ViewBag.PreselectedJobId as int?;
|
||||
var scanError = ViewBag.ScanError as string;
|
||||
ViewData["Title"] = $"Log Usage — {item?.Name}";
|
||||
ViewData["Title"] = $"Log Usage - {item?.Name}";
|
||||
Layout = null; // mobile-first standalone page
|
||||
}
|
||||
<!DOCTYPE html>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@model PowderCoating.Application.DTOs.Job.JobEditItemsViewModel
|
||||
@model PowderCoating.Application.DTOs.Job.JobEditItemsViewModel
|
||||
@using PowderCoating.Core.Entities
|
||||
|
||||
@{
|
||||
ViewData["Title"] = $"Edit Items — {Model.JobNumber}";
|
||||
ViewData["Title"] = $"Edit Items - {Model.JobNumber}";
|
||||
ViewData["PageIcon"] = "bi-list-check";
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@model (PowderCoating.Application.DTOs.Job.JobDto Job, PowderCoating.Application.DTOs.Job.IntakeJobDto Form)
|
||||
@model (PowderCoating.Application.DTOs.Job.JobDto Job, PowderCoating.Application.DTOs.Job.IntakeJobDto Form)
|
||||
|
||||
@{
|
||||
ViewData["Title"] = $"Part Intake — {Model.Job.JobNumber}";
|
||||
ViewData["Title"] = $"Part Intake - {Model.Job.JobNumber}";
|
||||
ViewData["PageIcon"] = "bi-box-seam";
|
||||
var job = Model.Job;
|
||||
var form = Model.Form;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@using PowderCoating.Application.DTOs.PurchaseOrder
|
||||
@using PowderCoating.Application.DTOs.PurchaseOrder
|
||||
@model ReceivePurchaseOrderDto
|
||||
|
||||
@{
|
||||
ViewData["Title"] = $"Receive Goods — {ViewBag.PoNumber}";
|
||||
ViewData["Title"] = $"Receive Goods - {ViewBag.PoNumber}";
|
||||
int poId = (int)ViewBag.PoId;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@model WizardStep1Dto
|
||||
@{
|
||||
ViewData["Title"] = "Setup Wizard — Company Profile";
|
||||
ViewData["Title"] = "Setup Wizard - Company Profile";
|
||||
var progress = ViewBag.Progress as WizardProgressDto ?? new WizardProgressDto();
|
||||
int step = ViewBag.Step as int? ?? 1;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@model WizardStep9Dto
|
||||
@{
|
||||
ViewData["Title"] = "Setup Wizard — Team Members";
|
||||
ViewData["Title"] = "Setup Wizard - Team Members";
|
||||
var progress = ViewBag.Progress as WizardProgressDto ?? new WizardProgressDto();
|
||||
int step = ViewBag.Step as int? ?? 10;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@model WizardStep6Dto
|
||||
@{
|
||||
ViewData["Title"] = "Setup Wizard — Chart of Accounts";
|
||||
ViewData["Title"] = "Setup Wizard - Chart of Accounts";
|
||||
var progress = ViewBag.Progress as WizardProgressDto ?? new WizardProgressDto();
|
||||
int step = ViewBag.Step as int? ?? 11;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@model WizardStep10Dto
|
||||
@{
|
||||
ViewData["Title"] = "Setup Wizard — Vendors & Suppliers";
|
||||
ViewData["Title"] = "Setup Wizard - Vendors & Suppliers";
|
||||
var progress = ViewBag.Progress as WizardProgressDto ?? new WizardProgressDto();
|
||||
int step = ViewBag.Step as int? ?? 12;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@model WizardStep8Dto
|
||||
@{
|
||||
ViewData["Title"] = "Setup Wizard — Inventory / Powder Colors";
|
||||
ViewData["Title"] = "Setup Wizard - Inventory / Powder Colors";
|
||||
var progress = ViewBag.Progress as WizardProgressDto ?? new WizardProgressDto();
|
||||
int step = ViewBag.Step as int? ?? 13;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@model WizardOvensStepDto
|
||||
@{
|
||||
ViewData["Title"] = "Setup Wizard — Equipment & Ovens";
|
||||
ViewData["Title"] = "Setup Wizard - Equipment & Ovens";
|
||||
var progress = ViewBag.Progress as WizardProgressDto ?? new WizardProgressDto();
|
||||
int step = ViewBag.Step as int? ?? 14;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@model WizardPricingTiersStepDto
|
||||
@{
|
||||
ViewData["Title"] = "Setup Wizard — Pricing Tiers";
|
||||
ViewData["Title"] = "Setup Wizard - Pricing Tiers";
|
||||
var progress = ViewBag.Progress as WizardProgressDto ?? new WizardProgressDto();
|
||||
int step = ViewBag.Step as int? ?? 15;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@model WizardCatalogStepDto
|
||||
@{
|
||||
ViewData["Title"] = "Setup Wizard — Service Catalog";
|
||||
ViewData["Title"] = "Setup Wizard - Service Catalog";
|
||||
var progress = ViewBag.Progress as WizardProgressDto ?? new WizardProgressDto();
|
||||
int step = ViewBag.Step as int? ?? 16;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@model WizardStep7Dto
|
||||
@{
|
||||
ViewData["Title"] = "Setup Wizard — Notifications";
|
||||
ViewData["Title"] = "Setup Wizard - Notifications";
|
||||
var progress = ViewBag.Progress as WizardProgressDto ?? new WizardProgressDto();
|
||||
int step = ViewBag.Step as int? ?? 17;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@model WizardStep9Dto
|
||||
@{
|
||||
ViewData["Title"] = "Setup Wizard — Team Members";
|
||||
ViewData["Title"] = "Setup Wizard - Team Members";
|
||||
var progress = ViewBag.Progress as WizardProgressDto ?? new WizardProgressDto();
|
||||
int step = ViewBag.Step as int? ?? 18;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@model WizardStep2QbDto
|
||||
@{
|
||||
ViewData["Title"] = "Setup Wizard — QuickBooks Migration";
|
||||
ViewData["Title"] = "Setup Wizard - QuickBooks Migration";
|
||||
var progress = ViewBag.Progress as WizardProgressDto ?? new WizardProgressDto();
|
||||
int step = ViewBag.Step as int? ?? 2;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@using PowderCoating.Core.Enums
|
||||
@model WizardStep2Dto
|
||||
@{
|
||||
ViewData["Title"] = "Setup Wizard — Operating Costs";
|
||||
ViewData["Title"] = "Setup Wizard - Operating Costs";
|
||||
var progress = ViewBag.Progress as WizardProgressDto ?? new WizardProgressDto();
|
||||
int step = ViewBag.Step as int? ?? 3;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@model WizardOvensStepDto
|
||||
@{
|
||||
ViewData["Title"] = "Setup Wizard — Shop Equipment";
|
||||
ViewData["Title"] = "Setup Wizard - Shop Equipment";
|
||||
var progress = ViewBag.Progress as WizardProgressDto ?? new WizardProgressDto();
|
||||
int step = ViewBag.Step as int? ?? 4;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@model WizardStep3Dto
|
||||
@{
|
||||
ViewData["Title"] = "Setup Wizard — Document Numbering";
|
||||
ViewData["Title"] = "Setup Wizard - Document Numbering";
|
||||
var progress = ViewBag.Progress as WizardProgressDto ?? new WizardProgressDto();
|
||||
int step = ViewBag.Step as int? ?? 5;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@model WizardStep5Dto
|
||||
@{
|
||||
ViewData["Title"] = "Setup Wizard — Job Settings";
|
||||
ViewData["Title"] = "Setup Wizard - Job Settings";
|
||||
var progress = ViewBag.Progress as WizardProgressDto ?? new WizardProgressDto();
|
||||
int step = ViewBag.Step as int? ?? 6;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@model WizardStep4Dto
|
||||
@{
|
||||
ViewData["Title"] = "Setup Wizard — Payment Terms";
|
||||
ViewData["Title"] = "Setup Wizard - Payment Terms";
|
||||
var progress = ViewBag.Progress as WizardProgressDto ?? new WizardProgressDto();
|
||||
int step = ViewBag.Step as int? ?? 7;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@model WizardPricingTiersStepDto
|
||||
@{
|
||||
ViewData["Title"] = "Setup Wizard — Pricing Tiers";
|
||||
ViewData["Title"] = "Setup Wizard - Pricing Tiers";
|
||||
var progress = ViewBag.Progress as WizardProgressDto ?? new WizardProgressDto();
|
||||
int step = ViewBag.Step as int? ?? 8;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@using PowderCoating.Application.DTOs.Wizard
|
||||
@model WizardStep7Dto
|
||||
@{
|
||||
ViewData["Title"] = "Setup Wizard — Notifications";
|
||||
ViewData["Title"] = "Setup Wizard - Notifications";
|
||||
var progress = ViewBag.Progress as WizardProgressDto ?? new WizardProgressDto();
|
||||
int step = ViewBag.Step as int? ?? 5;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-bs-theme="light">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using PowderCoating.Core.Entities
|
||||
@using PowderCoating.Core.Entities
|
||||
@model StripeWebhookEvent
|
||||
@{
|
||||
ViewData["Title"] = $"Webhook Event – {Model.EventId}";
|
||||
ViewData["Title"] = $"Webhook Event - {Model.EventId}";
|
||||
var statusClass = Model.Status switch
|
||||
{
|
||||
StripeWebhookEventStatus.Processed => "success",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@using PowderCoating.Core.Enums
|
||||
@model Company
|
||||
@{
|
||||
ViewData["Title"] = $"Manage – {Model.CompanyName}";
|
||||
ViewData["Title"] = $"Manage - {Model.CompanyName}";
|
||||
var planConfigs = (dynamic)ViewBag.PlanConfigs;
|
||||
|
||||
string PlanName(int plan)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@model PowderCoating.Application.DTOs.Accounting.VendorStatementDto
|
||||
@model PowderCoating.Application.DTOs.Accounting.VendorStatementDto
|
||||
@{
|
||||
ViewData["Title"] = $"Statement – {Model.VendorName}";
|
||||
ViewData["Title"] = $"Statement - {Model.VendorName}";
|
||||
}
|
||||
|
||||
<div class="d-flex justify-content-between align-items-start mb-4 flex-wrap gap-2">
|
||||
|
||||
Reference in New Issue
Block a user