feat(init): unify onboarding step widths with shared layout
Replace per-step hardcoded Card widths (384px, 776px, 1024px) with a consistent 1024px shell defined in _constants.ts. All steps now use an InitStepCard wrapper set to w=100%, inheriting width from the page shell. Also updates old-import cards to w=100% for proper width propagation.
This commit is contained in:
1 parent
0e2441167d
commit
1065e30c8d
14 files changed
+55
-31
No files matched your search
@@ -0,0 +1,12 @@
|
||||
import type { PropsWithChildren } from "react";
|
||||
import { Card } from "@mantine/core";
|
||||
|
||||
interface InitStepCardProps {
|
||||
withBorder?: boolean;
|
||||
}
|
||||
|
||||
export const InitStepCard = ({ children, withBorder = true }: PropsWithChildren<InitStepCardProps>) => (
|
||||
<Card w="100%" withBorder={withBorder}>
|
||||
{children}
|
||||
</Card>
|
||||
);
|
||||
@@ -0,0 +1,2 @@
|
||||
export const onboardingContentWidth = 64 * 16;
|
||||
export const onboardingMaxWidth = "90vw";
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { MantineColor } from "@mantine/core";
|
||||
import { Button, Card, Stack, Text } from "@mantine/core";
|
||||
import { Button, Stack, Text } from "@mantine/core";
|
||||
import { IconBook2, IconLayoutDashboard, IconMailForward } from "@tabler/icons-react";
|
||||
|
||||
import { isProviderEnabled } from "@homarr/auth/server";
|
||||
@@ -9,11 +9,13 @@ import { getScopedI18n } from "@homarr/translation/server";
|
||||
import { Link } from "@homarr/ui";
|
||||
import type { TablerIcon } from "@homarr/ui";
|
||||
|
||||
import { InitStepCard } from "../../_components/init-step-card";
|
||||
|
||||
export const InitFinish = async () => {
|
||||
const tFinish = await getScopedI18n("init.step.finish");
|
||||
|
||||
return (
|
||||
<Card w={64 * 6} maw="90vw">
|
||||
<InitStepCard>
|
||||
<Stack>
|
||||
<Text>{tFinish("description")}</Text>
|
||||
|
||||
@@ -37,7 +39,7 @@ export const InitFinish = async () => {
|
||||
{tFinish("action.docs")}
|
||||
</ExternalLinkButton>
|
||||
</Stack>
|
||||
</Card>
|
||||
</InitStepCard>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { Button, Card, Stack, TextInput } from "@mantine/core";
|
||||
import { Button, Stack, TextInput } from "@mantine/core";
|
||||
import { IconArrowRight } from "@tabler/icons-react";
|
||||
import type { z } from "zod/v4";
|
||||
|
||||
@@ -10,6 +10,8 @@ import { useZodForm } from "@homarr/form";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { groupCreateSchema } from "@homarr/validation/group";
|
||||
|
||||
import { InitStepCard } from "../../_components/init-step-card";
|
||||
|
||||
export const InitGroup = () => {
|
||||
const t = useI18n();
|
||||
const { mutateAsync } = clientApi.group.createInitialExternalGroup.useMutation({
|
||||
@@ -34,7 +36,7 @@ export const InitGroup = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Card w={64 * 6} maw="90vw">
|
||||
<InitStepCard>
|
||||
<form onSubmit={form.onSubmit(handleSubmitAsync)}>
|
||||
<Stack>
|
||||
<TextInput
|
||||
@@ -48,6 +50,6 @@ export const InitGroup = () => {
|
||||
</Button>
|
||||
</Stack>
|
||||
</form>
|
||||
</Card>
|
||||
</InitStepCard>
|
||||
);
|
||||
};
|
||||
@@ -1,10 +1,12 @@
|
||||
import { ActionIcon, Button, Card, Group, Text } from "@mantine/core";
|
||||
import { ActionIcon, Button, Group, Text } from "@mantine/core";
|
||||
import type { FileWithPath } from "@mantine/dropzone";
|
||||
import { IconPencil } from "@tabler/icons-react";
|
||||
|
||||
import { humanFileSize } from "@homarr/common";
|
||||
import { useScopedI18n } from "@homarr/translation/client";
|
||||
|
||||
import { InitStepCard } from "../../_components/init-step-card";
|
||||
|
||||
interface FileInfoCardProps {
|
||||
file: FileWithPath;
|
||||
onRemove: () => void;
|
||||
@@ -13,7 +15,7 @@ interface FileInfoCardProps {
|
||||
export const FileInfoCard = ({ file, onRemove }: FileInfoCardProps) => {
|
||||
const tFileInfo = useScopedI18n("init.step.import.fileInfo");
|
||||
return (
|
||||
<Card w={64 * 12 + 8} maw="90vw" withBorder={false}>
|
||||
<InitStepCard withBorder={false}>
|
||||
<Group justify="space-between" align="center" wrap="nowrap">
|
||||
<Group>
|
||||
<Text fw={500} lineClamp={1} style={{ wordBreak: "break-all" }}>
|
||||
@@ -36,6 +38,6 @@ export const FileInfoCard = ({ file, onRemove }: FileInfoCardProps) => {
|
||||
<IconPencil size={16} stroke={1.5} />
|
||||
</ActionIcon>
|
||||
</Group>
|
||||
</Card>
|
||||
</InitStepCard>
|
||||
);
|
||||
};
|
||||
@@ -1,13 +1,14 @@
|
||||
"use client";
|
||||
|
||||
import { startTransition, useState } from "react";
|
||||
import { Card, Stack } from "@mantine/core";
|
||||
import { Stack } from "@mantine/core";
|
||||
import type { FileWithPath } from "@mantine/dropzone";
|
||||
|
||||
import type { RouterOutputs } from "@homarr/api";
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { InitialOldmarrImport } from "@homarr/old-import/components";
|
||||
|
||||
import { InitStepCard } from "../../_components/init-step-card";
|
||||
import { FileInfoCard } from "./file-info-card";
|
||||
import { ImportDropZone } from "./import-dropzone";
|
||||
|
||||
@@ -20,7 +21,7 @@ export const InitImport = () => {
|
||||
|
||||
if (!file) {
|
||||
return (
|
||||
<Card w={64 * 12 + 8} maw="90vw">
|
||||
<InitStepCard>
|
||||
<ImportDropZone
|
||||
loading={isPending}
|
||||
updateFile={(file) => {
|
||||
@@ -40,12 +41,12 @@ export const InitImport = () => {
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</InitStepCard>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack mb="sm">
|
||||
<Stack w="100%" mb="sm">
|
||||
<FileInfoCard file={file} onRemove={() => setFile(null)} />
|
||||
{analyseResult !== null && <InitialOldmarrImport file={file} analyseResult={analyseResult} />}
|
||||
</Stack>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { Button, Card, Group, Stack, Switch, Text } from "@mantine/core";
|
||||
import { Button, Group, Stack, Switch, Text } from "@mantine/core";
|
||||
import { IconArrowRight } from "@tabler/icons-react";
|
||||
import type { z } from "zod/v4";
|
||||
|
||||
@@ -13,6 +13,8 @@ import type { TranslationObject } from "@homarr/translation";
|
||||
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
||||
import { settingsInitSchema } from "@homarr/validation/settings";
|
||||
|
||||
import { InitStepCard } from "../../_components/init-step-card";
|
||||
|
||||
export const InitSettings = () => {
|
||||
const tSection = useScopedI18n("management.page.settings.section");
|
||||
const t = useI18n();
|
||||
@@ -29,8 +31,8 @@ export const InitSettings = () => {
|
||||
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(handleSubmitAsync)}>
|
||||
<Stack>
|
||||
<Card w={64 * 12 + 8} maw="90vw">
|
||||
<Stack w="100%">
|
||||
<InitStepCard>
|
||||
<Stack gap="sm">
|
||||
<Text fw={500}>{tSection("analytics.title")}</Text>
|
||||
|
||||
@@ -38,8 +40,8 @@ export const InitSettings = () => {
|
||||
<AnalyticsRow kind="general" {...form.getInputProps("analytics.enableGeneral", { type: "checkbox" })} />
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Card>
|
||||
<Card w={64 * 12 + 8} maw="90vw">
|
||||
</InitStepCard>
|
||||
<InitStepCard>
|
||||
<Stack gap="sm">
|
||||
<Text fw={500}>{tSection("crawlingAndIndexing.title")}</Text>
|
||||
|
||||
@@ -62,7 +64,7 @@ export const InitSettings = () => {
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Card>
|
||||
</InitStepCard>
|
||||
|
||||
<Button type="submit" loading={form.submitting} rightSection={<IconArrowRight size={16} stroke={1.5} />}>
|
||||
{t("common.action.continue")}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import { Card, Stack, Text } from "@mantine/core";
|
||||
import { Stack, Text } from "@mantine/core";
|
||||
import { IconFileImport, IconPlayerPlay } from "@tabler/icons-react";
|
||||
|
||||
import { getMantineColor } from "@homarr/common";
|
||||
import { getScopedI18n } from "@homarr/translation/server";
|
||||
|
||||
import { InitStepCard } from "../../_components/init-step-card";
|
||||
import { InitStartButton } from "./next-button";
|
||||
|
||||
export const InitStart = async () => {
|
||||
const tStart = await getScopedI18n("init.step.start");
|
||||
|
||||
return (
|
||||
<Card w={64 * 6} maw="90vw">
|
||||
<InitStepCard>
|
||||
<Stack>
|
||||
<Text>{tStart("description")}</Text>
|
||||
|
||||
@@ -27,6 +28,6 @@ export const InitStart = async () => {
|
||||
{tStart("action.importOldmarr")}
|
||||
</InitStartButton>
|
||||
</Stack>
|
||||
</Card>
|
||||
</InitStepCard>
|
||||
);
|
||||
};
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Card } from "@mantine/core";
|
||||
|
||||
import { InitStepCard } from "../../_components/init-step-card";
|
||||
import { InitUserForm } from "./init-user-form";
|
||||
|
||||
export const InitUser = () => {
|
||||
return (
|
||||
<Card w={64 * 6} maw="90vw">
|
||||
<InitStepCard>
|
||||
<InitUserForm />
|
||||
</Card>
|
||||
</InitStepCard>
|
||||
);
|
||||
};
|
||||
@@ -9,6 +9,7 @@ import { getScopedI18n } from "@homarr/translation/server";
|
||||
import { CurrentColorSchemeCombobox } from "~/components/color-scheme/current-color-scheme-combobox";
|
||||
import { CurrentLanguageCombobox } from "~/components/language/current-language-combobox";
|
||||
import { HomarrLogoWithTitle } from "~/components/layout/logo/homarr-logo";
|
||||
import { onboardingContentWidth, onboardingMaxWidth } from "./_constants";
|
||||
import { BackToStart } from "./_steps/back";
|
||||
import { InitFinish } from "./_steps/finish/init-finish";
|
||||
import { InitGroup } from "./_steps/group/init-group";
|
||||
@@ -37,7 +38,7 @@ export default async function InitPage() {
|
||||
return (
|
||||
<Box mih="100dvh">
|
||||
<Center>
|
||||
<Stack align="center" mt="xl">
|
||||
<Stack align="center" mt="xl" w={onboardingContentWidth} maw={onboardingMaxWidth}>
|
||||
<HomarrLogoWithTitle size="lg" />
|
||||
<Stack gap={6} align="center">
|
||||
<Title order={3} fw={400} ta="center">
|
||||
|
||||
@@ -69,7 +69,7 @@ export const InitialOldmarrImport = ({ file, analyseResult }: InitialOldmarrImpo
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack mb="sm">
|
||||
<Stack w="100%" mb="sm">
|
||||
<ImportSettingsCard
|
||||
settings={settings}
|
||||
updateSetting={(setting, value) => {
|
||||
|
||||
@@ -38,7 +38,7 @@ export const BoardSelectionCard = ({ selections, updateSelections }: BoardSelect
|
||||
}
|
||||
|
||||
return (
|
||||
<Card w={64 * 12 + 8} maw="90vw" withBorder={false}>
|
||||
<Card w="100%" withBorder={false}>
|
||||
<Stack gap="sm">
|
||||
<Stack gap={0}>
|
||||
<Group justify="space-between" align="center">
|
||||
|
||||
@@ -17,7 +17,7 @@ interface ImportSettingsCardProps {
|
||||
export const ImportSettingsCard = ({ settings, updateSetting }: ImportSettingsCardProps) => {
|
||||
const tImportSettings = useScopedI18n("init.step.import.importSettings");
|
||||
return (
|
||||
<Card w={64 * 12 + 8} maw="90vw" withBorder={false}>
|
||||
<Card w="100%" withBorder={false}>
|
||||
<Stack gap="sm">
|
||||
<Stack gap={0}>
|
||||
<Text fw={500}>{tImportSettings("title")}</Text>
|
||||
|
||||
@@ -13,7 +13,7 @@ interface ImportSummaryCardProps {
|
||||
export const ImportSummaryCard = ({ counts, onSubmit, loading }: ImportSummaryCardProps) => {
|
||||
const tSummary = useScopedI18n("init.step.import.summary");
|
||||
return (
|
||||
<Card w={64 * 12 + 8} maw="90vw" withBorder={false}>
|
||||
<Card w="100%" withBorder={false}>
|
||||
<Stack gap="sm">
|
||||
<Stack gap={0}>
|
||||
<Text fw={500}>{tSummary("title")}</Text>
|
||||
|
||||
Reference in new issue
Block a user