Files
PowderCoatingLogix/src/PowderCoating.Web/Views/RecurringTemplates/Index.cshtml
T
spouliot f467862877 Add mobile card views to 12 high-priority list pages
Pages were blank on phones because mobile-cards.css hides .table-responsive
below 992px. Added .mobile-card-view sections to: GiftCertificates, PurchaseOrders,
CreditMemos, VendorCredits, JournalEntries, Appointments, InAppNotifications,
BankReconciliations, FixedAssets, RecurringTemplates, SmsAgreements, SmsConsentAudit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 23:07:52 -04:00

251 lines
13 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.
@model List<PowderCoating.Core.Entities.RecurringTemplate>
@using PowderCoating.Core.Enums
@{
ViewData["Title"] = "Recurring Transactions";
}
<div class="d-flex align-items-center justify-content-between mb-4">
<div>
<h4 class="fw-bold mb-0"><i class="bi bi-arrow-repeat me-2 text-primary"></i>Recurring Transactions</h4>
<p class="text-muted small mb-0">Templates that auto-generate bills or expenses on a schedule.</p>
</div>
<a asp-action="Create" class="btn btn-primary"><i class="bi bi-plus-lg me-1"></i>New Template</a>
</div>
@if (TempData["Success"] != null)
{
<div class="alert alert-success alert-permanent alert-dismissible fade show">
<i class="bi bi-check-circle-fill me-2"></i>@TempData["Success"]
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
}
@if (TempData["Error"] != null)
{
<div class="alert alert-danger alert-permanent alert-dismissible fade show">
<i class="bi bi-exclamation-triangle-fill me-2"></i>@TempData["Error"]
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
}
@if (!Model.Any())
{
<div class="text-center py-5">
<i class="bi bi-arrow-repeat display-4 text-muted"></i>
<h5 class="mt-3 text-muted">No recurring templates yet</h5>
<p class="text-muted">Create a template to automatically generate bills or expenses on a schedule.</p>
<a asp-action="Create" class="btn btn-primary mt-2"><i class="bi bi-plus-lg me-1"></i>Create Template</a>
</div>
}
else
{
<div class="mobile-card-view">
<div class="mobile-card-list">
@foreach (var t in Model)
{
var isOverdueRT = t.IsActive && t.NextFireDate.Date < DateTime.Today;
<div class="mobile-data-card">
<div class="mobile-card-header">
<div class="mobile-card-icon" style="background: linear-gradient(135deg, #f97316 0%, #ea580c 100%);">
<i class="bi bi-arrow-repeat"></i>
</div>
<div class="mobile-card-title">
<h6>@t.Name</h6>
<small>
@if (t.TemplateType == RecurringTemplateType.Bill)
{
<span>Bill</span>
}
else
{
<span>Expense</span>
}
&mdash;
@(t.IntervalCount == 1 ? t.Frequency.ToString() : $"Every {t.IntervalCount} &times; {t.Frequency}")
</small>
</div>
</div>
<div class="mobile-card-body">
<div class="mobile-card-row">
<span class="mobile-card-label">Status</span>
<span class="mobile-card-value">
@if (t.IsActive)
{
<span class="badge bg-success"><i class="bi bi-play-fill me-1"></i>Active</span>
}
else
{
<span class="badge bg-secondary"><i class="bi bi-pause-fill me-1"></i>Paused</span>
}
</span>
</div>
@if (t.IsActive)
{
<div class="mobile-card-row">
<span class="mobile-card-label">Next Fire</span>
<span class="mobile-card-value @(isOverdueRT ? "text-danger fw-semibold" : "")">
@t.NextFireDate.ToString("MM/dd/yyyy")
@if (isOverdueRT) { <i class="bi bi-exclamation-circle ms-1"></i> }
</span>
</div>
}
<div class="mobile-card-row">
<span class="mobile-card-label">Occurrences</span>
<span class="mobile-card-value">
@t.OccurrenceCount
@if (t.MaxOccurrences.HasValue) { <span class="text-muted"> / @t.MaxOccurrences</span> }
</span>
</div>
@if (!string.IsNullOrWhiteSpace(t.LastError))
{
<div class="mobile-card-row">
<span class="mobile-card-label">Error</span>
<span class="mobile-card-value text-danger small">@t.LastError</span>
</div>
}
</div>
<div class="mobile-card-footer">
<a asp-action="Edit" asp-route-id="@t.Id" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-pencil"></i>
</a>
<form asp-action="ToggleActive" asp-route-id="@t.Id" method="post" style="display:inline;">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-sm @(t.IsActive ? "btn-outline-warning" : "btn-outline-success")">
<i class="bi @(t.IsActive ? "bi-pause" : "bi-play")"></i>
</button>
</form>
@if (t.IsActive)
{
<form asp-action="GenerateNow" asp-route-id="@t.Id" method="post" style="display:inline;"
onsubmit="return confirm('Generate one occurrence now?')">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-sm btn-outline-primary">
<i class="bi bi-lightning-charge"></i>
</button>
</form>
}
</div>
</div>
}
</div>
</div>
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-light">
<tr>
<th>Name</th>
<th>Type</th>
<th>Frequency</th>
<th>Next Fire</th>
<th>Occurrences</th>
<th>Status</th>
<th>Last Error</th>
<th class="text-end">Actions</th>
</tr>
</thead>
<tbody>
@foreach (var t in Model)
{
<tr class="@(t.IsActive ? "" : "table-secondary text-muted")">
<td class="fw-semibold">@t.Name</td>
<td>
@if (t.TemplateType == RecurringTemplateType.Bill)
{
<span class="badge bg-primary-subtle text-primary"><i class="bi bi-receipt me-1"></i>Bill</span>
}
else
{
<span class="badge bg-warning-subtle text-warning"><i class="bi bi-credit-card me-1"></i>Expense</span>
}
</td>
<td>
@{
var freqLabel = t.IntervalCount == 1
? t.Frequency.ToString()
: $"Every {t.IntervalCount} × {t.Frequency}";
}
<span class="text-body-secondary small">@freqLabel</span>
</td>
<td>
@if (t.IsActive)
{
var isOverdue = t.NextFireDate.Date < DateTime.Today;
<span class="@(isOverdue ? "text-danger fw-semibold" : "")">
@t.NextFireDate.ToString("MM/dd/yyyy")
@if (isOverdue) { <i class="bi bi-exclamation-circle ms-1"></i> }
</span>
}
else
{
<span class="text-muted">—</span>
}
</td>
<td>
<span class="badge bg-secondary-subtle text-secondary">@t.OccurrenceCount</span>
@if (t.MaxOccurrences.HasValue)
{
<span class="text-muted small"> / @t.MaxOccurrences</span>
}
</td>
<td>
@if (t.IsActive)
{
<span class="badge bg-success"><i class="bi bi-play-fill me-1"></i>Active</span>
}
else
{
<span class="badge bg-secondary"><i class="bi bi-pause-fill me-1"></i>Paused</span>
}
</td>
<td>
@if (!string.IsNullOrWhiteSpace(t.LastError))
{
<span class="text-danger small" title="@t.LastError" data-bs-toggle="tooltip">
<i class="bi bi-exclamation-triangle-fill me-1"></i>Error
</span>
}
</td>
<td class="text-end">
<div class="d-flex gap-1 justify-content-end">
<a asp-action="Edit" asp-route-id="@t.Id" class="btn btn-sm btn-outline-secondary" title="Edit">
<i class="bi bi-pencil"></i>
</a>
<form asp-action="ToggleActive" asp-route-id="@t.Id" method="post">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-sm @(t.IsActive ? "btn-outline-warning" : "btn-outline-success")"
title="@(t.IsActive ? "Pause" : "Resume")">
<i class="bi @(t.IsActive ? "bi-pause" : "bi-play")"></i>
</button>
</form>
@if (t.IsActive)
{
<form asp-action="GenerateNow" asp-route-id="@t.Id" method="post"
onsubmit="return confirm('Generate one occurrence of this template now?')">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-sm btn-outline-primary" title="Generate Now">
<i class="bi bi-lightning-charge"></i>
</button>
</form>
}
<form asp-action="Delete" asp-route-id="@t.Id" method="post"
onsubmit="return confirm('Delete this recurring template? Generated documents will not be affected.')">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-sm btn-outline-danger" title="Delete">
<i class="bi bi-trash"></i>
</button>
</form>
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
<p class="text-muted small mt-2">
<i class="bi bi-info-circle me-1"></i>
The background service checks hourly and auto-generates due templates.
Bills are created as Draft; Expenses are recorded immediately.
<i class="bi bi-lightning-charge ms-2 text-primary"></i> Generate Now fires one occurrence immediately.
</p>
}