Skip to content

Commit

Permalink
feat: add color scheme and language option to onboarding, add previou…
Browse files Browse the repository at this point in the history
…s button
  • Loading branch information
Meierschlumpf committed Dec 11, 2024
1 parent cac88df commit d1018f5
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 23 deletions.
23 changes: 23 additions & 0 deletions apps/nextjs/src/app/[locale]/init/_steps/back.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"use client";

import { Button } from "@mantine/core";

import { clientApi } from "@homarr/api/client";
import { revalidatePathActionAsync } from "@homarr/common/client";
import { useI18n } from "@homarr/translation/client";

export const BackToStart = () => {
const t = useI18n();
const { mutateAsync, isPending } = clientApi.onboard.previousStep.useMutation();

const handleBackToStartAsync = async () => {
await mutateAsync();
await revalidatePathActionAsync("/init");
};

return (
<Button loading={isPending} variant="subtle" color="gray" fullWidth onClick={handleBackToStartAsync}>
{t("init.backToStart")}
</Button>
);
};
38 changes: 23 additions & 15 deletions apps/nextjs/src/app/[locale]/init/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import type { JSX } from "react";
import { Center, Stack, Text, Title } from "@mantine/core";
import { Box, Center, Stack, Text, Title } from "@mantine/core";

import { api } from "@homarr/api/server";
import type { MaybePromise } from "@homarr/common/types";
import type { OnboardingStep } from "@homarr/definitions";
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 { BackToStart } from "./_steps/back";
import { InitFinish } from "./_steps/finish/init-finish";
import { InitGroup } from "./_steps/group/init-group";
import { InitImport } from "./_steps/import/init-import";
Expand All @@ -27,22 +30,27 @@ export default async function InitPage() {
const t = await getScopedI18n("init.step");
const currentStep = await api.onboard.currentStep();

const CurrentComponent = stepComponents[currentStep];
const CurrentComponent = stepComponents[currentStep.current];

return (
<Center>
<Stack align="center" mt="xl">
<HomarrLogoWithTitle size="lg" />
<Stack gap={6} align="center">
<Title order={3} fw={400} ta="center">
{t(`${currentStep}.title`)}
</Title>
<Text size="sm" c="gray.5" ta="center">
{t(`${currentStep}.subtitle`)}
</Text>
<Box mih="100dvh">
<Center>
<Stack align="center" mt="xl">
<HomarrLogoWithTitle size="lg" />
<Stack gap={6} align="center">
<Title order={3} fw={400} ta="center">
{t(`${currentStep.current}.title`)}
</Title>
<Text size="sm" c="gray.5" ta="center">
{t(`${currentStep.current}.subtitle`)}
</Text>
</Stack>
<CurrentLanguageCombobox w="100%" />
<CurrentColorSchemeCombobox w="100%" />
{CurrentComponent && <CurrentComponent />}
{currentStep.previous === "start" && <BackToStart />}
</Stack>
{CurrentComponent && <CurrentComponent />}
</Stack>
</Center>
</Center>
</Box>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"use client";

import { Group, Text, useMantineColorScheme } from "@mantine/core";
import { IconMoon, IconSun } from "@tabler/icons-react";

import type { ColorScheme } from "@homarr/definitions";
import { colorSchemes } from "@homarr/definitions";
import { useScopedI18n } from "@homarr/translation/client";
import { SelectWithCustomItems } from "@homarr/ui";

interface CurrentColorSchemeComboboxProps {
w?: string;
}

export const CurrentColorSchemeCombobox = ({ w }: CurrentColorSchemeComboboxProps) => {
const tOptions = useScopedI18n("common.colorScheme.options");
const { colorScheme, setColorScheme } = useMantineColorScheme();

return (
<SelectWithCustomItems
value={colorScheme}
onChange={(value) => setColorScheme((value as ColorScheme | null) ?? "light")}
data={colorSchemes.map((scheme) => ({
value: scheme,
label: tOptions(scheme),
}))}
SelectOption={ColorSchemeCustomOption}
w={w}
/>
);
};

const appearanceIcons = {
light: IconSun,
dark: IconMoon,
};

const ColorSchemeCustomOption = ({ value, label }: { value: ColorScheme; label: string }) => {
const Icon = appearanceIcons[value];

return (
<Group>
<Icon size={16} stroke={1.5} />
<Text fz="sm" fw={500}>
{label}
</Text>
</Group>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ import { useChangeLocale, useCurrentLocale } from "@homarr/translation/client";

import { LanguageCombobox } from "./language-combobox";

export const CurrentLanguageCombobox = () => {
interface CurrentLanguageComboboxProps {
w?: string;
}

export const CurrentLanguageCombobox = ({ w }: CurrentLanguageComboboxProps) => {
const currentLocale = useCurrentLocale();
const { changeLocale, isPending } = useChangeLocale();

return <LanguageCombobox value={currentLocale} onChange={changeLocale} isPending={isPending} />;
return <LanguageCombobox value={currentLocale} onChange={changeLocale} isPending={isPending} w={w} />;
};
6 changes: 5 additions & 1 deletion apps/nextjs/src/components/language/language-combobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ import { localeConfigurations, supportedLanguages } from "@homarr/translation";

import classes from "./language-combobox.module.css";

import "flag-icons/css/flag-icons.min.css";

interface LanguageComboboxProps {
label?: string;
value: SupportedLanguage;
onChange: (value: SupportedLanguage) => void;
isPending?: boolean;
w?: string;
}

export const LanguageCombobox = ({ label, value, onChange, isPending }: LanguageComboboxProps) => {
export const LanguageCombobox = ({ label, value, onChange, isPending, w }: LanguageComboboxProps) => {
const combobox = useCombobox({
onDropdownClose: () => combobox.resetSelectedOption(),
});
Expand Down Expand Up @@ -49,6 +52,7 @@ export const LanguageCombobox = ({ label, value, onChange, isPending }: Language
rightSectionPointerEvents="none"
onClick={handleOnClick}
variant="filled"
w={w}
>
<OptionItem currentLocale={value} localeKey={value} />
</InputBase>
Expand Down
2 changes: 0 additions & 2 deletions apps/nextjs/src/components/user-avatar-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ import { signOut, useSession } from "@homarr/auth/client";
import { createModal, useModalAction } from "@homarr/modals";
import { useScopedI18n } from "@homarr/translation/client";

import "flag-icons/css/flag-icons.min.css";

import { useAuthContext } from "~/app/[locale]/_client-providers/session";
import { CurrentLanguageCombobox } from "./language/current-language-combobox";

Expand Down
2 changes: 1 addition & 1 deletion apps/nextjs/src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export async function middleware(request: NextRequest) {
const pathname = request.nextUrl.pathname;
if (!pathname.endsWith("/init")) {
const currentOnboardingStep = await serverFetchApi.onboard.currentStep.query();
if (currentOnboardingStep !== "finish") {
if (currentOnboardingStep.current !== "finish") {
return NextResponse.redirect(new URL("/init", request.url));
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/api/src/router/onboard/onboard-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { getOnboardingOrFallbackAsync, nextOnboardingStepAsync } from "./onboard

export const onboardRouter = createTRPCRouter({
currentStep: publicProcedure.query(async ({ ctx }) => {
return await getOnboardingOrFallbackAsync(ctx.db).then(({ current }) => current);
return await getOnboardingOrFallbackAsync(ctx.db);
}),
nextStep: publicProcedure
.input(
Expand Down
9 changes: 8 additions & 1 deletion packages/translation/src/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@
"docs": "Read the documentation"
}
}
}
},
"backToStart": "Back to start"
},
"user": {
"title": "Users",
Expand Down Expand Up @@ -799,6 +800,12 @@
"label": "Icon URL",
"header": "Type name or objects to filter for icons... Homarr will search through {countIcons} icons for you."
},
"colorScheme": {
"options": {
"light": "Light",
"dark": "Dark"
}
},
"information": {
"min": "Min",
"max": "Max",
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/src/components/select-with-custom-items.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface SelectWithCustomItemsProps<TSelectItem extends BaseSelectItem>
withAsterisk?: boolean;
onBlur?: (event: React.FocusEvent<HTMLButtonElement>) => void;
onFocus?: (event: React.FocusEvent<HTMLButtonElement>) => void;
w?: string;
}

type Props<TSelectItem extends BaseSelectItem> = SelectWithCustomItemsProps<TSelectItem> & {
Expand All @@ -30,6 +31,7 @@ export const SelectWithCustomItems = <TSelectItem extends BaseSelectItem>({
defaultValue,
placeholder,
SelectOption,
w,
...props
}: Props<TSelectItem>) => {
const combobox = useCombobox({
Expand Down Expand Up @@ -75,6 +77,7 @@ export const SelectWithCustomItems = <TSelectItem extends BaseSelectItem>({
onClick={toggle}
rightSectionPointerEvents="none"
multiline
w={w}
>
{selectedOption ? <SelectOption {...selectedOption} /> : <Input.Placeholder>{placeholder}</Input.Placeholder>}
</InputBase>
Expand Down

0 comments on commit d1018f5

Please sign in to comment.