fix(onboarding): return to welcome before administrator creation (#7012)
This commit is contained in:
1 parent
b40f000dd5
commit
83ae98847a
3 files changed
+55
-6
No files matched your search
@@ -16,7 +16,9 @@ import {
|
||||
} from "@tabler/icons-react";
|
||||
|
||||
After Homarr starts, open its URL. An unfinished installation redirects to the onboarding screen at `/init`. Choose
|
||||
**Get started** for a new installation or **Restore backup** to import a compatible SQLite backup.
|
||||
**Get started** for a new installation or **Restore backup** to import a compatible SQLite backup. Before creating the
|
||||
administrator, you can return to Welcome with **Back**, including after refreshing `/init`. Creating the administrator
|
||||
commits this setup stage; later steps do not rewind it.
|
||||
|
||||
<Callout type="info" title="Persist /appdata before you continue">
|
||||
Onboarding writes the administrator and board configuration to the database. If onboarding returns after a restart,
|
||||
|
||||
@@ -8,7 +8,7 @@ import { createId } from "@homarr/common";
|
||||
import { encryptSecret } from "@homarr/common/server";
|
||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
||||
import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error";
|
||||
import { handleTransactionsAsync, eq, inArray, like, or } from "@homarr/db";
|
||||
import { handleTransactionsAsync, and, eq, inArray, like, notExists, or } from "@homarr/db";
|
||||
import { getServerSettingByKeyAsync } from "@homarr/db/queries";
|
||||
import {
|
||||
apps,
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
sectionLayouts,
|
||||
sections,
|
||||
serverSettings,
|
||||
users,
|
||||
} from "@homarr/db/schema";
|
||||
import {
|
||||
emptySuperJSON,
|
||||
@@ -147,6 +148,23 @@ export const onboardRouter = createTRPCRouter({
|
||||
await nextOnboardingStepAsync(ctx.db);
|
||||
}),
|
||||
|
||||
returnToWelcome: onboardingProcedure.requiresStep("user").mutation(async ({ ctx }) => {
|
||||
const returnedRows = await ctx.db
|
||||
.update(onboarding)
|
||||
.set({ step: "start", previousStep: null })
|
||||
.where(
|
||||
and(
|
||||
eq(onboarding.step, "user"),
|
||||
eq(onboarding.previousStep, "start"),
|
||||
notExists(ctx.db.select({ id: users.id }).from(users)),
|
||||
),
|
||||
)
|
||||
.returning({ id: onboarding.id });
|
||||
if (returnedRows.length !== 1) {
|
||||
throw new TRPCError({ code: "PRECONDITION_FAILED", message: "Administrator setup can no longer be restarted." });
|
||||
}
|
||||
}),
|
||||
|
||||
testIntegration: onboardingProcedure
|
||||
.requiresStep("setup")
|
||||
.input(onboardingIntegrationDraftSchema)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Alert, Button, Stack, Text, TextInput } from "@mantine/core";
|
||||
import { IconArrowRight, IconShieldCheck, IconUserPlus } from "@tabler/icons-react";
|
||||
import { IconArrowLeft, IconArrowRight, IconShieldCheck, IconUserPlus } from "@tabler/icons-react";
|
||||
import type { z } from "zod/v4";
|
||||
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
@@ -36,11 +36,12 @@ const AccountShell = ({
|
||||
);
|
||||
|
||||
export const AccountSetup = ({ environment }: OnboardingStudioProps) => {
|
||||
if (environment.currentStep === "user") return <CredentialsSetup />;
|
||||
if (environment.currentStep === "user") return <CredentialsSetup canReturnToWelcome={!environment.hasUsers} />;
|
||||
return <ExternalGroupSetup />;
|
||||
};
|
||||
|
||||
const CredentialsSetup = () => {
|
||||
const CredentialsSetup = ({ canReturnToWelcome }: { canReturnToWelcome: boolean }) => {
|
||||
const navigationT = useI18n("init.studio");
|
||||
const t = useI18n("init.studio.account");
|
||||
const [requiresSignIn, setRequiresSignIn] = useState(false);
|
||||
const [submitError, setSubmitError] = useState<string | null>(null);
|
||||
@@ -54,6 +55,16 @@ const CredentialsSetup = () => {
|
||||
showErrorNotification({ title: t("errorTitle"), message });
|
||||
},
|
||||
});
|
||||
const returnToWelcome = clientApi.onboard.returnToWelcome.useMutation({
|
||||
async onSuccess() {
|
||||
sounds.swoosh();
|
||||
await revalidatePathActionAsync("/init");
|
||||
},
|
||||
onError() {
|
||||
sounds.error();
|
||||
showErrorNotification({ title: t("errorTitle"), message: t("unknownError") });
|
||||
},
|
||||
});
|
||||
const form = useZodForm(userInitSchema, {
|
||||
initialValues: { username: "", password: "", confirmPassword: "" },
|
||||
});
|
||||
@@ -119,9 +130,27 @@ const CredentialsSetup = () => {
|
||||
{submitError}
|
||||
</Alert>
|
||||
) : null}
|
||||
<Button type="submit" size="md" loading={form.submitting} rightSection={<IconArrowRight size={18} />}>
|
||||
<Button
|
||||
type="submit"
|
||||
size="md"
|
||||
loading={form.submitting}
|
||||
disabled={returnToWelcome.isPending}
|
||||
rightSection={<IconArrowRight size={18} />}
|
||||
>
|
||||
{t("create")}
|
||||
</Button>
|
||||
{canReturnToWelcome && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="default"
|
||||
leftSection={<IconArrowLeft size={18} />}
|
||||
loading={returnToWelcome.isPending}
|
||||
disabled={form.submitting}
|
||||
onClick={() => returnToWelcome.mutate()}
|
||||
>
|
||||
{navigationT("back")}
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
</form>
|
||||
</AccountShell>
|
||||
|
||||
Reference in new issue
Block a user