diff --git a/.env b/.env
new file mode 100644
index 000000000..4b400d5e7
--- /dev/null
+++ b/.env
@@ -0,0 +1 @@
+PASSWORD=TOTOISCOOL
\ No newline at end of file
diff --git a/data/configs/default.json b/data/configs/default.json
index 7d7dc6277..2b722842e 100644
--- a/data/configs/default.json
+++ b/data/configs/default.json
@@ -1,23 +1,30 @@
{
- "schemaVersion": "1.0",
+ "schemaVersion": 1,
"configProperties": {
"name": "default"
},
- "categories": [],
+ "categories": [
+ {
+ "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f",
+ "position": 0,
+ "name": "Example Category"
+ }
+ ],
"wrappers": [
{
- "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33e",
+ "id": "default",
"position": 1
}
],
"apps": [
{
- "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a",
- "name": "Documentation",
- "url": "https://homarr.dev",
+ "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a337",
+ "name": "Community",
+ "url": "https://discord.com/invite/aCsmEV5RgA",
"behaviour": {
- "onClickUrl": "https://homarr.dev",
- "isOpeningInNewTab": true
+ "onClickUrl": "https://discord.com/invite/aCsmEV5RgA",
+ "isOpeningNewTab": true,
+ "externalUrl": "https://discord.com/invite/aCsmEV5RgA"
},
"network": {
"enabledStatusChecker": false,
@@ -26,7 +33,7 @@
]
},
"appearance": {
- "iconUrl": "/imgs/logo/logo.png"
+ "iconUrl": "https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/discord.png"
},
"integration": {
"type": null,
@@ -35,7 +42,46 @@
"area": {
"type": "wrapper",
"properties": {
- "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33e"
+ "id": "default"
+ }
+ },
+ "shape": {
+ "location": {
+ "x": 3,
+ "y": 0
+ },
+ "size": {
+ "width": 3,
+ "height": 3
+ }
+ }
+ },
+ {
+ "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990",
+ "name": "Donate",
+ "url": "https://ko-fi.com/ajnart",
+ "behaviour": {
+ "onClickUrl": "https://ko-fi.com/ajnart",
+ "externalUrl": "https://ko-fi.com/ajnart",
+ "isOpeningNewTab": true
+ },
+ "network": {
+ "enabledStatusChecker": false,
+ "okStatus": [
+ 200
+ ]
+ },
+ "appearance": {
+ "iconUrl": "https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/61e1116779fc0a9bd5bdbcc7_Frame%206.png"
+ },
+ "integration": {
+ "type": null,
+ "properties": []
+ },
+ "area": {
+ "type": "wrapper",
+ "properties": {
+ "id": "default"
}
},
"shape": {
@@ -55,7 +101,8 @@
"url": "https://github.com/ajnart/homarr",
"behaviour": {
"onClickUrl": "https://github.com/ajnart/homarr",
- "isOpeningInNewTab": true
+ "externalUrl": "https://github.com/ajnart/homarr",
+ "isOpeningNewTab": true
},
"network": {
"enabledStatusChecker": false,
@@ -73,107 +120,7 @@
"area": {
"type": "wrapper",
"properties": {
- "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33e"
- }
- },
- "shape": {
- "location": {
- "x": 3,
- "y": 0
- },
- "size": {
- "width": 3,
- "height": 3
- }
- }
- },
- {
- "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a337",
- "name": "Community",
- "url": "https://discord.com/invite/aCsmEV5RgA",
- "behaviour": {
- "onClickUrl": "https://discord.com/invite/aCsmEV5RgA",
- "isOpeningInNewTab": true
- },
- "network": {
- "enabledStatusChecker": false,
- "okStatus": [
- 200
- ]
- },
- "appearance": {
- "iconUrl": "https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/discord.png"
- },
- "integration": {
- "type": null,
- "properties": []
- },
- "area": {
- "type": "wrapper",
- "properties": {
- "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33e"
- }
- },
- "shape": {
- "location": {
- "x": 6,
- "y": 0
- },
- "size": {
- "width": 3,
- "height": 3
- }
- }
- },
- {
- "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a990",
- "name": "Donate",
- "url": "https://ko-fi.com/ajnart",
- "behaviour": {
- "onClickUrl": "https://ko-fi.com/ajnart",
- "isOpeningInNewTab": true
- },
- "network": {
- "enabledStatusChecker": false,
- "okStatus": [
- 200
- ]
- },
- "appearance": {
- "iconUrl": "https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/61e1116779fc0a9bd5bdbcc7_Frame%206.png"
- },
- "integration": {
- "type": null,
- "properties": []
- },
- "area": {
- "type": "wrapper",
- "properties": {
- "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33e"
- }
- },
- "shape": {
- "location": {
- "x": 9,
- "y": 0
- },
- "size": {
- "width": 3,
- "height": 3
- }
- }
- }
- ],
- "widgets": [
- {
- "id": "date",
- "properties": {
- "display24HourFormat": true
- },
- "area": {
- "type": "wrapper",
- "properties": {
- "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33e"
+ "id": "default"
}
},
"shape": {
@@ -181,11 +128,227 @@
"x": 0,
"y": 3
},
+ "size": {
+ "width": 3,
+ "height": 3
+ }
+ }
+ },
+ {
+ "id": "5df743d9-5cb1-457c-85d2-64ff86855652",
+ "name": "Your app",
+ "url": "https://homarr.dev",
+ "appearance": {
+ "iconUrl": "/imgs/logo/logo.png"
+ },
+ "network": {
+ "enabledStatusChecker": false,
+ "okStatus": []
+ },
+ "behaviour": {
+ "isOpeningNewTab": true,
+ "externalUrl": "https://homarr.dev"
+ },
+ "area": {
+ "type": "wrapper",
+ "properties": {
+ "id": "default"
+ }
+ },
+ "shape": {
+ "location": {
+ "x": 15,
+ "y": 5
+ },
+ "size": {
+ "width": 5,
+ "height": 4
+ }
+ },
+ "integration": {
+ "type": null,
+ "properties": []
+ }
+ },
+ {
+ "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33a",
+ "name": "Documentation",
+ "url": "https://homarr.dev",
+ "behaviour": {
+ "onClickUrl": "https://homarr.dev",
+ "externalUrl": "https://homarr.dev",
+ "isOpeningNewTab": true
+ },
+ "network": {
+ "enabledStatusChecker": false,
+ "okStatus": [
+ 200
+ ]
+ },
+ "appearance": {
+ "iconUrl": "/imgs/logo/logo.png"
+ },
+ "integration": {
+ "type": null,
+ "properties": []
+ },
+ "area": {
+ "type": "wrapper",
+ "properties": {
+ "id": "default"
+ }
+ },
+ "shape": {
+ "location": {
+ "x": 3,
+ "y": 3
+ },
+ "size": {
+ "width": 3,
+ "height": 3
+ }
+ }
+ },
+ {
+ "id": "76217a87-7151-42d0-b0cf-1b72aef63f83",
+ "name": "Small app",
+ "url": "https://homarr.dev",
+ "appearance": {
+ "iconUrl": "/imgs/logo/logo.png"
+ },
+ "network": {
+ "enabledStatusChecker": false,
+ "okStatus": []
+ },
+ "behaviour": {
+ "isOpeningNewTab": true,
+ "externalUrl": "https://homarr.dev"
+ },
+ "area": {
+ "type": "category",
+ "properties": {
+ "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f"
+ }
+ },
+ "shape": {
+ "location": {
+ "x": 17,
+ "y": 0
+ },
+ "size": {
+ "width": 2,
+ "height": 2
+ }
+ },
+ "integration": {
+ "type": null,
+ "properties": []
+ }
+ },
+ {
+ "id": "e41a11f5-9c6e-41bc-ac0e-4c4c47582faa",
+ "name": "Your app",
+ "url": "https://homarr.dev",
+ "appearance": {
+ "iconUrl": "/imgs/logo/logo.png"
+ },
+ "network": {
+ "enabledStatusChecker": false,
+ "okStatus": []
+ },
+ "behaviour": {
+ "isOpeningNewTab": true,
+ "externalUrl": ""
+ },
+ "area": {
+ "type": "wrapper",
+ "properties": {
+ "id": "default"
+ }
+ },
+ "shape": {
+ "location": {
+ "x": 0,
+ "y": 6
+ },
+ "size": {
+ "width": 2,
+ "height": 2
+ }
+ },
+ "integration": {
+ "type": null,
+ "properties": []
+ }
+ }
+ ],
+ "widgets": [
+ {
+ "id": "weather",
+ "properties": {
+ "displayInFahrenheit": false,
+ "location": "Paris"
+ },
+ "area": {
+ "type": "category",
+ "properties": {
+ "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f"
+ }
+ },
+ "shape": {
+ "location": {
+ "x": 6,
+ "y": 0
+ },
"size": {
"width": 4,
"height": 2
}
}
+ },
+ {
+ "id": "date",
+ "properties": {
+ "display24HourFormat": true
+ },
+ "area": {
+ "type": "category",
+ "properties": {
+ "id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33f"
+ }
+ },
+ "shape": {
+ "location": {
+ "x": 0,
+ "y": 0
+ },
+ "size": {
+ "width": 4,
+ "height": 2
+ }
+ }
+ },
+ {
+ "id": "calendar",
+ "properties": {
+ "sundayStart": false
+ },
+ "area": {
+ "type": "wrapper",
+ "properties": {
+ "id": "default"
+ }
+ },
+ "shape": {
+ "location": {
+ "x": 15,
+ "y": 0
+ },
+ "size": {
+ "width": 5,
+ "height": 5
+ }
+ }
}
],
"settings": {
diff --git a/src/components/Dashboard/Modals/EditAppModal/Tabs/GeneralTab/GeneralTab.tsx b/src/components/Dashboard/Modals/EditAppModal/Tabs/GeneralTab/GeneralTab.tsx
index 0cf437e02..56a4e5e54 100644
--- a/src/components/Dashboard/Modals/EditAppModal/Tabs/GeneralTab/GeneralTab.tsx
+++ b/src/components/Dashboard/Modals/EditAppModal/Tabs/GeneralTab/GeneralTab.tsx
@@ -20,7 +20,6 @@ export const GeneralTab = ({ form, openTab }: GeneralTabProps) => {
description={t('general.appname.description')}
placeholder="My example app"
variant="default"
- mb="md"
withAsterisk
required
{...form.getInputProps('name')}
@@ -45,7 +44,7 @@ export const GeneralTab = ({ form, openTab }: GeneralTabProps) => {
description={t('general.externalAddress.description')}
placeholder="https://homarr.mywebsite.com/"
variant="default"
- mb="md"
+ required
{...form.getInputProps('behaviour.externalUrl')}
/>
diff --git a/src/components/Dashboard/Tiles/Widgets/WidgetsEditModal.tsx b/src/components/Dashboard/Tiles/Widgets/WidgetsEditModal.tsx
index caae1b41b..70ef85a66 100644
--- a/src/components/Dashboard/Tiles/Widgets/WidgetsEditModal.tsx
+++ b/src/components/Dashboard/Tiles/Widgets/WidgetsEditModal.tsx
@@ -3,6 +3,7 @@ import { ContextModalProps } from '@mantine/modals';
import { useTranslation } from 'next-i18next';
import { useState } from 'react';
import Widgets from '../../../../widgets';
+import type { IWidgetOptionValue } from '../../../../widgets/widgets';
import { useConfigContext } from '../../../../config/provider';
import { useConfigStore } from '../../../../config/store';
import { IWidget } from '../../../../widgets/widgets';
@@ -23,6 +24,8 @@ export const WidgetsEditModal = ({
const [moduleProperties, setModuleProperties] = useState(innerProps.options);
const items = Object.entries(moduleProperties ?? {}) as [string, IntegrationOptionsValueType][];
+ // Find the Key in the "Widgets" Object that matches the widgetId
+ const currentWidgetDefinition = Widgets[innerProps.widgetId as keyof typeof Widgets];
const { name: configName } = useConfigContext();
const updateConfig = useConfigStore((x) => x.updateConfig);
@@ -63,33 +66,38 @@ export const WidgetsEditModal = ({
return (
- {items.map(([key, value]) => (
- <>
- {typeof value === 'boolean' ? (
- handleChange(key, ev.currentTarget.checked)}
- />
- ) : null}
- {typeof value === 'string' ? (
- handleChange(key, ev.currentTarget.value)}
- />
- ) : null}
- {typeof value === 'object' && Array.isArray(value) ? (
- handleChange(key, v)}
- />
- ) : null}
- >
- ))}
-
+ {items.map(([key, value]) => {
+ const option = (currentWidgetDefinition as any).options[key] as IWidgetOptionValue;
+ switch (option.type) {
+ case 'switch':
+ return (
+ handleChange(key, ev.currentTarget.checked)}
+ />
+ );
+ case 'text':
+ return (
+ handleChange(key, ev.currentTarget.value)}
+ />
+ );
+ case 'multi-select':
+ return (
+ handleChange(key, v)}
+ />
+ );
+ default:
+ return null;
+ }
+ })}
);
};
+
+//
+// {items.map(([key, value]) => (
+// <>
+// {typeof value === 'boolean' ? (
+// handleChange(key, ev.currentTarget.checked)}
+// />
+// ) : null}
+// {typeof value === 'string' ? (
+// handleChange(key, ev.currentTarget.value)}
+// />
+// ) : null}
+// {typeof value === 'object' && Array.isArray(value) ? (
+// handleChange(key, v)}
+// />
+// ) : null}
+// >
+// ))}
+
+//
+//
+//
+//
+//
+// );
+// };
diff --git a/src/components/Dashboard/Wrappers/Category/CategoryEditMenu.tsx b/src/components/Dashboard/Wrappers/Category/CategoryEditMenu.tsx
index b49fb4cde..24c89bc8f 100644
--- a/src/components/Dashboard/Wrappers/Category/CategoryEditMenu.tsx
+++ b/src/components/Dashboard/Wrappers/Category/CategoryEditMenu.tsx
@@ -6,6 +6,7 @@ import {
IconRowInsertTop,
IconRowInsertBottom,
IconEdit,
+ IconTrash,
} from '@tabler/icons';
import { useConfigContext } from '../../../../config/provider';
import { CategoryType } from '../../../../types/category';
@@ -17,11 +18,11 @@ interface CategoryEditMenuProps {
export const CategoryEditMenu = ({ category }: CategoryEditMenuProps) => {
const { name: configName } = useConfigContext();
- const { addCategoryAbove, addCategoryBelow, moveCategoryUp, moveCategoryDown, edit } =
+ const { addCategoryAbove, addCategoryBelow, moveCategoryUp, moveCategoryDown, edit, remove } =
useCategoryActions(configName, category);
return (
-