feat: add widget preview pages (#9)
* feat: add widget definition system * fix: wrong typecheck command in turbo generator * chore: fix formatting * feat: add widget preview page * chore: fix formatting and type errors * chore: fix from widget edit modal and remove some never casts * chore: address pull request feedback
This commit is contained in:
15
apps/nextjs/src/app/[locale]/(main)/layout.tsx
Normal file
15
apps/nextjs/src/app/[locale]/(main)/layout.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { PropsWithChildren } from "react";
|
||||
|
||||
import { AppShellMain } from "@homarr/ui";
|
||||
|
||||
import { MainHeader } from "~/components/layout/header";
|
||||
import { ClientShell } from "~/components/layout/shell";
|
||||
|
||||
export default function MainLayout({ children }: PropsWithChildren) {
|
||||
return (
|
||||
<ClientShell hasNavigation={false}>
|
||||
<MainHeader />
|
||||
<AppShellMain>{children}</AppShellMain>
|
||||
</ClientShell>
|
||||
);
|
||||
}
|
||||
9
apps/nextjs/src/app/[locale]/(main)/page.tsx
Normal file
9
apps/nextjs/src/app/[locale]/(main)/page.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Stack, Title } from "@homarr/ui";
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<Stack>
|
||||
<Title>Home</Title>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import type { PropsWithChildren } from "react";
|
||||
|
||||
import { ModalsManager } from "../modals";
|
||||
|
||||
export const ModalsProvider = ({ children }: PropsWithChildren) => {
|
||||
return <ModalsManager>{children}</ModalsManager>;
|
||||
};
|
||||
@@ -10,7 +10,7 @@ export default async function Login() {
|
||||
return (
|
||||
<Center>
|
||||
<Stack align="center" mt="xl">
|
||||
<LogoWithTitle />
|
||||
<LogoWithTitle size="lg" />
|
||||
<Stack gap={6} align="center">
|
||||
<Title order={3} fw={400} ta="center">
|
||||
{t("title")}
|
||||
|
||||
@@ -23,7 +23,7 @@ export default async function InitUser() {
|
||||
return (
|
||||
<Center>
|
||||
<Stack align="center" mt="xl">
|
||||
<LogoWithTitle />
|
||||
<LogoWithTitle size="lg" />
|
||||
<Stack gap={6} align="center">
|
||||
<Title order={3} fw={400} ta="center">
|
||||
{t("title")}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Inter } from "next/font/google";
|
||||
|
||||
import "@homarr/ui/styles.css";
|
||||
import "@homarr/notifications/styles.css";
|
||||
import "@homarr/spotlight/styles.css";
|
||||
|
||||
import { headers } from "next/headers";
|
||||
|
||||
@@ -13,6 +14,7 @@ import {
|
||||
uiConfiguration,
|
||||
} from "@homarr/ui";
|
||||
|
||||
import { ModalsProvider } from "./_client-providers/modals";
|
||||
import { NextInternationalProvider } from "./_client-providers/next-international";
|
||||
import { TRPCReactProvider } from "./_client-providers/trpc";
|
||||
|
||||
@@ -51,8 +53,10 @@ export default function Layout(props: {
|
||||
defaultColorScheme={colorScheme}
|
||||
{...uiConfiguration}
|
||||
>
|
||||
<Notifications />
|
||||
{props.children}
|
||||
<ModalsProvider>
|
||||
<Notifications />
|
||||
{props.children}
|
||||
</ModalsProvider>
|
||||
</MantineProvider>
|
||||
</NextInternationalProvider>
|
||||
</TRPCReactProvider>
|
||||
|
||||
9
apps/nextjs/src/app/[locale]/modals.tsx
Normal file
9
apps/nextjs/src/app/[locale]/modals.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { createModalManager } from "mantine-modal-manager";
|
||||
|
||||
import { WidgetEditModal } from "@homarr/widgets";
|
||||
|
||||
export const [ModalsManager, modalEvents] = createModalManager({
|
||||
widgetEditModal: WidgetEditModal,
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import { auth } from "@homarr/auth";
|
||||
import { db } from "@homarr/db";
|
||||
import { Button, Stack, Title } from "@homarr/ui";
|
||||
|
||||
export default async function HomePage() {
|
||||
const currentSession = await auth();
|
||||
const users = await db.query.users.findMany();
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Title>Home</Title>
|
||||
<Button>Test</Button>
|
||||
<pre>{JSON.stringify(users)}</pre>
|
||||
{currentSession && (
|
||||
<span>
|
||||
Currently logged in as <b>{currentSession.user.name}</b>
|
||||
</span>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
27
apps/nextjs/src/app/[locale]/widgets/[sort]/layout.tsx
Normal file
27
apps/nextjs/src/app/[locale]/widgets/[sort]/layout.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import type { PropsWithChildren } from "react";
|
||||
|
||||
import { widgetImports } from "@homarr/widgets";
|
||||
|
||||
import { MainNavigation } from "~/components/layout/navigation";
|
||||
import { ClientShell } from "~/components/layout/shell";
|
||||
|
||||
const getLinks = () => {
|
||||
return Object.entries(widgetImports).map(([key, value]) => {
|
||||
return {
|
||||
href: `/widgets/${key}`,
|
||||
icon: value.definition.icon,
|
||||
label: value.definition.sort,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export default function WidgetPreviewLayout({ children }: PropsWithChildren) {
|
||||
const links = getLinks();
|
||||
|
||||
return (
|
||||
<ClientShell hasHeader={false}>
|
||||
<MainNavigation links={links} />
|
||||
{children}
|
||||
</ClientShell>
|
||||
);
|
||||
}
|
||||
48
apps/nextjs/src/app/[locale]/widgets/[sort]/page.tsx
Normal file
48
apps/nextjs/src/app/[locale]/widgets/[sort]/page.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
"use client";
|
||||
|
||||
import type { PropsWithChildren } from "react";
|
||||
import { useState } from "react";
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
import { ActionIcon, Affix, Center, IconPencil } from "@homarr/ui";
|
||||
import type { WidgetSort } from "@homarr/widgets";
|
||||
import { loadWidgetDynamic, widgetImports } from "@homarr/widgets";
|
||||
|
||||
import { modalEvents } from "../../modals";
|
||||
|
||||
type Props = PropsWithChildren<{ params: { sort: string } }>;
|
||||
|
||||
export default function WidgetPreview(props: Props) {
|
||||
const [options, setOptions] = useState<Record<string, unknown>>({});
|
||||
if (!(props.params.sort in widgetImports)) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const sort = props.params.sort as WidgetSort;
|
||||
const Comp = loadWidgetDynamic(sort);
|
||||
|
||||
return (
|
||||
<Center h="100vh">
|
||||
<Comp options={options as never} integrations={[]} />
|
||||
<Affix bottom={12} right={72}>
|
||||
<ActionIcon
|
||||
size={48}
|
||||
variant="default"
|
||||
radius="xl"
|
||||
onClick={() => {
|
||||
return modalEvents.openManagedModal({
|
||||
modal: "widgetEditModal",
|
||||
innerProps: {
|
||||
sort,
|
||||
definition: widgetImports[sort].definition.options,
|
||||
state: [options, setOptions],
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<IconPencil size={24} />
|
||||
</ActionIcon>
|
||||
</Affix>
|
||||
</Center>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user