Revert "feat: expand v2 beta guidance"
This reverts commit 7ba04f8019.
Co-authored-by: ajnart <49837342+ajnart@users.noreply.github.com>
This commit is contained in:
1 parent
1544e6ae1a
commit
fe8414c4f2
9 files changed
+124
-264
No files matched your search
@@ -1,79 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { Alert, Button, Group, Stack } from "@mantine/core";
|
||||
import { IconAlertTriangle, IconDownload } from "@tabler/icons-react";
|
||||
|
||||
import { useScopedI18n } from "@homarr/translation/client";
|
||||
|
||||
import { DangerousActionConfirmation } from "./dangerous-action-confirmation";
|
||||
|
||||
export const BackupExportButton = () => {
|
||||
const t = useScopedI18n("management.page.tool.backup.export");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [showConfirm, setShowConfirm] = useState(false);
|
||||
|
||||
const handleExport = async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await fetch("/api/backup/export");
|
||||
if (!response.ok) {
|
||||
const errorMessage = await response.json().then(
|
||||
(data) => data.error,
|
||||
() => `Server returned ${response.status}`,
|
||||
);
|
||||
throw new Error(errorMessage ?? t("error"));
|
||||
}
|
||||
|
||||
const blob = await response.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
const filename =
|
||||
response.headers.get("Content-Disposition")?.match(/filename="(.+)"/)?.[1] ?? "homarr-backup.zip";
|
||||
|
||||
const anchor = document.createElement("a");
|
||||
anchor.href = url;
|
||||
anchor.download = filename;
|
||||
anchor.click();
|
||||
URL.revokeObjectURL(url);
|
||||
setShowConfirm(false);
|
||||
} catch (cause) {
|
||||
setError(cause instanceof Error ? cause.message : t("error"));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack gap="sm">
|
||||
{error && (
|
||||
<Alert color="red" icon={<IconAlertTriangle size={16} />}>
|
||||
{error}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{showConfirm ? (
|
||||
<DangerousActionConfirmation
|
||||
title={t("confirm.title")}
|
||||
warningTitle={t("confirm.warningTitle")}
|
||||
warningBody={t("confirm.warningBody")}
|
||||
typePrompt={t("confirm.typePrompt", { phrase: "I understand" })}
|
||||
submitLabel={t("confirm.submit")}
|
||||
submitIcon={<IconDownload size={16} />}
|
||||
cancelLabel={t("confirm.cancel")}
|
||||
onConfirm={handleExport}
|
||||
onCancel={() => setShowConfirm(false)}
|
||||
disabled={loading}
|
||||
color="orange"
|
||||
/>
|
||||
) : (
|
||||
<Group>
|
||||
<Button leftSection={<IconDownload size={16} />} loading={loading} onClick={() => setShowConfirm(true)}>
|
||||
{t("button")}
|
||||
</Button>
|
||||
</Group>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -1,4 +1,3 @@
|
||||
export { BackupExportButton } from "./backup-export-button";
|
||||
export { DatabaseRestoreFlow } from "./database-restore-flow";
|
||||
export { DangerousActionConfirmation } from "./dangerous-action-confirmation";
|
||||
export type { BackupAnalysis, RestoreStep } from "./types";
|
||||
@@ -50,6 +50,11 @@
|
||||
transform: rotate(5deg);
|
||||
}
|
||||
|
||||
.featureCard {
|
||||
height: 100%;
|
||||
background: light-dark(var(--mantine-color-white), var(--mantine-color-dark-7));
|
||||
}
|
||||
|
||||
.diffViewer {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
ActionIcon,
|
||||
Accordion,
|
||||
Alert,
|
||||
Box,
|
||||
Button,
|
||||
@@ -24,7 +22,6 @@ import {
|
||||
IconBrandGithub,
|
||||
IconCheck,
|
||||
IconCopy,
|
||||
IconDatabaseExport,
|
||||
IconExternalLink,
|
||||
IconHeartHandshake,
|
||||
IconRobot,
|
||||
@@ -37,7 +34,6 @@ import {
|
||||
import { setClientCookie } from "@homarr/common";
|
||||
import { createModal, useModalAction } from "@homarr/modals";
|
||||
|
||||
import { BackupExportButton } from "~/components/backup";
|
||||
import { discordInviteUrl, v2BetaBlogUrl, v2BetaDiscussionUrl, v2BetaFeedbackUrl, v2BetaPreviewUrl } from "./constants";
|
||||
import classes from "./v2-beta-announcement.module.css";
|
||||
|
||||
@@ -58,7 +54,7 @@ const composeDiffLines = [
|
||||
{ kind: "context", value: " - homarr-v2:/appdata" },
|
||||
{ kind: "context", value: " environment:" },
|
||||
{ kind: "context", value: " SECRET_ENCRYPTION_KEY: <SECRET_KEY>" },
|
||||
{ kind: "added", value: "+ WORKSHOP_API_URL: https://v2.preview.homarr.dev/ # Only during the beta" },
|
||||
{ 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:" },
|
||||
@@ -80,12 +76,11 @@ volumes:
|
||||
`;
|
||||
|
||||
interface V2BetaAnnouncementProps {
|
||||
canExportBackup: boolean;
|
||||
dismissalCookieName: string;
|
||||
onDismiss: () => void;
|
||||
}
|
||||
|
||||
export const V2BetaAnnouncement = ({ canExportBackup, dismissalCookieName, onDismiss }: V2BetaAnnouncementProps) => {
|
||||
export const V2BetaAnnouncement = ({ dismissalCookieName, onDismiss }: V2BetaAnnouncementProps) => {
|
||||
const { openModal } = useModalAction(V2BetaAnnouncementModal);
|
||||
|
||||
const dismiss = () => {
|
||||
@@ -112,7 +107,7 @@ export const V2BetaAnnouncement = ({ canExportBackup, dismissalCookieName, onDis
|
||||
</Box>
|
||||
|
||||
<Group gap={6} wrap="nowrap">
|
||||
<Button variant="white" color="red" size="compact-sm" onClick={() => openModal({ canExportBackup })}>
|
||||
<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">
|
||||
@@ -124,12 +119,8 @@ export const V2BetaAnnouncement = ({ canExportBackup, dismissalCookieName, onDis
|
||||
);
|
||||
};
|
||||
|
||||
interface V2BetaAnnouncementModalProps {
|
||||
canExportBackup: boolean;
|
||||
}
|
||||
|
||||
const V2BetaAnnouncementModal = createModal<V2BetaAnnouncementModalProps>(({ innerProps }) => {
|
||||
const [openedSection, setOpenedSection] = useState<string | null>("workshop");
|
||||
const V2BetaAnnouncementModal = createModal<void>(() => {
|
||||
const { openModal: openInstallModal } = useModalAction(V2BetaInstallModal);
|
||||
|
||||
return (
|
||||
<Stack gap="lg">
|
||||
@@ -143,51 +134,27 @@ const V2BetaAnnouncementModal = createModal<V2BetaAnnouncementModalProps>(({ inn
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
<Accordion variant="separated" radius="md" value={openedSection} onChange={setOpenedSection}>
|
||||
<FeatureAccordionItem value="workshop" icon={<IconBrandCss3 size={20} />} title="Homarr Workshop">
|
||||
<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.
|
||||
</FeatureAccordionItem>
|
||||
<FeatureAccordionItem
|
||||
value="drag-and-drop"
|
||||
icon={<IconArrowsMove size={20} />}
|
||||
title="Complete drag-and-drop rewrite"
|
||||
>
|
||||
</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.
|
||||
</FeatureAccordionItem>
|
||||
<FeatureAccordionItem value="custom-widgets" icon={<IconWand size={20} />} title="Custom Widgets v2">
|
||||
</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.
|
||||
</FeatureAccordionItem>
|
||||
<FeatureAccordionItem value="assistant" icon={<IconRobot size={20} />} title="Homarr Assistant">
|
||||
</FeatureCard>
|
||||
<FeatureCard icon={<IconRobot size={21} />} title="Homarr Assistant">
|
||||
Ask questions, find what you need, and manage Homarr through permission-aware tools.
|
||||
</FeatureAccordionItem>
|
||||
<Accordion.Item value="installation">
|
||||
<Accordion.Control
|
||||
icon={
|
||||
<ThemeIcon variant="light" color="red" radius="md" size="lg">
|
||||
<IconTerminal2 size={20} />
|
||||
</ThemeIcon>
|
||||
}
|
||||
aria-label="Test Homarr v2 on your server"
|
||||
>
|
||||
<Box>
|
||||
<Text fw={750}>Test Homarr v2 on your server</Text>
|
||||
<Text c="dimmed" size="xs">
|
||||
Back up this instance, then update your Docker Compose file.
|
||||
</Text>
|
||||
</Box>
|
||||
</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<V2BetaInstallInstructions canExportBackup={innerProps.canExportBackup} />
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
</Accordion>
|
||||
</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: 3 }} spacing="sm">
|
||||
<SimpleGrid cols={{ base: 1, sm: 2 }} spacing="sm">
|
||||
<Button
|
||||
component="a"
|
||||
href={v2BetaPreviewUrl}
|
||||
@@ -198,6 +165,14 @@ const V2BetaAnnouncementModal = createModal<V2BetaAnnouncementModalProps>(({ inn
|
||||
>
|
||||
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}
|
||||
@@ -259,37 +234,13 @@ const V2BetaAnnouncementModal = createModal<V2BetaAnnouncementModalProps>(({ inn
|
||||
centered: true,
|
||||
});
|
||||
|
||||
interface V2BetaInstallInstructionsProps {
|
||||
canExportBackup: boolean;
|
||||
}
|
||||
|
||||
const V2BetaInstallInstructions = ({ canExportBackup }: V2BetaInstallInstructionsProps) => (
|
||||
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>
|
||||
|
||||
{canExportBackup ? (
|
||||
<Paper withBorder p="md" radius="md">
|
||||
<Stack gap="sm">
|
||||
<Group gap="sm">
|
||||
<IconDatabaseExport size={22} />
|
||||
<Title order={4}>Back up this Homarr instance</Title>
|
||||
</Group>
|
||||
<Text c="dimmed" size="sm">
|
||||
Download a full backup before testing v2. The archive contains sensitive data, so keep it somewhere safe.
|
||||
</Text>
|
||||
<BackupExportButton />
|
||||
</Stack>
|
||||
</Paper>
|
||||
) : (
|
||||
<Alert color="blue" title="Back up your database before continuing">
|
||||
Direct backup downloads are available here to Homarr administrators using SQLite. If you use MySQL or
|
||||
PostgreSQL, back up the database with its own tools before testing v2.
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Stack gap="xs">
|
||||
<Title order={3}>Update your Docker Compose file</Title>
|
||||
<Text c="dimmed" size="sm">
|
||||
@@ -334,33 +285,32 @@ const V2BetaInstallInstructions = ({ canExportBackup }: V2BetaInstallInstruction
|
||||
the beta uses the preview Workshop.
|
||||
</Text>
|
||||
</Stack>
|
||||
);
|
||||
)).withOptions({
|
||||
defaultTitle: "Test Homarr v2 on your server",
|
||||
size: 900,
|
||||
centered: true,
|
||||
});
|
||||
|
||||
interface FeatureAccordionItemProps {
|
||||
value: string;
|
||||
interface FeatureCardProps {
|
||||
icon: ReactNode;
|
||||
title: string;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
const FeatureAccordionItem = ({ value, icon, title, children }: FeatureAccordionItemProps) => (
|
||||
<Accordion.Item value={value}>
|
||||
<Accordion.Control
|
||||
icon={
|
||||
<ThemeIcon variant="light" color="red" radius="md" size="lg">
|
||||
{icon}
|
||||
</ThemeIcon>
|
||||
}
|
||||
aria-label={title}
|
||||
>
|
||||
<Text fw={750}>{title}</Text>
|
||||
</Accordion.Control>
|
||||
<Accordion.Panel>
|
||||
<Box pl={48}>
|
||||
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>
|
||||
</Accordion.Panel>
|
||||
</Accordion.Item>
|
||||
</Group>
|
||||
</Paper>
|
||||
);
|
||||
@@ -8,14 +8,12 @@ import { ClientShell } from "../shell";
|
||||
import { V2BetaAnnouncement } from "./v2-beta-announcement";
|
||||
|
||||
interface V2BetaDashboardShellProps {
|
||||
canExportBackup: boolean;
|
||||
dismissalCookieName: string;
|
||||
initiallyDismissed: boolean;
|
||||
header: ReactNode;
|
||||
}
|
||||
|
||||
export const V2BetaDashboardShell = ({
|
||||
canExportBackup,
|
||||
dismissalCookieName,
|
||||
initiallyDismissed,
|
||||
header,
|
||||
@@ -29,7 +27,6 @@ export const V2BetaDashboardShell = ({
|
||||
<AppShellHeader maw="100vw" zIndex={201} style={{ overflowX: "hidden" }}>
|
||||
{isAnnouncementVisible && (
|
||||
<V2BetaAnnouncement
|
||||
canExportBackup={canExportBackup}
|
||||
dismissalCookieName={dismissalCookieName}
|
||||
onDismiss={() => setIsAnnouncementVisible(false)}
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user