Add one-click Demo Company reset for tutorial recording prep
New ResetDemoCompany POST action wipes all seeded data (customers, jobs, quotes, invoices, inventory, equipment, catalog, pricing tiers, operating costs) from the DEMO company and immediately re-seeds with fresh records dated relative to today. Seed data already used relative dates so every reset produces a realistic, current-looking dataset. View adds a red "Reset Demo Company" card at the top of the Seed Data page, visible only when the DEMO company exists. Single button with confirm dialog; shows exactly what will be wiped and what will be preserved (user accounts, company settings, lookup tables). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -106,6 +106,75 @@ public class SeedDataController : Controller
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wipes all seeded data from the DEMO company and immediately re-seeds it with fresh demo data
|
||||
/// so all dates are current. Intended for tutorial recording resets — one click returns the demo
|
||||
/// company to a clean, realistic state without touching any other tenant.
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> ResetDemoCompany()
|
||||
{
|
||||
try
|
||||
{
|
||||
var companies = await _seedDataService.GetCompaniesAsync();
|
||||
var demo = companies.FirstOrDefault(c => c.CompanyCode == "DEMO");
|
||||
if (demo == null)
|
||||
{
|
||||
TempData["ErrorMessage"] = "Demo company (code: DEMO) not found. Run Seed System Data first.";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
// Remove all seed data categories
|
||||
var removeOptions = new RemoveSeedDataOptions
|
||||
{
|
||||
Customers = true,
|
||||
InventoryItems = true,
|
||||
Equipment = true,
|
||||
Catalog = true,
|
||||
PricingTiers = true,
|
||||
OperatingCosts = true,
|
||||
};
|
||||
|
||||
var removeResult = await _seedDataService.RemoveSeedDataAsync(demo.Id, removeOptions);
|
||||
if (!removeResult.Success)
|
||||
{
|
||||
TempData["ErrorMessage"] = $"Wipe step failed: {removeResult.Message}";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
// Re-seed with today's dates
|
||||
var seedResult = await _seedDataService.SeedCompanyDataAsync(demo.Id);
|
||||
|
||||
if (seedResult.Success)
|
||||
{
|
||||
TempData["SuccessMessage"] = $"Demo company reset complete. {seedResult.ItemsSeeded} records re-seeded with today's dates.";
|
||||
TempData["SeedDetails"] = string.Join("|", seedResult.Details);
|
||||
TempData["ItemsSeeded"] = seedResult.ItemsSeeded;
|
||||
|
||||
if (seedResult.Warnings.Any())
|
||||
{
|
||||
TempData["WarningMessage"] = $"{seedResult.ItemsSkipped} item(s) were skipped";
|
||||
var displayWarnings = seedResult.Warnings.Take(30).ToList();
|
||||
if (seedResult.Warnings.Count > 30)
|
||||
displayWarnings.Add($"… and {seedResult.Warnings.Count - 30} more (see logs)");
|
||||
TempData["SeedWarnings"] = string.Join("|", displayWarnings);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData["ErrorMessage"] = $"Wipe succeeded but re-seed failed: {seedResult.Message}";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error resetting demo company");
|
||||
TempData["ErrorMessage"] = $"An error occurred during demo reset: {ex.Message}";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes previously seeded demo data from a company according to the supplied options (e.g., jobs only, or all data). Used during QA/demo resets to return a company to a clean state without a full database drop.
|
||||
/// </summary>
|
||||
|
||||
@@ -98,6 +98,44 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Demo Reset Card — only shown when the DEMO company exists -->
|
||||
@{
|
||||
var demoCompany = Model?.FirstOrDefault(c => c.CompanyCode == "DEMO");
|
||||
}
|
||||
@if (demoCompany != null)
|
||||
{
|
||||
<div class="card mb-4 border-danger">
|
||||
<div class="card-header bg-danger text-white d-flex align-items-center gap-2">
|
||||
<i class="bi bi-arrow-repeat fs-5"></i>
|
||||
<h5 class="mb-0">Reset Demo Company</h5>
|
||||
<span class="badge bg-white text-danger ms-auto">Tutorial Prep</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="card-text">
|
||||
Wipes <strong>all seeded data</strong> from the Demo Company and immediately re-seeds it with
|
||||
fresh records dated relative to <strong>today</strong>. Use this before every recording session
|
||||
so jobs, quotes, invoices, and AR aging always look current.
|
||||
</p>
|
||||
<ul class="mb-3 small">
|
||||
<li>Removes: customers, jobs, quotes, invoices, inventory, equipment, catalog, pricing tiers, operating costs</li>
|
||||
<li>Re-seeds: 100 customers, 50 jobs across all statuses, quotes, invoices, inventory transactions, vendor bills, appointments — all dated from today</li>
|
||||
<li>Preserves: user accounts, company settings, lookup tables (job statuses, priorities, etc.)</li>
|
||||
</ul>
|
||||
<div class="alert alert-warning alert-permanent mb-3">
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
<strong>This permanently deletes and recreates all demo data.</strong> Any manual edits made to the demo company will be lost.
|
||||
</div>
|
||||
<form asp-action="ResetDemoCompany" method="post"
|
||||
onsubmit="return confirm('Reset the Demo Company?\n\nThis will DELETE all seeded data and re-seed it with fresh records dated today.\n\nAny manual edits to the demo company will be lost.');">
|
||||
@Html.AntiForgeryToken()
|
||||
<button type="submit" class="btn btn-danger">
|
||||
<i class="bi bi-arrow-repeat me-2"></i>Reset Demo Company Now
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- System Data Card -->
|
||||
<div class="card mb-4 border-primary">
|
||||
<div class="card-header bg-primary text-white">
|
||||
|
||||
Reference in New Issue
Block a user