feat: add import for config files from oldmarr (#1019)
* wip: add oldmarr config import
* wip: add support for wrong amount of categories / sections with autofix, color mapping, position adjustments of wrappers
* fix: lockfile broken
* feat: add support for form data trpc requests
* wip: improve file upload
* refactor: restructure import, add import configuration
* wip: add configurations for import to modal
* refactor: move oldmarr import to old-import package
* fix: column count not respects screen size for board
* feat: add beta badge for oldmarr config import
* chore: address pull request feedback
* fix: format issues
* fix: inconsistent versions
* fix: deepsource issues
* fix: revert {} to Record<string, never> convertion to prevent typecheck issue
* fix: inconsistent zod version
* fix: format issue
* chore: address pull request feedback
* fix: wrong import
* fix: broken lock file
* fix: inconsistent versions
* fix: format issues
This commit is contained in:
@@ -5,7 +5,15 @@ import { useState } from "react";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
||||
import { ReactQueryStreamedHydration } from "@tanstack/react-query-next-experimental";
|
||||
import { createWSClient, loggerLink, unstable_httpBatchStreamLink, wsLink } from "@trpc/client";
|
||||
import {
|
||||
createWSClient,
|
||||
httpLink,
|
||||
isNonJsonSerializable,
|
||||
loggerLink,
|
||||
splitLink,
|
||||
unstable_httpBatchStreamLink,
|
||||
wsLink,
|
||||
} from "@trpc/client";
|
||||
import superjson from "superjson";
|
||||
|
||||
import type { AppRouter } from "@homarr/api";
|
||||
@@ -34,18 +42,29 @@ export function TRPCReactProvider(props: PropsWithChildren) {
|
||||
enabled: (opts) =>
|
||||
process.env.NODE_ENV === "development" || (opts.direction === "down" && opts.result instanceof Error),
|
||||
}),
|
||||
(args) => {
|
||||
return ({ op, next }) => {
|
||||
console.log("op", op.type, op.input, op.path, op.id);
|
||||
if (op.type === "subscription") {
|
||||
const link = wsLink<AppRouter>({
|
||||
client: wsClient,
|
||||
transformer: superjson,
|
||||
});
|
||||
return link(args)({ op, next });
|
||||
}
|
||||
|
||||
return unstable_httpBatchStreamLink({
|
||||
splitLink({
|
||||
condition: ({ type }) => type === "subscription",
|
||||
true: wsLink<AppRouter>({
|
||||
client: wsClient,
|
||||
transformer: superjson,
|
||||
}),
|
||||
false: splitLink({
|
||||
condition: ({ input }) => isNonJsonSerializable(input),
|
||||
true: httpLink({
|
||||
/**
|
||||
* We don't want to transform the data here as we want to use form data
|
||||
*/
|
||||
transformer: {
|
||||
serialize(object: unknown) {
|
||||
return object;
|
||||
},
|
||||
deserialize(data: unknown) {
|
||||
return data;
|
||||
},
|
||||
},
|
||||
url: `${getBaseUrl()}/api/trpc`,
|
||||
}),
|
||||
false: unstable_httpBatchStreamLink({
|
||||
transformer: superjson,
|
||||
url: `${getBaseUrl()}/api/trpc`,
|
||||
headers() {
|
||||
@@ -53,9 +72,9 @@ export function TRPCReactProvider(props: PropsWithChildren) {
|
||||
headers.set("x-trpc-source", "nextjs-react");
|
||||
return headers;
|
||||
},
|
||||
})(args)({ op, next });
|
||||
};
|
||||
},
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback } from "react";
|
||||
import { IconCategoryPlus } from "@tabler/icons-react";
|
||||
import { Affix, Button, Group, Menu } from "@mantine/core";
|
||||
import { IconCategoryPlus, IconChevronDown, IconFileImport } from "@tabler/icons-react";
|
||||
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { useModalAction } from "@homarr/modals";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { BetaBadge } from "@homarr/ui";
|
||||
|
||||
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
|
||||
import { AddBoardModal } from "~/components/manage/boards/add-board-modal";
|
||||
import { MobileAffixButton } from "~/components/manage/mobile-affix-button";
|
||||
import { ImportBoardModal } from "~/components/manage/boards/import-board-modal";
|
||||
|
||||
interface CreateBoardButtonProps {
|
||||
boardNames: string[];
|
||||
@@ -17,7 +19,8 @@ interface CreateBoardButtonProps {
|
||||
|
||||
export const CreateBoardButton = ({ boardNames }: CreateBoardButtonProps) => {
|
||||
const t = useI18n();
|
||||
const { openModal } = useModalAction(AddBoardModal);
|
||||
const { openModal: openAddModal } = useModalAction(AddBoardModal);
|
||||
const { openModal: openImportModal } = useModalAction(ImportBoardModal);
|
||||
|
||||
const { mutateAsync, isPending } = clientApi.board.createBoard.useMutation({
|
||||
onSettled: async () => {
|
||||
@@ -25,8 +28,8 @@ export const CreateBoardButton = ({ boardNames }: CreateBoardButtonProps) => {
|
||||
},
|
||||
});
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
openModal({
|
||||
const onCreateClick = useCallback(() => {
|
||||
openAddModal({
|
||||
onSuccess: async (values) => {
|
||||
await mutateAsync({
|
||||
name: values.name,
|
||||
@@ -36,11 +39,41 @@ export const CreateBoardButton = ({ boardNames }: CreateBoardButtonProps) => {
|
||||
},
|
||||
boardNames,
|
||||
});
|
||||
}, [mutateAsync, boardNames, openModal]);
|
||||
}, [mutateAsync, boardNames, openAddModal]);
|
||||
|
||||
const onImportClick = useCallback(() => {
|
||||
openImportModal({ boardNames });
|
||||
}, [openImportModal, boardNames]);
|
||||
|
||||
const buttonGroupContent = (
|
||||
<>
|
||||
<Button leftSection={<IconCategoryPlus size="1rem" />} onClick={onCreateClick} loading={isPending}>
|
||||
{t("management.page.board.action.new.label")}
|
||||
</Button>
|
||||
<Menu position="bottom-end">
|
||||
<Menu.Target>
|
||||
<Button px="xs" ms={1}>
|
||||
<IconChevronDown size="1rem" />
|
||||
</Button>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<Menu.Item onClick={onImportClick} leftSection={<IconFileImport size="1rem" />}>
|
||||
<Group>
|
||||
{t("board.action.oldImport.label")}
|
||||
<BetaBadge size="xs" />
|
||||
</Group>
|
||||
</Menu.Item>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<MobileAffixButton leftSection={<IconCategoryPlus size="1rem" />} onClick={onClick} loading={isPending}>
|
||||
{t("management.page.board.action.new.label")}
|
||||
</MobileAffixButton>
|
||||
<>
|
||||
<Button.Group visibleFrom="md">{buttonGroupContent}</Button.Group>
|
||||
<Affix hiddenFrom="md" position={{ bottom: 20, right: 20 }}>
|
||||
<Button.Group>{buttonGroupContent}</Button.Group>
|
||||
</Affix>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user