Added setup wizard component for initial installation flow and integrated it into app routing.

This commit is contained in:
2026-01-21 17:01:32 +01:00
parent c8fe11bd2b
commit b8e1bbb28c
2 changed files with 182 additions and 23 deletions

View File

@@ -2,10 +2,13 @@
@using LucideBlazor @using LucideBlazor
@using Microsoft.AspNetCore.Components.Authorization @using Microsoft.AspNetCore.Components.Authorization
@using Moonlight.Frontend.UI.Shared @using Moonlight.Frontend.UI.Shared
@using Moonlight.Frontend.UI.Shared.Components
@using ShadcnBlazor.Emptys @using ShadcnBlazor.Emptys
@using Moonlight.Frontend.UI.Shared.Components.Auth @using Moonlight.Frontend.UI.Shared.Components.Auth
@using Moonlight.Frontend.UI.Shared.Partials @using Moonlight.Frontend.UI.Shared.Partials
@inject NavigationManager Navigation
<ErrorBoundary> <ErrorBoundary>
<ChildContent> <ChildContent>
<AuthorizeView> <AuthorizeView>
@@ -30,33 +33,42 @@
} }
else else
{ {
<Authentication/> var uri = new Uri(Navigation.Uri);
if (uri.LocalPath.StartsWith("/setup"))
{
<Setup />
}
else
{
<Authentication/>
}
} }
</NotAuthorized> </NotAuthorized>
</AuthorizeView> </AuthorizeView>
</ChildContent> </ChildContent>
<ErrorContent> <ErrorContent>
@if (context is HttpRequestException { StatusCode: HttpStatusCode.Unauthorized }) @if (context is HttpRequestException { StatusCode: HttpStatusCode.Unauthorized })
{ {
<Authentication/> <Authentication/>
} }
else else
{ {
<div class="m-10"> <div class="m-10">
<Empty> <Empty>
<EmptyHeader> <EmptyHeader>
<EmptyMedia Variant="EmptyMediaVariant.Icon"> <EmptyMedia Variant="EmptyMediaVariant.Icon">
<OctagonAlertIcon ClassName="text-red-500/80"/> <OctagonAlertIcon ClassName="text-red-500/80"/>
</EmptyMedia> </EmptyMedia>
<EmptyTitle> <EmptyTitle>
Critical Application Error Critical Application Error
</EmptyTitle> </EmptyTitle>
<EmptyDescription> <EmptyDescription>
@context.ToString() @context.ToString()
</EmptyDescription> </EmptyDescription>
</EmptyHeader> </EmptyHeader>
</Empty> </Empty>
</div> </div>
} }
</ErrorContent> </ErrorContent>
</ErrorBoundary> </ErrorBoundary>

View File

@@ -0,0 +1,147 @@
@using LucideBlazor
@using ShadcnBlazor.Cards
@using ShadcnBlazor.Spinners
@using ShadcnBlazor.Buttons
@using ShadcnBlazor.Inputs
@using ShadcnBlazor.Labels
<div class="h-screen w-full flex items-center justify-center">
<Card ClassName="w-full max-w-[calc(100%-2rem)] lg:max-w-xl grid gap-4 p-6">
@if (!IsLoaded)
{
<div class="space-y-6">
@if (CurrentStep == 0)
{
<div
class="flex h-56 items-center justify-center rounded-lg bg-linear-to-br from-gray-100 to-gray-300 dark:from-gray-800 dark:to-gray-900">
<img alt="Moonlight Logo" class="size-34" src="/_content/Moonlight.Frontend/logo.svg" />
</div>
<div class="space-y-2 text-center">
<h2 class="text-2xl font-bold">Welcome to Moonlight Panel</h2>
<p class="text-muted-foreground">
You successfully installed moonlight. Now you are ready to perform some initial steps to complete your installation
</p>
</div>
}
else if (CurrentStep == 1)
{
<div
class="flex h-56 items-center justify-center rounded-lg bg-linear-to-br from-gray-100 to-gray-300 dark:from-gray-800 dark:to-gray-900">
<UserIcon ClassName="size-34 text-blue-500" />
</div>
<div class="space-y-2 text-center">
<h2 class="text-2xl font-bold">Admin Account Creation</h2>
<p class="text-muted-foreground">
To continue please fill in the account details of the user you want to use as the initial administrator account.
If you use an external OIDC provider, these details need to match with your desired OIDC account
</p>
</div>
<div class="grid grid-cols-1 gap-5">
<div class="grid gap-2">
<Label for="username">Username</Label>
<InputField
@bind-Value="Username"
id="username"
placeholder="someoneelse"/>
</div>
<div class="grid gap-2">
<Label for="email">Email</Label>
<InputField
@bind-Value="Email"
id="email"
Type="email"
placeholder="a@cool.email"/>
</div>
<div class="grid gap-2">
<Label for="password">Password</Label>
<InputField
@bind-Value="Password"
id="password"
Type="password"
placeholder="......."/>
</div>
</div>
}
else if (CurrentStep == 2)
{
<div
class="flex h-56 items-center justify-center rounded-lg bg-linear-to-br from-gray-100 to-gray-300 dark:from-gray-800 dark:to-gray-900">
<RocketIcon ClassName="size-34 text-blue-500" />
</div>
<div class="space-y-2 text-center">
<h2 class="text-2xl font-bold">You are all set!</h2>
<p class="text-muted-foreground">
You are now ready to finish the initial setup.
The configured options will be applied to the instance.
You will be redirected to the login after changes have been applied successfully
</p>
</div>
}
<div class="flex items-center justify-between">
<div class="flex gap-2">
@for (var step = 0; step < StepCount; step++)
{
if (step == CurrentStep)
{
<div class="h-2 w-2 rounded-full transition-colors bg-foreground">
</div>
}
else
{
<div class="h-2 w-2 rounded-full transition-colors bg-muted">
</div>
}
}
</div>
<div class="flex gap-1.5">
@if (CurrentStep > 0)
{
<Button @onclick="() => Navigate(-1)" Variant="ButtonVariant.Outline">
<ChevronLeftIcon />
Back
</Button>
}
@if (CurrentStep != StepCount - 1)
{
<Button @onclick="() => Navigate(1)">
Continue
<ArrowRightIcon/>
</Button>
}
else
{
<Button>
Finish
<RocketIcon />
</Button>
}
</div>
</div>
</div>
}
else
{
<div class="w-full flex justify-center items-center">
<Spinner ClassName="size-10"/>
</div>
}
</Card>
</div>
@code
{
private bool IsLoaded = false;
private int CurrentStep = 0;
private int StepCount = 3;
private string Email;
private string Username;
private string Password;
private void Navigate(int diff) => CurrentStep += diff;
}