diff --git a/apps/nextjs/src/app/[locale]/boards/(content)/_custom-css.tsx b/apps/nextjs/src/app/[locale]/boards/(content)/_custom-css.tsx
new file mode 100644
index 000000000..ab01aff39
--- /dev/null
+++ b/apps/nextjs/src/app/[locale]/boards/(content)/_custom-css.tsx
@@ -0,0 +1,9 @@
+"use client";
+
+import { useRequiredBoard } from "./_context";
+
+export const CustomCss = () => {
+ const board = useRequiredBoard();
+
+ return ;
+};
diff --git a/apps/nextjs/src/app/[locale]/boards/[name]/settings/_customCss.tsx b/apps/nextjs/src/app/[locale]/boards/[name]/settings/_customCss.tsx
index 4d9bc7f75..4e007388f 100644
--- a/apps/nextjs/src/app/[locale]/boards/[name]/settings/_customCss.tsx
+++ b/apps/nextjs/src/app/[locale]/boards/[name]/settings/_customCss.tsx
@@ -1,7 +1,91 @@
"use client";
-// TODO: add some sort of store (maybe directory on GitHub)
+import { Alert, Button, Group, Input, Stack } from "@mantine/core";
+import { highlight, languages } from "prismjs";
+import Editor from "react-simple-code-editor";
-export const CustomCssSettingsContent = () => {
- return null;
+import "~/styles/prismjs.scss";
+
+import { IconInfoCircle } from "@tabler/icons-react";
+
+import { useForm } from "@homarr/form";
+import { useI18n, useScopedI18n } from "@homarr/translation/client";
+
+import type { Board } from "../../_types";
+import { useSavePartialSettingsMutation } from "./_shared";
+import classes from "./customcss.module.css";
+
+interface Props {
+ board: Board;
+}
+
+export const CustomCssSettingsContent = ({ board }: Props) => {
+ const t = useI18n();
+ const customCssT = useScopedI18n("board.field.customCss");
+ const { mutate: savePartialSettings, isPending } = useSavePartialSettingsMutation(board);
+ const form = useForm({
+ initialValues: {
+ customCss: board.customCss ?? "",
+ },
+ });
+
+ return (
+
+ );
+};
+
+interface CustomCssInputProps {
+ value?: string;
+ onChange: (value: string) => void;
+}
+
+const CustomCssInput = ({ value, onChange }: CustomCssInputProps) => {
+ const customCssT = useScopedI18n("board.field.customCss");
+
+ return (
+
+
+ highlight(code, languages.extend("css", {}), "css")}
+ padding={10}
+ style={{
+ fontFamily: '"Fira code", "Fira Mono", monospace',
+ fontSize: 12,
+ minHeight: 250,
+ }}
+ />
+
+
+ );
};
diff --git a/apps/nextjs/src/app/[locale]/boards/[name]/settings/customcss.module.css b/apps/nextjs/src/app/[locale]/boards/[name]/settings/customcss.module.css
new file mode 100644
index 000000000..b01c21383
--- /dev/null
+++ b/apps/nextjs/src/app/[locale]/boards/[name]/settings/customcss.module.css
@@ -0,0 +1,22 @@
+.codeEditorFooter {
+ border-bottom-left-radius: var(--mantine-radius-sm);
+ border-bottom-right-radius: var(--mantine-radius-sm);
+ background-color: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-7));
+}
+
+.codeEditorRoot {
+ margin-top: 4px;
+ border-color: light-dark(var(--mantine-color-gray-4), var(--mantine-color-dark-4));
+ border-width: 1px;
+ border-style: solid;
+ border-radius: var(--mantine-radius-sm);
+}
+
+.codeEditor {
+ background-color: light-dark(white, var(--mantine-color-dark-6));
+ font-size: var(--mantine-font-size-xs);
+}
+
+.codeEditor ::placeholder {
+ color: light-dark(var(--mantine-color-gray-5), var(--mantine-color-dark-3));
+}
diff --git a/apps/nextjs/src/app/[locale]/boards/[name]/settings/page.tsx b/apps/nextjs/src/app/[locale]/boards/[name]/settings/page.tsx
index cfafacf85..1748ee36b 100644
--- a/apps/nextjs/src/app/[locale]/boards/[name]/settings/page.tsx
+++ b/apps/nextjs/src/app/[locale]/boards/[name]/settings/page.tsx
@@ -84,7 +84,7 @@ export default async function BoardSettingsPage({ params, searchParams }: Props)
-
+
{hasFullAccess && (
<>
diff --git a/apps/nextjs/src/app/[locale]/boards/_layout-creator.tsx b/apps/nextjs/src/app/[locale]/boards/_layout-creator.tsx
index 9dd31a049..56dabdd88 100644
--- a/apps/nextjs/src/app/[locale]/boards/_layout-creator.tsx
+++ b/apps/nextjs/src/app/[locale]/boards/_layout-creator.tsx
@@ -12,6 +12,7 @@ import { ClientShell } from "~/components/layout/shell";
import type { Board } from "./_types";
import { BoardProvider } from "./(content)/_context";
import type { Params } from "./(content)/_creator";
+import { CustomCss } from "./(content)/_custom-css";
import { BoardMantineProvider } from "./(content)/_theme";
interface CreateBoardLayoutProps {
@@ -44,6 +45,7 @@ export const createBoardLayout = ({
+
}
diff --git a/apps/nextjs/src/app/[locale]/widgets/[kind]/_content.tsx b/apps/nextjs/src/app/[locale]/widgets/[kind]/_content.tsx
index 3c1ad6ba7..9a2a1f100 100644
--- a/apps/nextjs/src/app/[locale]/widgets/[kind]/_content.tsx
+++ b/apps/nextjs/src/app/[locale]/widgets/[kind]/_content.tsx
@@ -8,7 +8,7 @@ import type { IntegrationKind, WidgetKind } from "@homarr/definitions";
import { useModalAction } from "@homarr/modals";
import { showSuccessNotification } from "@homarr/notifications";
import { useScopedI18n } from "@homarr/translation/client";
-import type { BoardItemIntegration } from "@homarr/validation";
+import type { BoardItemAdvancedOptions, BoardItemIntegration } from "@homarr/validation";
import {
loadWidgetDynamic,
reduceWidgetOptionsWithDefaultValues,
@@ -42,9 +42,13 @@ export const WidgetPreviewPageContent = ({ kind, integrationData }: WidgetPrevie
const [state, setState] = useState<{
options: Record;
integrations: BoardItemIntegration[];
+ advancedOptions: BoardItemAdvancedOptions;
}>({
options: reduceWidgetOptionsWithDefaultValues(kind, {}),
integrations: [],
+ advancedOptions: {
+ customCssClasses: [],
+ },
});
const handleOpenEditWidgetModal = useCallback(() => {
diff --git a/apps/nextjs/src/components/board/items/item-actions.tsx b/apps/nextjs/src/components/board/items/item-actions.tsx
index 1f3650d9c..309dab8bd 100644
--- a/apps/nextjs/src/components/board/items/item-actions.tsx
+++ b/apps/nextjs/src/components/board/items/item-actions.tsx
@@ -2,7 +2,7 @@ import { useCallback } from "react";
import { createId } from "@homarr/db/client";
import type { WidgetKind } from "@homarr/definitions";
-import type { BoardItemIntegration } from "@homarr/validation";
+import type { BoardItemAdvancedOptions, BoardItemIntegration } from "@homarr/validation";
import type { EmptySection, Item } from "~/app/[locale]/boards/_types";
import { useUpdateBoard } from "~/app/[locale]/boards/(content)/_client";
@@ -31,6 +31,11 @@ interface UpdateItemOptions {
newOptions: Record;
}
+interface UpdateItemAdvancedOptions {
+ itemId: string;
+ newAdvancedOptions: BoardItemAdvancedOptions;
+}
+
interface UpdateItemIntegrations {
itemId: string;
newIntegrations: BoardItemIntegration[];
@@ -59,6 +64,9 @@ export const useItemActions = () => {
width: 1,
height: 1,
integrations: [],
+ advancedOptions: {
+ customCssClasses: [],
+ },
} satisfies Omit- & {
kind: WidgetKind;
};
@@ -91,7 +99,7 @@ export const useItemActions = () => {
return {
...section,
items: section.items.map((item) => {
- // Return same item if item is not the one we're moving
+ // Return same item if item is not the one we're changing
if (item.id !== itemId) return item;
return {
...item,
@@ -106,6 +114,33 @@ export const useItemActions = () => {
[updateBoard],
);
+ const updateItemAdvancedOptions = useCallback(
+ ({ itemId, newAdvancedOptions }: UpdateItemAdvancedOptions) => {
+ updateBoard((previous) => {
+ if (!previous) return previous;
+ return {
+ ...previous,
+ sections: previous.sections.map((section) => {
+ // Return same section if item is not in it
+ if (!section.items.some((item) => item.id === itemId)) return section;
+ return {
+ ...section,
+ items: section.items.map((item) => {
+ // Return same item if item is not the one we're changing
+ if (item.id !== itemId) return item;
+ return {
+ ...item,
+ advancedOptions: newAdvancedOptions,
+ };
+ }),
+ };
+ }),
+ };
+ });
+ },
+ [updateBoard],
+ );
+
const updateItemIntegrations = useCallback(
({ itemId, newIntegrations }: UpdateItemIntegrations) => {
updateBoard((previous) => {
@@ -224,6 +259,7 @@ export const useItemActions = () => {
moveItemToSection,
removeItem,
updateItemOptions,
+ updateItemAdvancedOptions,
updateItemIntegrations,
createItem,
};
diff --git a/apps/nextjs/src/components/board/sections/content.tsx b/apps/nextjs/src/components/board/sections/content.tsx
index 85c9d35d8..f978ced0f 100644
--- a/apps/nextjs/src/components/board/sections/content.tsx
+++ b/apps/nextjs/src/components/board/sections/content.tsx
@@ -70,7 +70,11 @@ const BoardItem = ({ refs, item, opacity }: ItemProps) => {
>
{
const { openModal } = useModalAction(WidgetEditModal);
const { openConfirmModal } = useConfirmModal();
const isEditMode = useAtomValue(editModeAtom);
- const { updateItemOptions, updateItemIntegrations, removeItem } = useItemActions();
+ const { updateItemOptions, updateItemAdvancedOptions, updateItemIntegrations, removeItem } = useItemActions();
const { data: integrationData, isPending } = clientApi.integration.all.useQuery();
const currentDefinition = useMemo(() => widgetImports[item.kind].definition, [item.kind]);
@@ -133,14 +137,19 @@ const ItemMenu = ({ offset, item }: { offset: number; item: Item }) => {
openModal({
kind: item.kind,
value: {
+ advancedOptions: item.advancedOptions,
options: item.options,
integrations: item.integrations,
},
- onSuccessfulEdit: ({ options, integrations }) => {
+ onSuccessfulEdit: ({ options, integrations, advancedOptions }) => {
updateItemOptions({
itemId: item.id,
newOptions: options,
});
+ updateItemAdvancedOptions({
+ itemId: item.id,
+ newAdvancedOptions: advancedOptions,
+ });
updateItemIntegrations({
itemId: item.id,
newIntegrations: integrations,
diff --git a/apps/nextjs/src/styles/prismjs.scss b/apps/nextjs/src/styles/prismjs.scss
new file mode 100644
index 000000000..d9f1d6327
--- /dev/null
+++ b/apps/nextjs/src/styles/prismjs.scss
@@ -0,0 +1,225 @@
+[data-mantine-color-scheme="light"] {
+ code[class*="language-"],
+ pre[class*="language-"] {
+ color: #000;
+ background: 0 0;
+ text-shadow: 0 1px #fff;
+ font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
+ font-size: 1em;
+ text-align: left;
+ white-space: pre;
+ word-spacing: normal;
+ word-break: normal;
+ word-wrap: normal;
+ line-height: 1.5;
+ -moz-tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
+ -webkit-hyphens: none;
+ -moz-hyphens: none;
+ -ms-hyphens: none;
+ hyphens: none;
+ }
+ code[class*="language-"] ::-moz-selection,
+ code[class*="language-"]::-moz-selection,
+ pre[class*="language-"] ::-moz-selection,
+ pre[class*="language-"]::-moz-selection {
+ text-shadow: none;
+ background: #b3d4fc;
+ }
+ code[class*="language-"] ::selection,
+ code[class*="language-"]::selection,
+ pre[class*="language-"] ::selection,
+ pre[class*="language-"]::selection {
+ text-shadow: none;
+ background: #b3d4fc;
+ }
+ @media print {
+ code[class*="language-"],
+ pre[class*="language-"] {
+ text-shadow: none;
+ }
+ }
+ pre[class*="language-"] {
+ padding: 1em;
+ margin: 0.5em 0;
+ overflow: auto;
+ }
+ :not(pre) > code[class*="language-"],
+ pre[class*="language-"] {
+ background: #f5f2f0;
+ }
+ :not(pre) > code[class*="language-"] {
+ padding: 0.1em;
+ border-radius: 0.3em;
+ white-space: normal;
+ }
+ .token.cdata,
+ .token.comment,
+ .token.doctype,
+ .token.prolog {
+ color: #708090;
+ }
+ .token.punctuation {
+ color: #999;
+ }
+ .token.namespace {
+ opacity: 0.7;
+ }
+ .token.boolean,
+ .token.constant,
+ .token.deleted,
+ .token.number,
+ .token.property,
+ .token.symbol,
+ .token.tag {
+ color: #905;
+ }
+ .token.attr-name,
+ .token.builtin,
+ .token.char,
+ .token.inserted,
+ .token.selector,
+ .token.string {
+ color: #690;
+ }
+ .language-css .token.string,
+ .style .token.string,
+ .token.entity,
+ .token.operator,
+ .token.url {
+ color: #9a6e3a;
+ background: hsla(0, 0%, 100%, 0.5);
+ }
+ .token.atrule,
+ .token.attr-value,
+ .token.keyword {
+ color: #07a;
+ }
+ .token.class-name,
+ .token.function {
+ color: #dd4a68;
+ }
+ .token.important,
+ .token.regex,
+ .token.variable {
+ color: #e90;
+ }
+ .token.bold,
+ .token.important {
+ font-weight: 700;
+ }
+ .token.italic {
+ font-style: italic;
+ }
+ .token.entity {
+ cursor: help;
+ }
+}
+
+[data-mantine-color-scheme="dark"] {
+ code[class*="language-"],
+ pre[class*="language-"] {
+ color: #fff;
+ background: 0 0;
+ text-shadow: 0 -0.1em 0.2em #000;
+ font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
+ font-size: 1em;
+ text-align: left;
+ white-space: pre;
+ word-spacing: normal;
+ word-break: normal;
+ word-wrap: normal;
+ line-height: 1.5;
+ -moz-tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
+ -webkit-hyphens: none;
+ -moz-hyphens: none;
+ -ms-hyphens: none;
+ hyphens: none;
+ }
+ @media print {
+ code[class*="language-"],
+ pre[class*="language-"] {
+ text-shadow: none;
+ }
+ }
+ :not(pre) > code[class*="language-"],
+ pre[class*="language-"] {
+ background: #4c3f33;
+ }
+ pre[class*="language-"] {
+ padding: 1em;
+ margin: 0.5em 0;
+ overflow: auto;
+ border: 0.3em solid #7a6651;
+ border-radius: 0.5em;
+ box-shadow: 1px 1px 0.5em #000 inset;
+ }
+ :not(pre) > code[class*="language-"] {
+ padding: 0.15em 0.2em 0.05em;
+ border-radius: 0.3em;
+ border: 0.13em solid #7a6651;
+ box-shadow: 1px 1px 0.3em -0.1em #000 inset;
+ white-space: normal;
+ }
+ .token.cdata,
+ .token.comment,
+ .token.doctype,
+ .token.prolog {
+ color: #997f66;
+ }
+ .token.punctuation {
+ opacity: 0.7;
+ }
+ .token.namespace {
+ opacity: 0.7;
+ }
+ .token.boolean,
+ .token.constant,
+ .token.number,
+ .token.property,
+ .token.symbol,
+ .token.tag {
+ color: #d1939e;
+ }
+ .token.attr-name,
+ .token.builtin,
+ .token.char,
+ .token.inserted,
+ .token.selector,
+ .token.string {
+ color: #bce051;
+ }
+ .language-css .token.string,
+ .style .token.string,
+ .token.entity,
+ .token.operator,
+ .token.url,
+ .token.variable {
+ color: #f4b73d;
+ }
+ .token.atrule,
+ .token.attr-value,
+ .token.keyword {
+ color: #d1939e;
+ }
+ .token.important,
+ .token.regex {
+ color: #e90;
+ }
+ .token.bold,
+ .token.important {
+ font-weight: 700;
+ }
+ .token.italic {
+ font-style: italic;
+ }
+ .token.entity {
+ cursor: help;
+ }
+ .token.deleted {
+ color: red;
+ }
+}
diff --git a/packages/api/src/router/board.ts b/packages/api/src/router/board.ts
index 1e5067a78..c37e92c57 100644
--- a/packages/api/src/router/board.ts
+++ b/packages/api/src/router/board.ts
@@ -16,6 +16,7 @@ import {
} from "@homarr/db/schema/sqlite";
import type { WidgetKind } from "@homarr/definitions";
import { getPermissionsWithParents, widgetKinds } from "@homarr/definitions";
+import type { BoardItemAdvancedOptions } from "@homarr/validation";
import { createSectionSchema, sharedItemSchema, validation, z } from "@homarr/validation";
import { zodUnionFromArray } from "../../../validation/src/enums";
@@ -229,6 +230,7 @@ export const boardRouter = createTRPCRouter({
xOffset: item.xOffset,
yOffset: item.yOffset,
options: superjson.stringify(item.options),
+ advancedOptions: superjson.stringify(item.advancedOptions),
sectionId: item.sectionId,
})),
);
@@ -515,6 +517,7 @@ const getFullBoardWithWhereAsync = async (db: Database, where: SQL, use
items: section.items.map((item) => ({
...item,
integrations: item.integrations.map((item) => item.integration),
+ advancedOptions: superjson.parse(item.advancedOptions),
options: superjson.parse>(item.options),
})),
}),
diff --git a/packages/api/src/router/test/board.spec.ts b/packages/api/src/router/test/board.spec.ts
index 092aca3cb..11ac8c913 100644
--- a/packages/api/src/router/test/board.spec.ts
+++ b/packages/api/src/router/test/board.spec.ts
@@ -664,6 +664,7 @@ describe("saveBoard should save full board", () => {
width: 1,
xOffset: 0,
yOffset: 0,
+ advancedOptions: {},
},
],
},
@@ -724,6 +725,7 @@ describe("saveBoard should save full board", () => {
width: 1,
xOffset: 0,
yOffset: 0,
+ advancedOptions: {},
},
],
},
@@ -837,6 +839,7 @@ describe("saveBoard should save full board", () => {
width: 1,
xOffset: 3,
yOffset: 2,
+ advancedOptions: {},
},
],
},
@@ -905,6 +908,7 @@ describe("saveBoard should save full board", () => {
width: 1,
xOffset: 0,
yOffset: 0,
+ advancedOptions: {},
},
],
},
@@ -1018,6 +1022,7 @@ describe("saveBoard should save full board", () => {
width: 2,
xOffset: 7,
yOffset: 5,
+ advancedOptions: {},
},
],
},
diff --git a/packages/db/migrations/mysql/0002_flimsy_deathbird.sql b/packages/db/migrations/mysql/0002_flimsy_deathbird.sql
new file mode 100644
index 000000000..57e15e767
--- /dev/null
+++ b/packages/db/migrations/mysql/0002_flimsy_deathbird.sql
@@ -0,0 +1 @@
+ALTER TABLE `item` ADD `advanced_options` text DEFAULT ('{"json": {}}') NOT NULL;
\ No newline at end of file
diff --git a/packages/db/migrations/mysql/0002_freezing_black_panther.sql b/packages/db/migrations/mysql/0003_freezing_black_panther.sql
similarity index 100%
rename from packages/db/migrations/mysql/0002_freezing_black_panther.sql
rename to packages/db/migrations/mysql/0003_freezing_black_panther.sql
diff --git a/packages/db/migrations/mysql/meta/0002_snapshot.json b/packages/db/migrations/mysql/meta/0002_snapshot.json
index 398aab9a7..f098f20c9 100644
--- a/packages/db/migrations/mysql/meta/0002_snapshot.json
+++ b/packages/db/migrations/mysql/meta/0002_snapshot.json
@@ -1,8 +1,8 @@
{
"version": "5",
"dialect": "mysql",
- "id": "e7a373e1-9f36-4910-9f2b-ac6fd5e79145",
- "prevId": "ba2dd885-4e7f-4a45-99a0-7b45cbd0a5c2",
+ "id": "4e382d0d-a432-4953-bd5e-04f3f33e26a4",
+ "prevId": "fdeaf6eb-cd62-4fa5-9b38-d7f80a60db9f",
"tables": {
"account": {
"name": "account",
@@ -910,6 +910,14 @@
"notNull": true,
"autoincrement": false,
"default": "('{\"json\": {}}')"
+ },
+ "advanced_options": {
+ "name": "advanced_options",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "('{\"json\": {}}')"
}
},
"indexes": {},
@@ -991,40 +999,6 @@
},
"uniqueConstraints": {}
},
- "serverSetting": {
- "name": "serverSetting",
- "columns": {
- "key": {
- "name": "key",
- "type": "varchar(64)",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false
- },
- "value": {
- "name": "value",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "('{\"json\": {}}')"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {
- "serverSetting_key": {
- "name": "serverSetting_key",
- "columns": ["key"]
- }
- },
- "uniqueConstraints": {
- "serverSetting_key_unique": {
- "name": "serverSetting_key_unique",
- "columns": ["key"]
- }
- }
- },
"session": {
"name": "session",
"columns": {
diff --git a/packages/db/migrations/mysql/meta/0003_snapshot.json b/packages/db/migrations/mysql/meta/0003_snapshot.json
new file mode 100644
index 000000000..19b512ac9
--- /dev/null
+++ b/packages/db/migrations/mysql/meta/0003_snapshot.json
@@ -0,0 +1,1208 @@
+{
+ "version": "5",
+ "dialect": "mysql",
+ "id": "4e382d0d-a432-4953-bd5e-04f3f33e26a4",
+ "prevId": "e7a373e1-9f36-4910-9f2b-ac6fd5e79145",
+ "tables": {
+ "account": {
+ "name": "account",
+ "columns": {
+ "userId": {
+ "name": "userId",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "providerAccountId": {
+ "name": "providerAccountId",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "token_type": {
+ "name": "token_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "session_state": {
+ "name": "session_state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "userId_idx": {
+ "name": "userId_idx",
+ "columns": ["userId"],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "account_userId_user_id_fk": {
+ "name": "account_userId_user_id_fk",
+ "tableFrom": "account",
+ "tableTo": "user",
+ "columnsFrom": ["userId"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "account_provider_providerAccountId_pk": {
+ "name": "account_provider_providerAccountId_pk",
+ "columns": ["provider", "providerAccountId"]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "app": {
+ "name": "app",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "icon_url": {
+ "name": "icon_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "href": {
+ "name": "href",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "app_id": {
+ "name": "app_id",
+ "columns": ["id"]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "boardGroupPermission": {
+ "name": "boardGroupPermission",
+ "columns": {
+ "board_id": {
+ "name": "board_id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "group_id": {
+ "name": "group_id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "permission": {
+ "name": "permission",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "boardGroupPermission_board_id_board_id_fk": {
+ "name": "boardGroupPermission_board_id_board_id_fk",
+ "tableFrom": "boardGroupPermission",
+ "tableTo": "board",
+ "columnsFrom": ["board_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "boardGroupPermission_group_id_group_id_fk": {
+ "name": "boardGroupPermission_group_id_group_id_fk",
+ "tableFrom": "boardGroupPermission",
+ "tableTo": "group",
+ "columnsFrom": ["group_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "boardGroupPermission_board_id_group_id_permission_pk": {
+ "name": "boardGroupPermission_board_id_group_id_permission_pk",
+ "columns": ["board_id", "group_id", "permission"]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "boardUserPermission": {
+ "name": "boardUserPermission",
+ "columns": {
+ "board_id": {
+ "name": "board_id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "permission": {
+ "name": "permission",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "boardUserPermission_board_id_board_id_fk": {
+ "name": "boardUserPermission_board_id_board_id_fk",
+ "tableFrom": "boardUserPermission",
+ "tableTo": "board",
+ "columnsFrom": ["board_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "boardUserPermission_user_id_user_id_fk": {
+ "name": "boardUserPermission_user_id_user_id_fk",
+ "tableFrom": "boardUserPermission",
+ "tableTo": "user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "boardUserPermission_board_id_user_id_permission_pk": {
+ "name": "boardUserPermission_board_id_user_id_permission_pk",
+ "columns": ["board_id", "user_id", "permission"]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "board": {
+ "name": "board",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(256)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "is_public": {
+ "name": "is_public",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "page_title": {
+ "name": "page_title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "meta_title": {
+ "name": "meta_title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "logo_image_url": {
+ "name": "logo_image_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "favicon_image_url": {
+ "name": "favicon_image_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "background_image_url": {
+ "name": "background_image_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "background_image_attachment": {
+ "name": "background_image_attachment",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "('fixed')"
+ },
+ "background_image_repeat": {
+ "name": "background_image_repeat",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "('no-repeat')"
+ },
+ "background_image_size": {
+ "name": "background_image_size",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "('cover')"
+ },
+ "primary_color": {
+ "name": "primary_color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "('#fa5252')"
+ },
+ "secondary_color": {
+ "name": "secondary_color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "('#fd7e14')"
+ },
+ "opacity": {
+ "name": "opacity",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 100
+ },
+ "custom_css": {
+ "name": "custom_css",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "column_count": {
+ "name": "column_count",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 10
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "board_creator_id_user_id_fk": {
+ "name": "board_creator_id_user_id_fk",
+ "tableFrom": "board",
+ "tableTo": "user",
+ "columnsFrom": ["creator_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "board_id": {
+ "name": "board_id",
+ "columns": ["id"]
+ }
+ },
+ "uniqueConstraints": {
+ "board_name_unique": {
+ "name": "board_name_unique",
+ "columns": ["name"]
+ }
+ }
+ },
+ "groupMember": {
+ "name": "groupMember",
+ "columns": {
+ "groupId": {
+ "name": "groupId",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "groupMember_groupId_group_id_fk": {
+ "name": "groupMember_groupId_group_id_fk",
+ "tableFrom": "groupMember",
+ "tableTo": "group",
+ "columnsFrom": ["groupId"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "groupMember_userId_user_id_fk": {
+ "name": "groupMember_userId_user_id_fk",
+ "tableFrom": "groupMember",
+ "tableTo": "user",
+ "columnsFrom": ["userId"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "groupMember_groupId_userId_pk": {
+ "name": "groupMember_groupId_userId_pk",
+ "columns": ["groupId", "userId"]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "groupPermission": {
+ "name": "groupPermission",
+ "columns": {
+ "groupId": {
+ "name": "groupId",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "permission": {
+ "name": "permission",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "groupPermission_groupId_group_id_fk": {
+ "name": "groupPermission_groupId_group_id_fk",
+ "tableFrom": "groupPermission",
+ "tableTo": "group",
+ "columnsFrom": ["groupId"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "group": {
+ "name": "group",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "group_owner_id_user_id_fk": {
+ "name": "group_owner_id_user_id_fk",
+ "tableFrom": "group",
+ "tableTo": "user",
+ "columnsFrom": ["owner_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "group_id": {
+ "name": "group_id",
+ "columns": ["id"]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "iconRepository": {
+ "name": "iconRepository",
+ "columns": {
+ "iconRepository_id": {
+ "name": "iconRepository_id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "iconRepository_slug": {
+ "name": "iconRepository_slug",
+ "type": "varchar(150)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "iconRepository_iconRepository_id": {
+ "name": "iconRepository_iconRepository_id",
+ "columns": ["iconRepository_id"]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "icon": {
+ "name": "icon",
+ "columns": {
+ "icon_id": {
+ "name": "icon_id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "icon_name": {
+ "name": "icon_name",
+ "type": "varchar(250)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "icon_url": {
+ "name": "icon_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "icon_checksum": {
+ "name": "icon_checksum",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "iconRepository_id": {
+ "name": "iconRepository_id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "icon_iconRepository_id_iconRepository_iconRepository_id_fk": {
+ "name": "icon_iconRepository_id_iconRepository_iconRepository_id_fk",
+ "tableFrom": "icon",
+ "tableTo": "iconRepository",
+ "columnsFrom": ["iconRepository_id"],
+ "columnsTo": ["iconRepository_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "icon_icon_id": {
+ "name": "icon_icon_id",
+ "columns": ["icon_id"]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "integration_item": {
+ "name": "integration_item",
+ "columns": {
+ "item_id": {
+ "name": "item_id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "integration_id": {
+ "name": "integration_id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "integration_item_item_id_item_id_fk": {
+ "name": "integration_item_item_id_item_id_fk",
+ "tableFrom": "integration_item",
+ "tableTo": "item",
+ "columnsFrom": ["item_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "integration_item_integration_id_integration_id_fk": {
+ "name": "integration_item_integration_id_integration_id_fk",
+ "tableFrom": "integration_item",
+ "tableTo": "integration",
+ "columnsFrom": ["integration_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "integration_item_item_id_integration_id_pk": {
+ "name": "integration_item_item_id_integration_id_pk",
+ "columns": ["item_id", "integration_id"]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "integrationSecret": {
+ "name": "integrationSecret",
+ "columns": {
+ "kind": {
+ "name": "kind",
+ "type": "varchar(16)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "integration_id": {
+ "name": "integration_id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "integration_secret__kind_idx": {
+ "name": "integration_secret__kind_idx",
+ "columns": ["kind"],
+ "isUnique": false
+ },
+ "integration_secret__updated_at_idx": {
+ "name": "integration_secret__updated_at_idx",
+ "columns": ["updated_at"],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "integrationSecret_integration_id_integration_id_fk": {
+ "name": "integrationSecret_integration_id_integration_id_fk",
+ "tableFrom": "integrationSecret",
+ "tableTo": "integration",
+ "columnsFrom": ["integration_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "integrationSecret_integration_id_kind_pk": {
+ "name": "integrationSecret_integration_id_kind_pk",
+ "columns": ["integration_id", "kind"]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "integration": {
+ "name": "integration",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "url": {
+ "name": "url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "kind": {
+ "name": "kind",
+ "type": "varchar(128)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "integration__kind_idx": {
+ "name": "integration__kind_idx",
+ "columns": ["kind"],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "integration_id": {
+ "name": "integration_id",
+ "columns": ["id"]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "invite": {
+ "name": "invite",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "token": {
+ "name": "token",
+ "type": "varchar(512)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expiration_date": {
+ "name": "expiration_date",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "invite_creator_id_user_id_fk": {
+ "name": "invite_creator_id_user_id_fk",
+ "tableFrom": "invite",
+ "tableTo": "user",
+ "columnsFrom": ["creator_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "invite_id": {
+ "name": "invite_id",
+ "columns": ["id"]
+ }
+ },
+ "uniqueConstraints": {
+ "invite_token_unique": {
+ "name": "invite_token_unique",
+ "columns": ["token"]
+ }
+ }
+ },
+ "item": {
+ "name": "item",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "section_id": {
+ "name": "section_id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "x_offset": {
+ "name": "x_offset",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "y_offset": {
+ "name": "y_offset",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "width": {
+ "name": "width",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "height": {
+ "name": "height",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "options": {
+ "name": "options",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "('{\"json\": {}}')"
+ },
+ "advanced_options": {
+ "name": "advanced_options",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "('{\"json\": {}}')"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "item_section_id_section_id_fk": {
+ "name": "item_section_id_section_id_fk",
+ "tableFrom": "item",
+ "tableTo": "section",
+ "columnsFrom": ["section_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "item_id": {
+ "name": "item_id",
+ "columns": ["id"]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "section": {
+ "name": "section",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "board_id": {
+ "name": "board_id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "position": {
+ "name": "position",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "section_board_id_board_id_fk": {
+ "name": "section_board_id_board_id_fk",
+ "tableFrom": "section",
+ "tableTo": "board",
+ "columnsFrom": ["board_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "section_id": {
+ "name": "section_id",
+ "columns": ["id"]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "serverSetting": {
+ "name": "serverSetting",
+ "columns": {
+ "key": {
+ "name": "key",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "('{\"json\": {}}')"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "serverSetting_key": {
+ "name": "serverSetting_key",
+ "columns": ["key"]
+ }
+ },
+ "uniqueConstraints": {
+ "serverSetting_key_unique": {
+ "name": "serverSetting_key_unique",
+ "columns": ["key"]
+ }
+ }
+ },
+ "session": {
+ "name": "session",
+ "columns": {
+ "sessionToken": {
+ "name": "sessionToken",
+ "type": "varchar(512)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires": {
+ "name": "expires",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "user_id_idx": {
+ "name": "user_id_idx",
+ "columns": ["userId"],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "session_userId_user_id_fk": {
+ "name": "session_userId_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": ["userId"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "session_sessionToken": {
+ "name": "session_sessionToken",
+ "columns": ["sessionToken"]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "user": {
+ "name": "user",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "emailVerified": {
+ "name": "emailVerified",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "salt": {
+ "name": "salt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "homeBoardId": {
+ "name": "homeBoardId",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_homeBoardId_board_id_fk": {
+ "name": "user_homeBoardId_board_id_fk",
+ "tableFrom": "user",
+ "tableTo": "board",
+ "columnsFrom": ["homeBoardId"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "user_id": {
+ "name": "user_id",
+ "columns": ["id"]
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "verificationToken": {
+ "name": "verificationToken",
+ "columns": {
+ "identifier": {
+ "name": "identifier",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "token": {
+ "name": "token",
+ "type": "varchar(512)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires": {
+ "name": "expires",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "verificationToken_identifier_token_pk": {
+ "name": "verificationToken_identifier_token_pk",
+ "columns": ["identifier", "token"]
+ }
+ },
+ "uniqueConstraints": {}
+ }
+ },
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ }
+}
diff --git a/packages/db/migrations/mysql/meta/_journal.json b/packages/db/migrations/mysql/meta/_journal.json
index 4c20d0621..bb349fd94 100644
--- a/packages/db/migrations/mysql/meta/_journal.json
+++ b/packages/db/migrations/mysql/meta/_journal.json
@@ -19,8 +19,15 @@
{
"idx": 2,
"version": "5",
+ "when": 1715980459023,
+ "tag": "0002_flimsy_deathbird",
+ "breakpoints": true
+ },
+ {
+ "idx": 3,
+ "version": "5",
"when": 1716148439439,
- "tag": "0002_freezing_black_panther",
+ "tag": "0003_freezing_black_panther",
"breakpoints": true
}
]
diff --git a/packages/db/migrations/sqlite/0002_cooing_sumo.sql b/packages/db/migrations/sqlite/0002_cooing_sumo.sql
new file mode 100644
index 000000000..a12cdb821
--- /dev/null
+++ b/packages/db/migrations/sqlite/0002_cooing_sumo.sql
@@ -0,0 +1 @@
+ALTER TABLE `item` ADD `advanced_options` text DEFAULT '{"json": {}}' NOT NULL;
\ No newline at end of file
diff --git a/packages/db/migrations/sqlite/0002_adorable_raider.sql b/packages/db/migrations/sqlite/0003_adorable_raider.sql
similarity index 100%
rename from packages/db/migrations/sqlite/0002_adorable_raider.sql
rename to packages/db/migrations/sqlite/0003_adorable_raider.sql
diff --git a/packages/db/migrations/sqlite/meta/0002_snapshot.json b/packages/db/migrations/sqlite/meta/0002_snapshot.json
index 66649ac0e..885a5df37 100644
--- a/packages/db/migrations/sqlite/meta/0002_snapshot.json
+++ b/packages/db/migrations/sqlite/meta/0002_snapshot.json
@@ -1,8 +1,8 @@
{
"version": "6",
"dialect": "sqlite",
- "id": "b72fe407-31bc-4dd0-8c36-dbb8e42ef708",
- "prevId": "2ed0ffc3-8612-42e7-bd8e-f5f8f3338a39",
+ "id": "5ad60251-8450-437d-9081-a456884120d2",
+ "prevId": "0575873a-9e10-4480-8d7d-c47198622c22",
"tables": {
"account": {
"name": "account",
@@ -877,6 +877,14 @@
"notNull": true,
"autoincrement": false,
"default": "'{\"json\": {}}'"
+ },
+ "advanced_options": {
+ "name": "advanced_options",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'{\"json\": {}}'"
}
},
"indexes": {},
@@ -948,36 +956,6 @@
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
- "serverSetting": {
- "name": "serverSetting",
- "columns": {
- "key": {
- "name": "key",
- "type": "text",
- "primaryKey": true,
- "notNull": true,
- "autoincrement": false
- },
- "value": {
- "name": "value",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "autoincrement": false,
- "default": "'{\"json\": {}}'"
- }
- },
- "indexes": {
- "serverSetting_key_unique": {
- "name": "serverSetting_key_unique",
- "columns": ["key"],
- "isUnique": true
- }
- },
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {}
- },
"session": {
"name": "session",
"columns": {
diff --git a/packages/db/migrations/sqlite/meta/0003_snapshot.json b/packages/db/migrations/sqlite/meta/0003_snapshot.json
new file mode 100644
index 000000000..7324558d3
--- /dev/null
+++ b/packages/db/migrations/sqlite/meta/0003_snapshot.json
@@ -0,0 +1,1152 @@
+{
+ "version": "6",
+ "dialect": "sqlite",
+ "id": "b72fe407-31bc-4dd0-8c36-dbb8e42ef708",
+ "prevId": "5ad60251-8450-437d-9081-a456884120d2",
+ "tables": {
+ "account": {
+ "name": "account",
+ "columns": {
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "providerAccountId": {
+ "name": "providerAccountId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "token_type": {
+ "name": "token_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "session_state": {
+ "name": "session_state",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "userId_idx": {
+ "name": "userId_idx",
+ "columns": ["userId"],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "account_userId_user_id_fk": {
+ "name": "account_userId_user_id_fk",
+ "tableFrom": "account",
+ "tableTo": "user",
+ "columnsFrom": ["userId"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "account_provider_providerAccountId_pk": {
+ "columns": ["provider", "providerAccountId"],
+ "name": "account_provider_providerAccountId_pk"
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "app": {
+ "name": "app",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "icon_url": {
+ "name": "icon_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "href": {
+ "name": "href",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "boardGroupPermission": {
+ "name": "boardGroupPermission",
+ "columns": {
+ "board_id": {
+ "name": "board_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "group_id": {
+ "name": "group_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "permission": {
+ "name": "permission",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "boardGroupPermission_board_id_board_id_fk": {
+ "name": "boardGroupPermission_board_id_board_id_fk",
+ "tableFrom": "boardGroupPermission",
+ "tableTo": "board",
+ "columnsFrom": ["board_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "boardGroupPermission_group_id_group_id_fk": {
+ "name": "boardGroupPermission_group_id_group_id_fk",
+ "tableFrom": "boardGroupPermission",
+ "tableTo": "group",
+ "columnsFrom": ["group_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "boardGroupPermission_board_id_group_id_permission_pk": {
+ "columns": ["board_id", "group_id", "permission"],
+ "name": "boardGroupPermission_board_id_group_id_permission_pk"
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "boardUserPermission": {
+ "name": "boardUserPermission",
+ "columns": {
+ "board_id": {
+ "name": "board_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "permission": {
+ "name": "permission",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "boardUserPermission_board_id_board_id_fk": {
+ "name": "boardUserPermission_board_id_board_id_fk",
+ "tableFrom": "boardUserPermission",
+ "tableTo": "board",
+ "columnsFrom": ["board_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "boardUserPermission_user_id_user_id_fk": {
+ "name": "boardUserPermission_user_id_user_id_fk",
+ "tableFrom": "boardUserPermission",
+ "tableTo": "user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "boardUserPermission_board_id_user_id_permission_pk": {
+ "columns": ["board_id", "permission", "user_id"],
+ "name": "boardUserPermission_board_id_user_id_permission_pk"
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "board": {
+ "name": "board",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "is_public": {
+ "name": "is_public",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "page_title": {
+ "name": "page_title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "meta_title": {
+ "name": "meta_title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "logo_image_url": {
+ "name": "logo_image_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "favicon_image_url": {
+ "name": "favicon_image_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "background_image_url": {
+ "name": "background_image_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "background_image_attachment": {
+ "name": "background_image_attachment",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'fixed'"
+ },
+ "background_image_repeat": {
+ "name": "background_image_repeat",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'no-repeat'"
+ },
+ "background_image_size": {
+ "name": "background_image_size",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'cover'"
+ },
+ "primary_color": {
+ "name": "primary_color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'#fa5252'"
+ },
+ "secondary_color": {
+ "name": "secondary_color",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'#fd7e14'"
+ },
+ "opacity": {
+ "name": "opacity",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 100
+ },
+ "custom_css": {
+ "name": "custom_css",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "column_count": {
+ "name": "column_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": 10
+ }
+ },
+ "indexes": {
+ "board_name_unique": {
+ "name": "board_name_unique",
+ "columns": ["name"],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "board_creator_id_user_id_fk": {
+ "name": "board_creator_id_user_id_fk",
+ "tableFrom": "board",
+ "tableTo": "user",
+ "columnsFrom": ["creator_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "groupMember": {
+ "name": "groupMember",
+ "columns": {
+ "groupId": {
+ "name": "groupId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "groupMember_groupId_group_id_fk": {
+ "name": "groupMember_groupId_group_id_fk",
+ "tableFrom": "groupMember",
+ "tableTo": "group",
+ "columnsFrom": ["groupId"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "groupMember_userId_user_id_fk": {
+ "name": "groupMember_userId_user_id_fk",
+ "tableFrom": "groupMember",
+ "tableTo": "user",
+ "columnsFrom": ["userId"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "groupMember_groupId_userId_pk": {
+ "columns": ["groupId", "userId"],
+ "name": "groupMember_groupId_userId_pk"
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "groupPermission": {
+ "name": "groupPermission",
+ "columns": {
+ "groupId": {
+ "name": "groupId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "permission": {
+ "name": "permission",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "groupPermission_groupId_group_id_fk": {
+ "name": "groupPermission_groupId_group_id_fk",
+ "tableFrom": "groupPermission",
+ "tableTo": "group",
+ "columnsFrom": ["groupId"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "group": {
+ "name": "group",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "group_owner_id_user_id_fk": {
+ "name": "group_owner_id_user_id_fk",
+ "tableFrom": "group",
+ "tableTo": "user",
+ "columnsFrom": ["owner_id"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "iconRepository": {
+ "name": "iconRepository",
+ "columns": {
+ "iconRepository_id": {
+ "name": "iconRepository_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "iconRepository_slug": {
+ "name": "iconRepository_slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "icon": {
+ "name": "icon",
+ "columns": {
+ "icon_id": {
+ "name": "icon_id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "icon_name": {
+ "name": "icon_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "icon_url": {
+ "name": "icon_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "icon_checksum": {
+ "name": "icon_checksum",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "iconRepository_id": {
+ "name": "iconRepository_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "icon_iconRepository_id_iconRepository_iconRepository_id_fk": {
+ "name": "icon_iconRepository_id_iconRepository_iconRepository_id_fk",
+ "tableFrom": "icon",
+ "tableTo": "iconRepository",
+ "columnsFrom": ["iconRepository_id"],
+ "columnsTo": ["iconRepository_id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "integration_item": {
+ "name": "integration_item",
+ "columns": {
+ "item_id": {
+ "name": "item_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "integration_id": {
+ "name": "integration_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "integration_item_item_id_item_id_fk": {
+ "name": "integration_item_item_id_item_id_fk",
+ "tableFrom": "integration_item",
+ "tableTo": "item",
+ "columnsFrom": ["item_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "integration_item_integration_id_integration_id_fk": {
+ "name": "integration_item_integration_id_integration_id_fk",
+ "tableFrom": "integration_item",
+ "tableTo": "integration",
+ "columnsFrom": ["integration_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "integration_item_item_id_integration_id_pk": {
+ "columns": ["integration_id", "item_id"],
+ "name": "integration_item_item_id_integration_id_pk"
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "integrationSecret": {
+ "name": "integrationSecret",
+ "columns": {
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "integration_id": {
+ "name": "integration_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "integration_secret__kind_idx": {
+ "name": "integration_secret__kind_idx",
+ "columns": ["kind"],
+ "isUnique": false
+ },
+ "integration_secret__updated_at_idx": {
+ "name": "integration_secret__updated_at_idx",
+ "columns": ["updated_at"],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "integrationSecret_integration_id_integration_id_fk": {
+ "name": "integrationSecret_integration_id_integration_id_fk",
+ "tableFrom": "integrationSecret",
+ "tableTo": "integration",
+ "columnsFrom": ["integration_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "integrationSecret_integration_id_kind_pk": {
+ "columns": ["integration_id", "kind"],
+ "name": "integrationSecret_integration_id_kind_pk"
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "integration": {
+ "name": "integration",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "url": {
+ "name": "url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "integration__kind_idx": {
+ "name": "integration__kind_idx",
+ "columns": ["kind"],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "invite": {
+ "name": "invite",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expiration_date": {
+ "name": "expiration_date",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "invite_token_unique": {
+ "name": "invite_token_unique",
+ "columns": ["token"],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {
+ "invite_creator_id_user_id_fk": {
+ "name": "invite_creator_id_user_id_fk",
+ "tableFrom": "invite",
+ "tableTo": "user",
+ "columnsFrom": ["creator_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "item": {
+ "name": "item",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "section_id": {
+ "name": "section_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "x_offset": {
+ "name": "x_offset",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "y_offset": {
+ "name": "y_offset",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "width": {
+ "name": "width",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "height": {
+ "name": "height",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "options": {
+ "name": "options",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'{\"json\": {}}'"
+ },
+ "advanced_options": {
+ "name": "advanced_options",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'{\"json\": {}}'"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "item_section_id_section_id_fk": {
+ "name": "item_section_id_section_id_fk",
+ "tableFrom": "item",
+ "tableTo": "section",
+ "columnsFrom": ["section_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "section": {
+ "name": "section",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "board_id": {
+ "name": "board_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "kind": {
+ "name": "kind",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "position": {
+ "name": "position",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "section_board_id_board_id_fk": {
+ "name": "section_board_id_board_id_fk",
+ "tableFrom": "section",
+ "tableTo": "board",
+ "columnsFrom": ["board_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "serverSetting": {
+ "name": "serverSetting",
+ "columns": {
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "'{\"json\": {}}'"
+ }
+ },
+ "indexes": {
+ "serverSetting_key_unique": {
+ "name": "serverSetting_key_unique",
+ "columns": ["key"],
+ "isUnique": true
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "session": {
+ "name": "session",
+ "columns": {
+ "sessionToken": {
+ "name": "sessionToken",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "userId": {
+ "name": "userId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires": {
+ "name": "expires",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {
+ "user_id_idx": {
+ "name": "user_id_idx",
+ "columns": ["userId"],
+ "isUnique": false
+ }
+ },
+ "foreignKeys": {
+ "session_userId_user_id_fk": {
+ "name": "session_userId_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": ["userId"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "user": {
+ "name": "user",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "emailVerified": {
+ "name": "emailVerified",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "salt": {
+ "name": "salt",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ },
+ "homeBoardId": {
+ "name": "homeBoardId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_homeBoardId_board_id_fk": {
+ "name": "user_homeBoardId_board_id_fk",
+ "tableFrom": "user",
+ "tableTo": "board",
+ "columnsFrom": ["homeBoardId"],
+ "columnsTo": ["id"],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "verificationToken": {
+ "name": "verificationToken",
+ "columns": {
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "expires": {
+ "name": "expires",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "verificationToken_identifier_token_pk": {
+ "columns": ["identifier", "token"],
+ "name": "verificationToken_identifier_token_pk"
+ }
+ },
+ "uniqueConstraints": {}
+ }
+ },
+ "enums": {},
+ "_meta": {
+ "schemas": {},
+ "tables": {},
+ "columns": {}
+ }
+}
diff --git a/packages/db/migrations/sqlite/meta/_journal.json b/packages/db/migrations/sqlite/meta/_journal.json
index 43c0f2a94..5d9bc2d12 100644
--- a/packages/db/migrations/sqlite/meta/_journal.json
+++ b/packages/db/migrations/sqlite/meta/_journal.json
@@ -19,8 +19,15 @@
{
"idx": 2,
"version": "6",
+ "when": 1715973963014,
+ "tag": "0002_cooing_sumo",
+ "breakpoints": true
+ },
+ {
+ "idx": 3,
+ "version": "6",
"when": 1716148434186,
- "tag": "0002_adorable_raider",
+ "tag": "0003_adorable_raider",
"breakpoints": true
}
]
diff --git a/packages/db/schema/mysql.ts b/packages/db/schema/mysql.ts
index 8b9d70bf9..d53a9ec30 100644
--- a/packages/db/schema/mysql.ts
+++ b/packages/db/schema/mysql.ts
@@ -243,6 +243,7 @@ export const items = mysqlTable("item", {
width: int("width").notNull(),
height: int("height").notNull(),
options: text("options").default('{"json": {}}').notNull(), // empty superjson object
+ advancedOptions: text("advanced_options").default('{"json": {}}').notNull(), // empty superjson object
});
export const apps = mysqlTable("app", {
diff --git a/packages/db/schema/sqlite.ts b/packages/db/schema/sqlite.ts
index 905225ad2..f7c3c87e5 100644
--- a/packages/db/schema/sqlite.ts
+++ b/packages/db/schema/sqlite.ts
@@ -246,6 +246,7 @@ export const items = sqliteTable("item", {
width: int("width").notNull(),
height: int("height").notNull(),
options: text("options").default('{"json": {}}').notNull(), // empty superjson object
+ advancedOptions: text("advanced_options").default('{"json": {}}').notNull(), // empty superjson object
});
export const apps = sqliteTable("app", {
diff --git a/packages/modals/src/index.tsx b/packages/modals/src/index.tsx
index 26265c7bb..68876a6b6 100644
--- a/packages/modals/src/index.tsx
+++ b/packages/modals/src/index.tsx
@@ -103,7 +103,6 @@ const ActiveModal = ({ modal, state, handleCloseModal }: ActiveModalProps) => {
{
fontSize: "1.25rem",
fontWeight: 500,
},
+ inner: {
+ display: modal.id === state.current?.id ? undefined : "none",
+ },
}}
trapFocus={modal.id === state.current?.id}
{...otherModalProps}
diff --git a/packages/translation/src/lang/en.ts b/packages/translation/src/lang/en.ts
index fc382d453..757502ffb 100644
--- a/packages/translation/src/lang/en.ts
+++ b/packages/translation/src/lang/en.ts
@@ -469,6 +469,10 @@ export default {
multiSelect: {
placeholder: "Pick one or more values",
},
+ multiText: {
+ placeholder: "Add more values",
+ addLabel: `Add {value}`,
+ },
select: {
placeholder: "Pick value",
badge: {
@@ -594,10 +598,17 @@ export default {
},
edit: {
title: "Edit item",
+ advancedOptions: {
+ label: "Advanced options",
+ title: "Advanced item options",
+ },
field: {
integrations: {
label: "Integrations",
},
+ customCssClasses: {
+ label: "Custom css classes",
+ },
},
},
remove: {
@@ -944,7 +955,13 @@ export default {
label: "Opacity",
},
customCss: {
- label: "Custom CSS",
+ label: "Custom css for this board",
+ description: "Further, customize your dashboard using CSS, only recommended for experienced users",
+ customClassesAlert: {
+ title: "Custom classes",
+ description:
+ "You can add custom classes to your board items in the advanced options of each item and use them in the custom CSS above.",
+ },
},
columnCount: {
label: "Column count",
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 111b352f0..783154b52 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -29,7 +29,8 @@
"typescript": "^5.4.5"
},
"dependencies": {
- "@homarr/log": "workspace:^0.1.0"
+ "@homarr/log": "workspace:^0.1.0",
+ "@homarr/translation": "workspace:^0.1.0"
},
"eslintConfig": {
"extends": [
diff --git a/packages/ui/src/components/index.tsx b/packages/ui/src/components/index.tsx
index 5155f07de..425545c50 100644
--- a/packages/ui/src/components/index.tsx
+++ b/packages/ui/src/components/index.tsx
@@ -5,3 +5,4 @@ export { UserAvatar } from "./user-avatar";
export { UserAvatarGroup } from "./user-avatar-group";
export { TablePagination } from "./table-pagination";
export { SearchInput } from "./search-input";
+export { TextMultiSelect } from "./text-multi-select";
diff --git a/packages/ui/src/components/text-multi-select.tsx b/packages/ui/src/components/text-multi-select.tsx
new file mode 100644
index 000000000..e63d570dc
--- /dev/null
+++ b/packages/ui/src/components/text-multi-select.tsx
@@ -0,0 +1,98 @@
+"use client";
+
+import type { FocusEventHandler } from "react";
+import { useState } from "react";
+import { Combobox, Group, Pill, PillsInput, Text, useCombobox } from "@mantine/core";
+import { IconPlus } from "@tabler/icons-react";
+
+import { useI18n } from "@homarr/translation/client";
+
+interface TextMultiSelectProps {
+ label: string;
+ value?: string[];
+ onChange: (value: string[]) => void;
+ onFocus?: FocusEventHandler;
+ onBlur?: FocusEventHandler;
+ error?: string;
+}
+
+export const TextMultiSelect = ({ label, value = [], onChange, onBlur, onFocus, error }: TextMultiSelectProps) => {
+ const t = useI18n();
+ const combobox = useCombobox({
+ onDropdownClose: () => combobox.resetSelectedOption(),
+ onDropdownOpen: () => combobox.updateSelectedOptionIndex("active"),
+ });
+
+ const [search, setSearch] = useState("");
+
+ const exactOptionMatch = value.some((item) => item === search);
+
+ const handleValueSelect = (selectedValue: string) => {
+ setSearch("");
+
+ if (selectedValue === "$create") {
+ onChange([...value, search]);
+ } else {
+ onChange(value.filter((filterValue) => filterValue !== selectedValue));
+ }
+ };
+
+ const handleValueRemove = (removedValue: string) =>
+ onChange(value.filter((filterValue) => filterValue !== removedValue));
+
+ const values = value.map((item) => (
+ handleValueRemove(item)}>
+ {item}
+
+ ));
+
+ return (
+
+
+ combobox.openDropdown()}>
+
+ {values}
+
+
+ {
+ onFocus?.(event);
+ combobox.openDropdown();
+ }}
+ onBlur={(event) => {
+ onBlur?.(event);
+ combobox.closeDropdown();
+ }}
+ value={search}
+ placeholder={t("common.multiText.placeholder")}
+ onChange={(event) => {
+ combobox.updateSelectedOptionIndex();
+ setSearch(event.currentTarget.value);
+ }}
+ onKeyDown={(event) => {
+ if (event.key === "Backspace" && search.length === 0) {
+ event.preventDefault();
+ handleValueRemove(value.at(-1)!);
+ }
+ }}
+ />
+
+
+
+
+
+ {!exactOptionMatch && search.trim().length > 0 && (
+
+
+
+
+
+ {t("common.multiText.addLabel", { value: search })}
+
+
+
+
+ )}
+
+ );
+};
diff --git a/packages/validation/src/index.ts b/packages/validation/src/index.ts
index 2d7f3701d..34c4211c5 100644
--- a/packages/validation/src/index.ts
+++ b/packages/validation/src/index.ts
@@ -18,4 +18,10 @@ export const validation = {
icons: iconsSchemas,
};
-export { createSectionSchema, sharedItemSchema, type BoardItemIntegration } from "./shared";
+export {
+ createSectionSchema,
+ sharedItemSchema,
+ itemAdvancedOptionsSchema,
+ type BoardItemIntegration,
+ type BoardItemAdvancedOptions,
+} from "./shared";
diff --git a/packages/validation/src/shared.ts b/packages/validation/src/shared.ts
index 0aa6feaf1..46b2de2c1 100644
--- a/packages/validation/src/shared.ts
+++ b/packages/validation/src/shared.ts
@@ -13,6 +13,12 @@ export const integrationSchema = z.object({
export type BoardItemIntegration = z.infer;
+export const itemAdvancedOptionsSchema = z.object({
+ customCssClasses: z.array(z.string()).default([]),
+});
+
+export type BoardItemAdvancedOptions = z.infer;
+
export const sharedItemSchema = z.object({
id: z.string(),
xOffset: z.number(),
@@ -20,6 +26,7 @@ export const sharedItemSchema = z.object({
height: z.number(),
width: z.number(),
integrations: z.array(integrationSchema),
+ advancedOptions: itemAdvancedOptionsSchema,
});
export const commonItemSchema = z
diff --git a/packages/widgets/package.json b/packages/widgets/package.json
index dde1e0d8a..a31e97954 100644
--- a/packages/widgets/package.json
+++ b/packages/widgets/package.json
@@ -23,6 +23,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
+ "@types/prismjs": "^1.26.4",
"@types/video.js": "^7.3.58",
"eslint": "^8.57.0",
"typescript": "^5.4.5"
@@ -40,17 +41,15 @@
"@homarr/form": "workspace:^0.1.0",
"@homarr/modals": "workspace:^0.1.0",
"@homarr/notifications": "workspace:^0.1.0",
- "@homarr/spotlight": "workspace:^0.1.0",
"@homarr/redis": "workspace:^0.1.0",
+ "@homarr/spotlight": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
- "@tiptap/extension-link": "^2.4.0",
- "@tiptap/react": "^2.4.0",
- "@tiptap/starter-kit": "^2.4.0",
"@tiptap/extension-color": "2.4.0",
"@tiptap/extension-highlight": "2.4.0",
"@tiptap/extension-image": "2.4.0",
+ "@tiptap/extension-link": "^2.4.0",
"@tiptap/extension-table": "2.4.0",
"@tiptap/extension-table-cell": "2.4.0",
"@tiptap/extension-table-header": "2.4.0",
@@ -60,6 +59,10 @@
"@tiptap/extension-text-align": "2.4.0",
"@tiptap/extension-text-style": "2.4.0",
"@tiptap/extension-underline": "2.4.0",
+ "@tiptap/react": "^2.4.0",
+ "@tiptap/starter-kit": "^2.4.0",
+ "prismjs": "^1.29.0",
+ "react-simple-code-editor": "^0.13.1",
"video.js": "^8.12.0"
}
}
diff --git a/packages/widgets/src/_inputs/form.ts b/packages/widgets/src/_inputs/form.ts
index a0199b55f..207eee02d 100644
--- a/packages/widgets/src/_inputs/form.ts
+++ b/packages/widgets/src/_inputs/form.ts
@@ -4,4 +4,5 @@ import { createFormContext } from "@homarr/form";
import type { WidgetEditModalState } from "../modals/widget-edit-modal";
-export const [FormProvider, useFormContext, useForm] = createFormContext();
+export const [FormProvider, useFormContext, useForm] =
+ createFormContext>();
diff --git a/packages/widgets/src/modals/widget-advanced-options-modal.tsx b/packages/widgets/src/modals/widget-advanced-options-modal.tsx
new file mode 100644
index 000000000..674bc8f9c
--- /dev/null
+++ b/packages/widgets/src/modals/widget-advanced-options-modal.tsx
@@ -0,0 +1,52 @@
+"use client";
+
+import { Button, Group, Stack } from "@mantine/core";
+
+import { useForm } from "@homarr/form";
+import { createModal } from "@homarr/modals";
+import { useI18n } from "@homarr/translation/client";
+import { TextMultiSelect } from "@homarr/ui";
+import type { BoardItemAdvancedOptions } from "@homarr/validation";
+
+interface InnerProps {
+ advancedOptions: BoardItemAdvancedOptions;
+ onSuccess: (options: BoardItemAdvancedOptions) => void;
+}
+
+export const WidgetAdvancedOptionsModal = createModal(({ actions, innerProps }) => {
+ const t = useI18n();
+ const form = useForm({
+ initialValues: innerProps.advancedOptions,
+ });
+ const handleSubmit = (values: BoardItemAdvancedOptions) => {
+ innerProps.onSuccess(values);
+ actions.closeModal();
+ };
+
+ return (
+
+ );
+}).withOptions({
+ defaultTitle(t) {
+ return t("item.edit.advancedOptions.title");
+ },
+ size: "lg",
+ transitionProps: {
+ duration: 0,
+ },
+});
diff --git a/packages/widgets/src/modals/widget-edit-modal.tsx b/packages/widgets/src/modals/widget-edit-modal.tsx
index 51531ccbd..cad7cf18f 100644
--- a/packages/widgets/src/modals/widget-edit-modal.tsx
+++ b/packages/widgets/src/modals/widget-edit-modal.tsx
@@ -1,21 +1,25 @@
"use client";
+import { useState } from "react";
import { Button, Group, Stack } from "@mantine/core";
import type { WidgetKind } from "@homarr/definitions";
-import { createModal } from "@homarr/modals";
+import { createModal, useModalAction } from "@homarr/modals";
import { useI18n } from "@homarr/translation/client";
import type { BoardItemIntegration } from "@homarr/validation";
import { widgetImports } from "..";
import { getInputForType } from "../_inputs";
import { FormProvider, useForm } from "../_inputs/form";
+import type { BoardItemAdvancedOptions } from "../../../validation/src/shared";
import type { OptionsBuilderResult } from "../options";
import type { IntegrationSelectOption } from "../widget-integration-select";
import { WidgetIntegrationSelect } from "../widget-integration-select";
+import { WidgetAdvancedOptionsModal } from "./widget-advanced-options-modal";
export interface WidgetEditModalState {
options: Record;
+ advancedOptions: BoardItemAdvancedOptions;
integrations: BoardItemIntegration[];
}
@@ -29,16 +33,21 @@ interface ModalProps {
export const WidgetEditModal = createModal>(({ actions, innerProps }) => {
const t = useI18n();
+ const [advancedOptions, setAdvancedOptions] = useState(innerProps.value.advancedOptions);
const form = useForm({
initialValues: innerProps.value,
});
+ const { openModal } = useModalAction(WidgetAdvancedOptionsModal);
const { definition } = widgetImports[innerProps.kind];
return (