Files
Moonlight/Moonlight.Frontend/UI/Admin/Views/Sys/Diagnose.razor

229 lines
12 KiB
Plaintext

@using LucideBlazor
@using Moonlight.Shared.Http.Responses.Admin
@using ShadcnBlazor.Accordions
@using ShadcnBlazor.Alerts
@using ShadcnBlazor.Buttons
@using ShadcnBlazor.Cards
@using ShadcnBlazor.Emptys
@using ShadcnBlazor.Extras.Common
@using ShadcnBlazor.Spinners
@inject HttpClient HttpClient
<div class="grid grid-cols-1 xl:grid-cols-2 gap-5 mt-5">
<div class="col-span-1">
<Card>
<CardHeader>
<CardTitle>Automatic diagnosis</CardTitle>
<CardDescription>
Use a diagnostic report to share configuration details and errors with Moonlight developers, with
sensitive data automatically censored.
</CardDescription>
</CardHeader>
<CardContent ClassName="flex flex-col gap-y-5">
<Alert
ClassName="w-full flex flex-row items-center gap-3 border-yellow-500/80 bg-yellow-500/5 text-yellow-500">
<div class="flex shrink-0 items-center">
<TriangleAlertIcon ClassName="size-6 text-yellow-500/60"/>
</div>
<div class="flex flex-1 items-center justify-between gap-4">
<div class="flex flex-col gap-0.5">
<AlertTitle>Notice</AlertTitle>
<AlertDescription ClassName="text-yellow-500/80">
Only share these reports with the moonlight developers or the corresponding plugin
developers.
Even though we do our best to censor sensitive data it may still contain information you
dont want a random person on the internet to know
</AlertDescription>
</div>
</div>
</Alert>
</CardContent>
<CardFooter ClassName="justify-end">
<WButtom OnClick="DiagnoseAsync">
<StethoscopeIcon/>
Start diagnostics
</WButtom>
</CardFooter>
</Card>
</div>
<div class="col-span-1">
<Card>
<CardContent ClassName="flex justify-center items-center">
@if (IsLoading)
{
<Spinner ClassName="size-10"/>
}
else
{
if (HasDiagnosed)
{
if (Entries.Length == 0)
{
<Empty>
<EmptyHeader>
<EmptyMedia Variant="EmptyMediaVariant.Icon">
<SearchIcon/>
</EmptyMedia>
<EmptyTitle>No results available</EmptyTitle>
<EmptyDescription>
Diagnosis didnt return any results
</EmptyDescription>
</EmptyHeader>
</Empty>
}
else
{
<Accordion
ClassName="w-full"
Type="AccordionType.Single">
@for (var i = 0; i < Entries.Length; i++)
{
var entry = Entries[i];
var textColor = entry.Level switch
{
DiagnoseLevel.Error => "text-destructive",
DiagnoseLevel.Warning => "text-yellow-300",
DiagnoseLevel.Healthy => "text-green-500"
};
<AccordionItem
ClassName="overflow-hidden border bg-background px-4 first:rounded-t-lg last:rounded-b-lg last:border-b"
Value="@($"diagnoseEntry{i}")">
<AccordionTrigger className="hover:no-underline">
<div class="flex items-center gap-3">
@switch (entry.Level)
{
case DiagnoseLevel.Error:
<CircleXIcon ClassName="@textColor"/>
break;
case DiagnoseLevel.Warning:
<TriangleAlertIcon ClassName="@textColor"/>
break;
case DiagnoseLevel.Healthy:
<CircleCheckIcon ClassName="@textColor"/>
break;
}
<div class="flex flex-col items-start text-left">
<span class="@textColor">
@entry.Title
</span>
<span class="text-sm text-muted-foreground">
@(string.Join(" / ", entry.Tags))
</span>
</div>
</div>
</AccordionTrigger>
<AccordionContent ClassName="ps-7">
<div class="text-muted-foreground flex flex-col gap-y-3">
@if (!string.IsNullOrWhiteSpace(entry.StackStrace))
{
<div
class="rounded-xl p-2.5 bg-black max-h-36 overflow-auto scrollbar-thin">
@entry.StackStrace
</div>
}
@if (!string.IsNullOrWhiteSpace(entry.Message))
{
<p>
@entry.Message
</p>
}
@if (
!string.IsNullOrWhiteSpace(entry.ReportUrl) ||
!string.IsNullOrWhiteSpace(entry.StackStrace) ||
!string.IsNullOrWhiteSpace(entry.SolutionUrl)
)
{
<div class="flex justify-end gap-x-1">
@if (!string.IsNullOrWhiteSpace(entry.StackStrace))
{
<Button Variant="ButtonVariant.Outline">
<CopyIcon/>
Copy
</Button>
}
@if (!string.IsNullOrWhiteSpace(entry.SolutionUrl))
{
<Button Variant="ButtonVariant.Outline">
<Slot>
<a href="@entry.SolutionUrl" @attributes="context">
<WrenchIcon/>
Show suggested solution
</a>
</Slot>
</Button>
}
@if (!string.IsNullOrWhiteSpace(entry.ReportUrl))
{
<Button Variant="ButtonVariant.Outline">
<Slot>
<a href="@entry.ReportUrl" @attributes="context">
<GitBranchIcon/>
Report on Github
</a>
</Slot>
</Button>
}
</div>
}
</div>
</AccordionContent>
</AccordionItem>
}
</Accordion>
}
}
else
{
<Empty>
<EmptyHeader>
<EmptyMedia Variant="EmptyMediaVariant.Icon">
<CircleQuestionMarkIcon/>
</EmptyMedia>
<EmptyTitle>No results available yet</EmptyTitle>
<EmptyDescription>
Press the start button to start the automatic diagnosis
</EmptyDescription>
</EmptyHeader>
</Empty>
}
}
</CardContent>
</Card>
</div>
</div>
@code
{
private bool IsLoading = false;
private bool HasDiagnosed = false;
private DiagnoseResultResponse[] Entries;
private async Task DiagnoseAsync()
{
IsLoading = true;
HasDiagnosed = false;
await InvokeAsync(StateHasChanged);
var results = await HttpClient.GetFromJsonAsync<DiagnoseResultResponse[]>("api/admin/system/diagnose");
Entries = results ?? [];
IsLoading = false;
HasDiagnosed = true;
await InvokeAsync(StateHasChanged);
}
}