- Case numbers SUP-000001, SUP-000002 … per customer, unique per customer (new index; a migration step renames any old duplicates first). The portal suggests the next number on the finding page and under the new "Ny sag" on the customer page; an empty field gets the next number, a typed one that is taken is refused. Concurrent creates retry. - SupportCase gains Source (Staff/Customer), description, contact name, e-mail, phone, the customer's Windows user and ClientRequestId. - POST/GET /api/v1/cases for the customer's agent (license with priority support): creates a case idempotently on ClientRequestId, links the finding it came from, lists the customer's cases incl. staff-created. Customer/license creation is shared with the findings endpoint. - Optional mail to support for customer cases (MailKit; prefers AUTH LOGIN as Microsoft 365 needs). A failed mail is logged, never fails the call. deploy-hub.yml writes optional SUPPORT_SMTP_* secrets to .env. - Supportrapport: created, closed, open now, average/median resolution time, oldest open case and customer-created share per customer for a period (default 90 days), plus the cases; CSV for Danish Excel. - Cases page: newest first, customer cases marked, contact/description. - Customer page: findings can be filtered to one instance (?instans=id, ignored for another customer's instance); active findings per instance by severity. - dotnet-ef as a local tool (dotnet-tools.json) for the migration. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
38 lines
1.7 KiB
Text
38 lines
1.7 KiB
Text
@page "/portal/sager"
|
|
@attribute [Authorize]
|
|
@inject PortalQueries Queries
|
|
|
|
<PageTitle>Supportsager · SQLInsight Hub</PageTitle>
|
|
<h1>Åbne supportsager</h1>
|
|
<p class="lead">Nyeste øverst. Sager, kunden selv har oprettet i SQLInsight Web, er markeret <span class="badge">Kunde</span>. <a href="/portal/rapporter/support">Supportrapport →</a></p>
|
|
<section class="card">
|
|
@if (_cases.Count == 0)
|
|
{
|
|
<p class="empty">Ingen åbne sager.</p>
|
|
}
|
|
else
|
|
{
|
|
<table>
|
|
<thead><tr><th>Sag</th><th>Kunde</th><th>Titel</th><th class="num">Fund</th><th>Aktive</th><th>Oprettet</th><th></th></tr></thead>
|
|
<tbody>
|
|
@foreach (var c in _cases)
|
|
{
|
|
<tr>
|
|
<td class="mono nowrap">@c.Reference @if (c.Source == SQLInsight.Contracts.SupportCaseSources.Customer) { <span class="badge">Kunde</span> }</td>
|
|
<td><a href="/portal/kunder/@c.CustomerId">@c.Customer!.Name</a></td>
|
|
<td>@c.Title<CaseDetails Case="c" /></td>
|
|
<td class="num">@c.Findings.Count</td>
|
|
<td>@c.Findings.Count(f => f.ResolvedAt == null)</td>
|
|
<td class="nowrap">@c.CreatedAt.ToString("yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture) UTC</td>
|
|
<td><form method="post" action="/portal/actions/cases/@c.Id/close"><AntiforgeryToken /><button type="submit" class="theme-toggle">Luk</button></form></td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
}
|
|
</section>
|
|
|
|
@code {
|
|
private IReadOnlyList<SupportCase> _cases = [];
|
|
protected override async Task OnInitializedAsync() => _cases = await Queries.OpenCasesAsync();
|
|
}
|