317 lines
10 KiB
TypeScript
317 lines
10 KiB
TypeScript
"use client";
|
||
|
||
import type { ReactNode } from "react";
|
||
import {
|
||
ActionIcon,
|
||
Alert,
|
||
Box,
|
||
Button,
|
||
CopyButton,
|
||
Group,
|
||
Paper,
|
||
SimpleGrid,
|
||
Stack,
|
||
Text,
|
||
ThemeIcon,
|
||
Title,
|
||
} from "@mantine/core";
|
||
import {
|
||
IconArrowsMove,
|
||
IconBrandCss3,
|
||
IconBrandDiscord,
|
||
IconBrandGithub,
|
||
IconCheck,
|
||
IconCopy,
|
||
IconExternalLink,
|
||
IconHeartHandshake,
|
||
IconRobot,
|
||
IconRocket,
|
||
IconTerminal2,
|
||
IconWand,
|
||
IconX,
|
||
} from "@tabler/icons-react";
|
||
|
||
import { setClientCookie } from "@homarr/common";
|
||
import { createModal, useModalAction } from "@homarr/modals";
|
||
|
||
import { discordInviteUrl, v2BetaBlogUrl, v2BetaDiscussionUrl, v2BetaFeedbackUrl, v2BetaPreviewUrl } from "./constants";
|
||
import classes from "./v2-beta-announcement.module.css";
|
||
|
||
const diffLineClassNames = {
|
||
added: classes.diffLine_added,
|
||
context: classes.diffLine_context,
|
||
removed: classes.diffLine_removed,
|
||
} as const;
|
||
|
||
const composeDiffLines = [
|
||
{ kind: "context", value: " services:" },
|
||
{ kind: "context", value: " homarr:" },
|
||
{ kind: "added", value: "+ image: ghcr.io/homarr-labs/homarr-test:v2" },
|
||
{ kind: "removed", value: "- image: ghcr.io/homarr-labs/homarr:latest" },
|
||
{ kind: "context", value: " ports:" },
|
||
{ kind: "context", value: ' - "7575:7575"' },
|
||
{ kind: "context", value: " volumes:" },
|
||
{ kind: "context", value: " - homarr-v2:/appdata" },
|
||
{ kind: "context", value: " environment:" },
|
||
{ kind: "context", value: " SECRET_ENCRYPTION_KEY: <SECRET_KEY>" },
|
||
{ kind: "context", value: " WORKSHOP_API_URL: https://v2.preview.homarr.dev/ # Only during the beta" },
|
||
{ kind: "context", value: "" },
|
||
{ kind: "context", value: " volumes:" },
|
||
{ kind: "context", value: " homarr-v2:" },
|
||
] as const;
|
||
|
||
const v2ComposeFile = `services:
|
||
homarr:
|
||
image: ghcr.io/homarr-labs/homarr-test:v2
|
||
ports:
|
||
- "7575:7575"
|
||
volumes:
|
||
- homarr-v2:/appdata
|
||
environment:
|
||
SECRET_ENCRYPTION_KEY: <SECRET_KEY>
|
||
WORKSHOP_API_URL: https://v2.preview.homarr.dev/ # Only during the beta
|
||
|
||
volumes:
|
||
homarr-v2:
|
||
`;
|
||
|
||
interface V2BetaAnnouncementProps {
|
||
dismissalCookieName: string;
|
||
onDismiss: () => void;
|
||
}
|
||
|
||
export const V2BetaAnnouncement = ({ dismissalCookieName, onDismiss }: V2BetaAnnouncementProps) => {
|
||
const { openModal } = useModalAction(V2BetaAnnouncementModal);
|
||
|
||
const dismiss = () => {
|
||
setClientCookie(dismissalCookieName, "dismissed", {
|
||
maxAge: 60 * 60 * 24 * 365,
|
||
path: "/",
|
||
sameSite: "lax",
|
||
secure: window.location.protocol === "https:",
|
||
});
|
||
onDismiss();
|
||
};
|
||
|
||
return (
|
||
<Box component="aside" className={classes.banner} aria-label="Homarr v2 beta announcement">
|
||
<Group h="100%" justify="space-between" gap="sm" wrap="nowrap" px="md">
|
||
<Box className={classes.copy}>
|
||
<Text fw={750} size="sm" truncate>
|
||
We've been cooking, Homarr v2 beta is here
|
||
</Text>
|
||
<Text className={classes.subtitle} visibleFrom="sm" size="xs" truncate>
|
||
Custom Widgets & CSS workshop, new drag-and-drop, new container system, side-bars are back, new widgets
|
||
and so much more, come check it out and help us make v2 ready for everyone
|
||
</Text>
|
||
</Box>
|
||
|
||
<Group gap={6} wrap="nowrap">
|
||
<Button variant="white" color="red" size="compact-sm" onClick={() => openModal(undefined)}>
|
||
Explore v2
|
||
</Button>
|
||
<ActionIcon variant="subtle" color="white" onClick={dismiss} aria-label="Dismiss the v2 beta announcement">
|
||
<IconX size={18} />
|
||
</ActionIcon>
|
||
</Group>
|
||
</Group>
|
||
</Box>
|
||
);
|
||
};
|
||
|
||
const V2BetaAnnouncementModal = createModal<void>(() => {
|
||
const { openModal: openInstallModal } = useModalAction(V2BetaInstallModal);
|
||
|
||
return (
|
||
<Stack gap="lg">
|
||
<Paper className={classes.modalHero} p={{ base: "lg", sm: "xl" }} radius="md">
|
||
<Stack gap="sm" maw={760}>
|
||
<Title order={2}>Help us get Homarr v2 ready</Title>
|
||
<Text>
|
||
There is no release date yet. Homarr v2 will become the default when it is ready—and we need you to test it
|
||
and share feedback before we can be confident enough to make that switch.
|
||
</Text>
|
||
</Stack>
|
||
</Paper>
|
||
|
||
<SimpleGrid cols={{ base: 1, sm: 2 }} spacing="md">
|
||
<FeatureCard icon={<IconBrandCss3 size={21} />} title="Homarr Workshop">
|
||
Share and discover Custom Widgets and CSS made by the Homarr community.
|
||
</FeatureCard>
|
||
<FeatureCard icon={<IconArrowsMove size={21} />} title="Complete drag-and-drop rewrite">
|
||
Move, resize, multi-select, and arrange your dashboard with a board editor rebuilt from the ground up.
|
||
</FeatureCard>
|
||
<FeatureCard icon={<IconWand size={21} />} title="Custom Widgets v2">
|
||
We think you can make almost any widget with live previews, API requests, options, actions, and validation.
|
||
</FeatureCard>
|
||
<FeatureCard icon={<IconRobot size={21} />} title="Homarr Assistant">
|
||
Ask questions, find what you need, and manage Homarr through permission-aware tools.
|
||
</FeatureCard>
|
||
</SimpleGrid>
|
||
|
||
<Alert variant="light" color="red" icon={<IconHeartHandshake size={22} />} title="Your testing is essential">
|
||
Everyone uses Homarr in a different way. We’re counting on you to tell us anything that breaks or doesn’t feel
|
||
right with this new version. Every report matters.
|
||
</Alert>
|
||
|
||
<SimpleGrid cols={{ base: 1, sm: 2 }} spacing="sm">
|
||
<Button
|
||
component="a"
|
||
href={v2BetaPreviewUrl}
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
size="md"
|
||
leftSection={<IconRocket size={18} />}
|
||
>
|
||
Try the hosted preview
|
||
</Button>
|
||
<Button
|
||
variant="light"
|
||
size="md"
|
||
leftSection={<IconTerminal2 size={18} />}
|
||
onClick={() => openInstallModal(undefined)}
|
||
>
|
||
Test v2 on your server
|
||
</Button>
|
||
<Button
|
||
component="a"
|
||
href={v2BetaBlogUrl}
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
variant="default"
|
||
size="md"
|
||
rightSection={<IconExternalLink size={16} />}
|
||
>
|
||
See everything new
|
||
</Button>
|
||
<Button
|
||
component="a"
|
||
href={discordInviteUrl}
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
variant="default"
|
||
size="md"
|
||
leftSection={<IconBrandDiscord size={18} />}
|
||
>
|
||
Join us on Discord
|
||
</Button>
|
||
</SimpleGrid>
|
||
|
||
<Button
|
||
component="a"
|
||
href={v2BetaFeedbackUrl}
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
variant="light"
|
||
color="red"
|
||
size="md"
|
||
leftSection={<IconBrandGithub size={18} />}
|
||
>
|
||
Report anything that breaks or feels wrong
|
||
</Button>
|
||
|
||
<Group justify="space-between" align="center">
|
||
<Text c="dimmed" size="xs" maw={720}>
|
||
Please use a separate volume for the beta and keep a backup of your current Homarr data.
|
||
</Text>
|
||
<Button
|
||
component="a"
|
||
href={v2BetaDiscussionUrl}
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
variant="subtle"
|
||
size="compact-sm"
|
||
rightSection={<IconExternalLink size={15} />}
|
||
>
|
||
Follow v2 development
|
||
</Button>
|
||
</Group>
|
||
</Stack>
|
||
);
|
||
}).withOptions({
|
||
defaultTitle: "Homarr v2 beta",
|
||
size: 1000,
|
||
centered: true,
|
||
});
|
||
|
||
const V2BetaInstallModal = createModal<void>(() => (
|
||
<Stack gap="lg">
|
||
<Alert color="yellow" title="Keep your current Homarr data safe">
|
||
Run the beta with the separate <code>homarr-v2</code> volume shown below. Keep a backup, and do not point this
|
||
prerelease image at the only copy of your production data.
|
||
</Alert>
|
||
|
||
<Stack gap="xs">
|
||
<Title order={3}>Update your Docker Compose file</Title>
|
||
<Text c="dimmed" size="sm">
|
||
Replace the current image and add the beta-only Workshop URL. The complete example below can be copied as a
|
||
starting point.
|
||
</Text>
|
||
</Stack>
|
||
|
||
<Paper withBorder radius="md" className={classes.diffViewer}>
|
||
<Group justify="space-between" px="md" py="sm" className={classes.diffHeader}>
|
||
<Group gap="xs">
|
||
<IconTerminal2 size={17} />
|
||
<Text fw={700} size="sm">
|
||
docker-compose.yml
|
||
</Text>
|
||
</Group>
|
||
<CopyButton value={v2ComposeFile} timeout={2000}>
|
||
{({ copied, copy }) => (
|
||
<Button
|
||
variant="subtle"
|
||
color="gray"
|
||
size="compact-sm"
|
||
leftSection={copied ? <IconCheck size={15} /> : <IconCopy size={15} />}
|
||
onClick={copy}
|
||
>
|
||
{copied ? "Copied" : "Copy file"}
|
||
</Button>
|
||
)}
|
||
</CopyButton>
|
||
</Group>
|
||
<Box component="pre" className={classes.diffCode} aria-label="Docker Compose changes for Homarr v2 beta">
|
||
{composeDiffLines.map((line, index) => (
|
||
<Box component="span" className={diffLineClassNames[line.kind]} key={`${line.kind}-${index}`}>
|
||
{line.value || " "}
|
||
</Box>
|
||
))}
|
||
</Box>
|
||
</Paper>
|
||
|
||
<Text c="dimmed" size="xs">
|
||
Replace <code><SECRET_KEY></code> with your existing encryption key. The Workshop URL is only required while
|
||
the beta uses the preview Workshop.
|
||
</Text>
|
||
</Stack>
|
||
)).withOptions({
|
||
defaultTitle: "Test Homarr v2 on your server",
|
||
size: 900,
|
||
centered: true,
|
||
});
|
||
|
||
interface FeatureCardProps {
|
||
icon: ReactNode;
|
||
title: string;
|
||
children: ReactNode;
|
||
}
|
||
|
||
const FeatureCard = ({ icon, title, children }: FeatureCardProps) => (
|
||
<Paper className={classes.featureCard} withBorder p="md" radius="md">
|
||
<Group gap="sm" align="flex-start" wrap="nowrap">
|
||
<ThemeIcon variant="light" color="red" radius="md" size="lg">
|
||
{icon}
|
||
</ThemeIcon>
|
||
<Box>
|
||
<Text fw={750} size="sm">
|
||
{title}
|
||
</Text>
|
||
<Text c="dimmed" size="sm">
|
||
{children}
|
||
</Text>
|
||
</Box>
|
||
</Group>
|
||
</Paper>
|
||
);
|