mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-09 15:05:48 +01:00
✨ Change integration structure to array and rename to widgets in config
This commit is contained in:
@@ -164,7 +164,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"integrations": [],
|
"widgets": [{
|
||||||
|
"id": "date",
|
||||||
|
"properties": {
|
||||||
|
"display24HourFormat": true
|
||||||
|
},
|
||||||
|
"area": {
|
||||||
|
"type": "wrapper",
|
||||||
|
"properties": {
|
||||||
|
"id": "47af36c0-47c1-4e5b-bfc7-ad645ee6a33e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"shape": {
|
||||||
|
"location": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 3
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"width": 4,
|
||||||
|
"height": 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
"settings": {
|
"settings": {
|
||||||
"common": {
|
"common": {
|
||||||
"searchEngine": {
|
"searchEngine": {
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ export const ChangeIntegrationPositionModal = ({
|
|||||||
|
|
||||||
updateConfig(configName, (prev) => ({
|
updateConfig(configName, (prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
integrations: {
|
widgets: {
|
||||||
...prev.integrations,
|
...prev.widgets,
|
||||||
[innerProps.integration]: {
|
[innerProps.integration]: {
|
||||||
...prev.integrations[innerProps.integration],
|
...prev.widgets[innerProps.integration],
|
||||||
shape: {
|
shape: {
|
||||||
location: {
|
location: {
|
||||||
x,
|
x,
|
||||||
@@ -55,10 +55,10 @@ export const ChangeIntegrationPositionModal = ({
|
|||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
heightData={heightData}
|
heightData={heightData}
|
||||||
widthData={widthData}
|
widthData={widthData}
|
||||||
initialX={innerProps.module.shape.location.x}
|
initialX={innerProps.widget.shape.location.x}
|
||||||
initialY={innerProps.module.shape.location.y}
|
initialY={innerProps.widget.shape.location.y}
|
||||||
initialWidth={innerProps.module.shape.size.width}
|
initialWidth={innerProps.widget.shape.size.width}
|
||||||
initialHeight={innerProps.module.shape.size.height}
|
initialHeight={innerProps.widget.shape.size.height}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { ReactNode, RefObject } from 'react';
|
|||||||
|
|
||||||
interface GridstackTileWrapperProps {
|
interface GridstackTileWrapperProps {
|
||||||
id: string;
|
id: string;
|
||||||
type: 'app' | 'module';
|
type: 'app' | 'widget';
|
||||||
x?: number;
|
x?: number;
|
||||||
y?: number;
|
y?: number;
|
||||||
width?: number;
|
width?: number;
|
||||||
|
|||||||
@@ -47,15 +47,15 @@ export const WidgetsEditModal = ({
|
|||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
updateConfig(configName, (prev) => ({
|
updateConfig(configName, (prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
integrations: {
|
widgets: {
|
||||||
...prev.integrations,
|
...prev.widgets,
|
||||||
[innerProps.integration]:
|
[innerProps.integration]:
|
||||||
'properties' in (prev.integrations[innerProps.integration] ?? {})
|
'properties' in (prev.widgets[innerProps.integration] ?? {})
|
||||||
? {
|
? {
|
||||||
...prev.integrations[innerProps.integration],
|
...prev.widgets[innerProps.integration],
|
||||||
properties: moduleProperties,
|
properties: moduleProperties,
|
||||||
}
|
}
|
||||||
: prev.integrations[innerProps.integration],
|
: prev.widgets[innerProps.integration],
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
context.closeModal(id);
|
context.closeModal(id);
|
||||||
|
|||||||
@@ -8,18 +8,18 @@ import { WidgetsRemoveModalInnerProps } from './WidgetsRemoveModal';
|
|||||||
|
|
||||||
export type WidgetChangePositionModalInnerProps = {
|
export type WidgetChangePositionModalInnerProps = {
|
||||||
integration: string;
|
integration: string;
|
||||||
module: IWidget<string, any>;
|
widget: IWidget<string, any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface WidgetsMenuProps {
|
interface WidgetsMenuProps {
|
||||||
integration: string;
|
integration: string;
|
||||||
module: IWidget<string, any> | undefined;
|
widget: IWidget<string, any> | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WidgetsMenu = ({ integration, module }: WidgetsMenuProps) => {
|
export const WidgetsMenu = ({ integration, widget }: WidgetsMenuProps) => {
|
||||||
const { t } = useTranslation(`modules/${integration}`);
|
const { t } = useTranslation(`modules/${integration}`);
|
||||||
|
|
||||||
if (!module) return null;
|
if (!widget) return null;
|
||||||
|
|
||||||
const handleDeleteClick = () => {
|
const handleDeleteClick = () => {
|
||||||
openContextModalGeneric<WidgetsRemoveModalInnerProps>({
|
openContextModalGeneric<WidgetsRemoveModalInnerProps>({
|
||||||
@@ -38,7 +38,7 @@ export const WidgetsMenu = ({ integration, module }: WidgetsMenuProps) => {
|
|||||||
title: null,
|
title: null,
|
||||||
innerProps: {
|
innerProps: {
|
||||||
integration,
|
integration,
|
||||||
module,
|
widget: widget,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -49,7 +49,7 @@ export const WidgetsMenu = ({ integration, module }: WidgetsMenuProps) => {
|
|||||||
title: <Title order={4}>{t('descriptor.settings.title')}</Title>,
|
title: <Title order={4}>{t('descriptor.settings.title')}</Title>,
|
||||||
innerProps: {
|
innerProps: {
|
||||||
integration,
|
integration,
|
||||||
options: module.properties,
|
options: widget.properties,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -59,7 +59,7 @@ export const WidgetsMenu = ({ integration, module }: WidgetsMenuProps) => {
|
|||||||
handleClickEdit={handleEditClick}
|
handleClickEdit={handleEditClick}
|
||||||
handleClickChangePosition={handleChangeSizeClick}
|
handleClickChangePosition={handleChangeSizeClick}
|
||||||
handleClickDelete={handleDeleteClick}
|
handleClickDelete={handleDeleteClick}
|
||||||
displayEdit={module.properties !== undefined}
|
displayEdit={widget.properties !== undefined}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ interface DashboardCategoryProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const DashboardCategory = ({ category }: DashboardCategoryProps) => {
|
export const DashboardCategory = ({ category }: DashboardCategoryProps) => {
|
||||||
const { refs, items, integrations } = useGridstack('category', category.id);
|
const { refs, items, widgets } = useGridstack('category', category.id);
|
||||||
const isEditMode = useEditModeStore((x) => x.enabled);
|
const isEditMode = useEditModeStore((x) => x.enabled);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -45,21 +45,23 @@ export const DashboardCategory = ({ category }: DashboardCategoryProps) => {
|
|||||||
</GridstackTileWrapper>
|
</GridstackTileWrapper>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{Object.entries(integrations).map(([k, v]) => {
|
{widgets.map((widget) => {
|
||||||
const widget = Widgets[k as keyof typeof Widgets] as IWidgetDefinition | undefined;
|
const definition = Widgets[widget.id as keyof typeof Widgets] as
|
||||||
if (!widget) return null;
|
| IWidgetDefinition
|
||||||
|
| undefined;
|
||||||
|
if (!definition) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GridstackTileWrapper
|
<GridstackTileWrapper
|
||||||
type="module"
|
type="widget"
|
||||||
key={k}
|
key={widget.id}
|
||||||
itemRef={refs.items.current[k]}
|
itemRef={refs.items.current[widget.id]}
|
||||||
id={widget.id}
|
id={definition.id}
|
||||||
{...widget.gridstack}
|
{...definition.gridstack}
|
||||||
{...v.shape.location}
|
{...widget.shape.location}
|
||||||
{...v.shape.size}
|
{...widget.shape.size}
|
||||||
>
|
>
|
||||||
<widget.component className="grid-stack-item-content" module={v} />
|
<definition.component className="grid-stack-item-content" widget={widget} />
|
||||||
</GridstackTileWrapper>
|
</GridstackTileWrapper>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ interface DashboardSidebarProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const DashboardSidebar = ({ location }: DashboardSidebarProps) => {
|
export const DashboardSidebar = ({ location }: DashboardSidebarProps) => {
|
||||||
const { refs, items, integrations } = useGridstack('sidebar', location);
|
const { refs, items, widgets } = useGridstack('sidebar', location);
|
||||||
|
|
||||||
const minRow = useMinRowForFullHeight(refs.wrapper);
|
const minRow = useMinRowForFullHeight(refs.wrapper);
|
||||||
|
|
||||||
@@ -47,21 +47,23 @@ export const DashboardSidebar = ({ location }: DashboardSidebarProps) => {
|
|||||||
</GridstackTileWrapper>
|
</GridstackTileWrapper>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{Object.entries(integrations).map(([k, v]) => {
|
{widgets.map((widget) => {
|
||||||
const widget = Widgets[k as keyof typeof Widgets] as IWidgetDefinition | undefined;
|
const definition = Widgets[widget.id as keyof typeof Widgets] as
|
||||||
if (!widget) return null;
|
| IWidgetDefinition
|
||||||
|
| undefined;
|
||||||
|
if (!definition) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GridstackTileWrapper
|
<GridstackTileWrapper
|
||||||
type="module"
|
type="widget"
|
||||||
key={k}
|
key={widget.id}
|
||||||
itemRef={refs.items.current[k]}
|
itemRef={refs.items.current[widget.id]}
|
||||||
id={widget.id}
|
id={definition.id}
|
||||||
{...widget.gridstack}
|
{...definition.gridstack}
|
||||||
{...v.shape.location}
|
{...widget.shape.location}
|
||||||
{...v.shape.size}
|
{...widget.shape.size}
|
||||||
>
|
>
|
||||||
<widget.component className="grid-stack-item-content" module={v} />
|
<definition.component className="grid-stack-item-content" widget={widget} />
|
||||||
</GridstackTileWrapper>
|
</GridstackTileWrapper>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ interface DashboardWrapperProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const DashboardWrapper = ({ wrapper }: DashboardWrapperProps) => {
|
export const DashboardWrapper = ({ wrapper }: DashboardWrapperProps) => {
|
||||||
const { refs, items, integrations } = useGridstack('wrapper', wrapper.id);
|
const { refs, items, widgets } = useGridstack('wrapper', wrapper.id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -35,21 +35,23 @@ export const DashboardWrapper = ({ wrapper }: DashboardWrapperProps) => {
|
|||||||
</GridstackTileWrapper>
|
</GridstackTileWrapper>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{Object.entries(integrations).map(([k, v]) => {
|
{widgets.map((widget) => {
|
||||||
const widget = Widgets[k as keyof typeof Widgets] as IWidgetDefinition | undefined;
|
const definition = Widgets[widget.id as keyof typeof Widgets] as
|
||||||
if (!widget) return null;
|
| IWidgetDefinition
|
||||||
|
| undefined;
|
||||||
|
if (!definition) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GridstackTileWrapper
|
<GridstackTileWrapper
|
||||||
type="module"
|
type="widget"
|
||||||
key={k}
|
key={widget.id}
|
||||||
itemRef={refs.items.current[k]}
|
itemRef={refs.items.current[widget.id]}
|
||||||
id={widget.id}
|
id={definition.id}
|
||||||
{...widget.gridstack}
|
{...definition.gridstack}
|
||||||
{...v.shape.location}
|
{...widget.shape.location}
|
||||||
{...v.shape.size}
|
{...widget.shape.size}
|
||||||
>
|
>
|
||||||
<widget.component className="grid-stack-item-content" module={v} />
|
<definition.component className="grid-stack-item-content" widget={widget} />
|
||||||
</GridstackTileWrapper>
|
</GridstackTileWrapper>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { GridStack, GridStackNode } from 'fily-publish-gridstack';
|
import { GridStack, GridStackNode } from 'fily-publish-gridstack';
|
||||||
import { MutableRefObject, RefObject } from 'react';
|
import { MutableRefObject, RefObject } from 'react';
|
||||||
import { IntegrationsType } from '../../../../types/integration';
|
|
||||||
import { AppType } from '../../../../types/app';
|
import { AppType } from '../../../../types/app';
|
||||||
|
import { IWidget } from '../../../../widgets/widgets';
|
||||||
|
|
||||||
export const initializeGridstack = (
|
export const initializeGridstack = (
|
||||||
areaType: 'wrapper' | 'category' | 'sidebar',
|
areaType: 'wrapper' | 'category' | 'sidebar',
|
||||||
@@ -10,7 +10,7 @@ export const initializeGridstack = (
|
|||||||
itemRefs: MutableRefObject<Record<string, RefObject<HTMLDivElement>>>,
|
itemRefs: MutableRefObject<Record<string, RefObject<HTMLDivElement>>>,
|
||||||
areaId: string,
|
areaId: string,
|
||||||
items: AppType[],
|
items: AppType[],
|
||||||
integrations: IntegrationsType,
|
widgets: IWidget<string, any>[],
|
||||||
isEditMode: boolean,
|
isEditMode: boolean,
|
||||||
events: {
|
events: {
|
||||||
onChange: (changedNode: GridStackNode) => void;
|
onChange: (changedNode: GridStackNode) => void;
|
||||||
@@ -60,9 +60,10 @@ export const initializeGridstack = (
|
|||||||
({ id }) =>
|
({ id }) =>
|
||||||
itemRefs.current[id] && grid.makeWidget(itemRefs.current[id].current as HTMLDivElement)
|
itemRefs.current[id] && grid.makeWidget(itemRefs.current[id].current as HTMLDivElement)
|
||||||
);
|
);
|
||||||
Object.keys(integrations).forEach(
|
console.log('widgets', widgets, itemRefs.current);
|
||||||
(key) =>
|
widgets.forEach(
|
||||||
itemRefs.current[key] && grid.makeWidget(itemRefs.current[key].current as HTMLDivElement)
|
({ id }) =>
|
||||||
|
itemRefs.current[id] && grid.makeWidget(itemRefs.current[id].current as HTMLDivElement)
|
||||||
);
|
);
|
||||||
grid.batchUpdate(false);
|
grid.batchUpdate(false);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,15 +11,14 @@ import {
|
|||||||
import { useConfigContext } from '../../../../config/provider';
|
import { useConfigContext } from '../../../../config/provider';
|
||||||
import { useConfigStore } from '../../../../config/store';
|
import { useConfigStore } from '../../../../config/store';
|
||||||
import { useResize } from '../../../../hooks/use-resize';
|
import { useResize } from '../../../../hooks/use-resize';
|
||||||
import { IntegrationsType } from '../../../../types/integration';
|
|
||||||
import { AppType } from '../../../../types/app';
|
import { AppType } from '../../../../types/app';
|
||||||
import { TileBaseType } from '../../../../types/tile';
|
import { IWidget } from '../../../../widgets/widgets';
|
||||||
import { useEditModeStore } from '../../Views/useEditModeStore';
|
import { useEditModeStore } from '../../Views/useEditModeStore';
|
||||||
import { initializeGridstack } from './init-gridstack';
|
import { initializeGridstack } from './init-gridstack';
|
||||||
|
|
||||||
interface UseGristackReturnType {
|
interface UseGristackReturnType {
|
||||||
items: AppType[];
|
items: AppType[];
|
||||||
integrations: Partial<IntegrationsType>;
|
widgets: IWidget<string, any>[];
|
||||||
refs: {
|
refs: {
|
||||||
wrapper: RefObject<HTMLDivElement>;
|
wrapper: RefObject<HTMLDivElement>;
|
||||||
items: MutableRefObject<Record<string, RefObject<HTMLDivElement>>>;
|
items: MutableRefObject<Record<string, RefObject<HTMLDivElement>>>;
|
||||||
@@ -54,32 +53,24 @@ export const useGridstack = (
|
|||||||
) ?? [],
|
) ?? [],
|
||||||
[config]
|
[config]
|
||||||
);
|
);
|
||||||
const integrations = useMemo(() => {
|
const widgets = useMemo(() => {
|
||||||
if (!config) return;
|
if (!config) return [];
|
||||||
return (Object.entries(config.integrations) as [keyof IntegrationsType, TileBaseType][])
|
return config.widgets.filter(
|
||||||
.filter(
|
(w) =>
|
||||||
([k, v]) =>
|
w.area.type === areaType &&
|
||||||
v.area.type === areaType &&
|
(w.area.type === 'sidebar'
|
||||||
(v.area.type === 'sidebar'
|
? w.area.properties.location === areaId
|
||||||
? v.area.properties.location === areaId
|
: w.area.properties.id === areaId)
|
||||||
: v.area.properties.id === areaId)
|
);
|
||||||
)
|
|
||||||
.reduce((prev, [k, v]) => {
|
|
||||||
prev[k] = v as unknown as any;
|
|
||||||
return prev;
|
|
||||||
}, {} as IntegrationsType);
|
|
||||||
}, [config]);
|
}, [config]);
|
||||||
|
|
||||||
// define items in itemRefs for easy access and reference to items
|
// define items in itemRefs for easy access and reference to items
|
||||||
if (
|
if (Object.keys(itemRefs.current).length !== items.length + (widgets ?? []).length) {
|
||||||
Object.keys(itemRefs.current).length !==
|
|
||||||
items.length + Object.keys(integrations ?? {}).length
|
|
||||||
) {
|
|
||||||
items.forEach(({ id }: { id: keyof typeof itemRefs.current }) => {
|
items.forEach(({ id }: { id: keyof typeof itemRefs.current }) => {
|
||||||
itemRefs.current[id] = itemRefs.current[id] || createRef();
|
itemRefs.current[id] = itemRefs.current[id] || createRef();
|
||||||
});
|
});
|
||||||
Object.keys(integrations ?? {}).forEach((k) => {
|
(widgets ?? []).forEach(({ id }) => {
|
||||||
itemRefs.current[k] = itemRefs.current[k] || createRef();
|
itemRefs.current[id] = itemRefs.current[id] || createRef();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +93,7 @@ export const useGridstack = (
|
|||||||
const currentItem =
|
const currentItem =
|
||||||
itemType === 'app'
|
itemType === 'app'
|
||||||
? previous.apps.find((x) => x.id === itemId)
|
? previous.apps.find((x) => x.id === itemId)
|
||||||
: previous.integrations[itemId as keyof typeof previous.integrations];
|
: previous.widgets.find((x) => x.id === itemId);
|
||||||
if (!currentItem) return previous;
|
if (!currentItem) return previous;
|
||||||
|
|
||||||
currentItem.shape = {
|
currentItem.shape = {
|
||||||
@@ -126,11 +117,12 @@ export const useGridstack = (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const integrationsCopy = { ...previous.integrations };
|
|
||||||
integrationsCopy[itemId as keyof typeof integrationsCopy] = currentItem as any;
|
|
||||||
return {
|
return {
|
||||||
...previous,
|
...previous,
|
||||||
integrations: integrationsCopy,
|
widgets: [
|
||||||
|
...previous.widgets.filter((x) => x.id !== itemId),
|
||||||
|
{ ...(currentItem as IWidget<string, any>) },
|
||||||
|
],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -149,8 +141,7 @@ export const useGridstack = (
|
|||||||
const currentItem =
|
const currentItem =
|
||||||
itemType === 'app'
|
itemType === 'app'
|
||||||
? previous.apps.find((x) => x.id === itemId)
|
? previous.apps.find((x) => x.id === itemId)
|
||||||
: previous.integrations[itemId as keyof typeof previous.integrations];
|
: previous.widgets.find((x) => x.id === itemId);
|
||||||
|
|
||||||
if (!currentItem) return previous;
|
if (!currentItem) return previous;
|
||||||
|
|
||||||
if (areaType === 'sidebar') {
|
if (areaType === 'sidebar') {
|
||||||
@@ -190,11 +181,12 @@ export const useGridstack = (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const integrationsCopy = { ...previous.integrations };
|
|
||||||
integrationsCopy[itemId as keyof typeof integrationsCopy] = currentItem as any;
|
|
||||||
return {
|
return {
|
||||||
...previous,
|
...previous,
|
||||||
integrations: integrationsCopy,
|
widgets: [
|
||||||
|
...previous.widgets.filter((x) => x.id !== itemId),
|
||||||
|
{ ...(currentItem as IWidget<string, any>) },
|
||||||
|
],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -209,18 +201,18 @@ export const useGridstack = (
|
|||||||
itemRefs,
|
itemRefs,
|
||||||
areaId,
|
areaId,
|
||||||
items,
|
items,
|
||||||
integrations ?? {},
|
widgets ?? [],
|
||||||
isEditMode,
|
isEditMode,
|
||||||
{
|
{
|
||||||
onChange,
|
onChange,
|
||||||
onAdd,
|
onAdd,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}, [items.length, wrapperRef.current, Object.keys(integrations ?? {}).length]);
|
}, [items.length, wrapperRef.current, (widgets ?? []).length]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items,
|
items,
|
||||||
integrations: integrations ?? {},
|
widgets: widgets ?? [],
|
||||||
refs: {
|
refs: {
|
||||||
items: itemRefs,
|
items: itemRefs,
|
||||||
wrapper: wrapperRef,
|
wrapper: wrapperRef,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ async function Get(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
|
|
||||||
const config = getConfig(configName);
|
const config = getConfig(configName);
|
||||||
|
|
||||||
const dashDotUrl = config.integrations.dashdot?.properties.url;
|
const dashDotUrl = config.widgets.dashdot?.properties.url;
|
||||||
|
|
||||||
if (!dashDotUrl) {
|
if (!dashDotUrl) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ async function Get(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
|
|
||||||
const config = getConfig(configName);
|
const config = getConfig(configName);
|
||||||
|
|
||||||
const dashDotUrl = config.integrations.dashdot?.properties.url;
|
const dashDotUrl = config.widgets.dashdot?.properties.url;
|
||||||
|
|
||||||
if (!dashDotUrl) {
|
if (!dashDotUrl) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export const getFallbackConfig = (name?: string): BackendConfigType => ({
|
|||||||
name: name ?? 'default',
|
name: name ?? 'default',
|
||||||
},
|
},
|
||||||
categories: [],
|
categories: [],
|
||||||
integrations: {},
|
widgets: {},
|
||||||
apps: [],
|
apps: [],
|
||||||
settings: {
|
settings: {
|
||||||
common: {
|
common: {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { CategoryType } from './category';
|
import { CategoryType } from './category';
|
||||||
import { WrapperType } from './wrapper';
|
import { WrapperType } from './wrapper';
|
||||||
import { ConfigAppType, AppType } from './app';
|
import { ConfigAppType, AppType } from './app';
|
||||||
import { IntegrationsType } from './integration';
|
|
||||||
import { SettingsType } from './settings';
|
import { SettingsType } from './settings';
|
||||||
|
import { IWidget } from '../widgets/widgets';
|
||||||
|
|
||||||
export interface ConfigType {
|
export interface ConfigType {
|
||||||
schemaVersion: string;
|
schemaVersion: string;
|
||||||
@@ -10,7 +10,7 @@ export interface ConfigType {
|
|||||||
categories: CategoryType[];
|
categories: CategoryType[];
|
||||||
wrappers: WrapperType[];
|
wrappers: WrapperType[];
|
||||||
apps: AppType[];
|
apps: AppType[];
|
||||||
integrations: IntegrationsType;
|
widgets: IWidget<string, any>[];
|
||||||
settings: SettingsType;
|
settings: SettingsType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ const definition = defineWidget({
|
|||||||
export type IBitTorrent = IWidget<typeof definition['id'], typeof definition>;
|
export type IBitTorrent = IWidget<typeof definition['id'], typeof definition>;
|
||||||
|
|
||||||
interface BitTorrentTileProps extends BaseTileProps {
|
interface BitTorrentTileProps extends BaseTileProps {
|
||||||
module: IBitTorrent; // TODO: change to new type defined through widgetDefinition
|
widget: IBitTorrent; // TODO: change to new type defined through widgetDefinition
|
||||||
}
|
}
|
||||||
|
|
||||||
function BitTorrentTile({ className, module }: BitTorrentTileProps) {
|
function BitTorrentTile({ className, widget }: BitTorrentTileProps) {
|
||||||
return <HomarrCardWrapper>Bit Torrent</HomarrCardWrapper>;
|
return <HomarrCardWrapper>Bit Torrent</HomarrCardWrapper>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,10 +34,10 @@ const definition = defineWidget({
|
|||||||
export type ICalendarWidget = IWidget<typeof definition['id'], typeof definition>;
|
export type ICalendarWidget = IWidget<typeof definition['id'], typeof definition>;
|
||||||
|
|
||||||
interface CalendarTileProps extends BaseTileProps {
|
interface CalendarTileProps extends BaseTileProps {
|
||||||
module: ICalendarWidget;
|
widget: ICalendarWidget;
|
||||||
}
|
}
|
||||||
|
|
||||||
function CalendarTile({ className, module }: CalendarTileProps) {
|
function CalendarTile({ className, widget }: CalendarTileProps) {
|
||||||
const { secondaryColor } = useColorTheme();
|
const { secondaryColor } = useColorTheme();
|
||||||
const { name: configName } = useConfigContext();
|
const { name: configName } = useConfigContext();
|
||||||
const { classes, cx } = useStyles(secondaryColor);
|
const { classes, cx } = useStyles(secondaryColor);
|
||||||
@@ -64,7 +64,7 @@ function CalendarTile({ className, module }: CalendarTileProps) {
|
|||||||
size="xs"
|
size="xs"
|
||||||
fullWidth
|
fullWidth
|
||||||
onChange={() => {}}
|
onChange={() => {}}
|
||||||
firstDayOfWeek={module.properties.sundayStart ? 'sunday' : 'monday'}
|
firstDayOfWeek={widget.properties.sundayStart ? 'sunday' : 'monday'}
|
||||||
dayStyle={(date) => ({
|
dayStyle={(date) => ({
|
||||||
margin: 1,
|
margin: 1,
|
||||||
backgroundColor: isToday(date)
|
backgroundColor: isToday(date)
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ const useDashDotStorage = () => {
|
|||||||
'dashdot/storage',
|
'dashdot/storage',
|
||||||
{
|
{
|
||||||
configName,
|
configName,
|
||||||
url: config?.integrations.dashDot?.properties.url,
|
url: config?.widgets.dashDot?.properties.url,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
queryFn: () => fetchDashDotStorageLoad(configName),
|
queryFn: () => fetchDashDotStorageLoad(configName),
|
||||||
|
|||||||
@@ -50,24 +50,24 @@ const definition = defineWidget({
|
|||||||
export type IDashDotTile = IWidget<typeof definition['id'], typeof definition>;
|
export type IDashDotTile = IWidget<typeof definition['id'], typeof definition>;
|
||||||
|
|
||||||
interface DashDotTileProps extends BaseTileProps {
|
interface DashDotTileProps extends BaseTileProps {
|
||||||
module: IDashDotTile; // TODO: change to new type defined through widgetDefinition
|
widget: IDashDotTile; // TODO: change to new type defined through widgetDefinition
|
||||||
}
|
}
|
||||||
|
|
||||||
function DashDotTile({ module, className }: DashDotTileProps) {
|
function DashDotTile({ widget, className }: DashDotTileProps) {
|
||||||
const { classes } = useDashDotTileStyles();
|
const { classes } = useDashDotTileStyles();
|
||||||
const { t } = useTranslation('modules/dashdot');
|
const { t } = useTranslation('modules/dashdot');
|
||||||
|
|
||||||
const dashDotUrl = module?.properties.url;
|
const dashDotUrl = widget?.properties.url;
|
||||||
|
|
||||||
const { data: info } = useDashDotInfo();
|
const { data: info } = useDashDotInfo({ dashDotUrl });
|
||||||
|
|
||||||
const graphs = module?.properties.graphs.map((g) => ({
|
const graphs = widget?.properties.graphs.map((g) => ({
|
||||||
id: g,
|
id: g,
|
||||||
name: t(`card.graphs.${g}.title`),
|
name: t(`card.graphs.${g}.title`),
|
||||||
twoSpan: ['network', 'gpu'].includes(g),
|
twoSpan: ['network', 'gpu'].includes(g),
|
||||||
isMultiView:
|
isMultiView:
|
||||||
(g === 'cpu' && module.properties.cpuMultiView) ||
|
(g === 'cpu' && widget.properties.cpuMultiView) ||
|
||||||
(g === 'storage' && module.properties.storageMultiView),
|
(g === 'storage' && widget.properties.storageMultiView),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const heading = (
|
const heading = (
|
||||||
@@ -78,7 +78,7 @@ function DashDotTile({ module, className }: DashDotTileProps) {
|
|||||||
|
|
||||||
const menu = (
|
const menu = (
|
||||||
// TODO: add widgetWrapper that is generic and uses the definition
|
// TODO: add widgetWrapper that is generic and uses the definition
|
||||||
<WidgetsMenu module={module} integration={definition.id} />
|
<WidgetsMenu widget={widget} integration={definition.id} />
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!dashDotUrl) {
|
if (!dashDotUrl) {
|
||||||
@@ -93,7 +93,7 @@ function DashDotTile({ module, className }: DashDotTileProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isCompact = module?.properties.useCompactView ?? false;
|
const isCompact = widget?.properties.useCompactView ?? false;
|
||||||
|
|
||||||
const isCompactStorageVisible = graphs?.some((g) => g.id === 'storage' && isCompact);
|
const isCompactStorageVisible = graphs?.some((g) => g.id === 'storage' && isCompact);
|
||||||
|
|
||||||
@@ -130,14 +130,14 @@ function DashDotTile({ module, className }: DashDotTileProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const useDashDotInfo = () => {
|
const useDashDotInfo = ({ dashDotUrl }: { dashDotUrl: string }) => {
|
||||||
const { name: configName, config } = useConfigContext();
|
const { name: configName } = useConfigContext();
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: [
|
queryKey: [
|
||||||
'dashdot/info',
|
'dashdot/info',
|
||||||
{
|
{
|
||||||
configName,
|
configName,
|
||||||
url: config?.integrations.dashDot?.properties.url,
|
dashDotUrl,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
queryFn: () => fetchDashDotInfo(configName),
|
queryFn: () => fetchDashDotInfo(configName),
|
||||||
|
|||||||
@@ -30,17 +30,17 @@ const definition = defineWidget({
|
|||||||
export type IDateWidget = IWidget<typeof definition['id'], typeof definition>;
|
export type IDateWidget = IWidget<typeof definition['id'], typeof definition>;
|
||||||
|
|
||||||
interface DateTileProps extends BaseTileProps {
|
interface DateTileProps extends BaseTileProps {
|
||||||
module: IDateWidget; // TODO: change to new type defined through widgetDefinition
|
widget: IDateWidget; // TODO: change to new type defined through widgetDefinition
|
||||||
}
|
}
|
||||||
|
|
||||||
function DateTile({ className, module }: DateTileProps) {
|
function DateTile({ className, widget }: DateTileProps) {
|
||||||
const date = useDateState();
|
const date = useDateState();
|
||||||
const formatString = module.properties.display24HourFormat ? 'HH:mm' : 'h:mm A';
|
const formatString = widget.properties.display24HourFormat ? 'HH:mm' : 'h:mm A';
|
||||||
|
|
||||||
// TODO: add widgetWrapper that is generic and uses the definition
|
// TODO: add widgetWrapper that is generic and uses the definition
|
||||||
return (
|
return (
|
||||||
<HomarrCardWrapper className={className}>
|
<HomarrCardWrapper className={className}>
|
||||||
<WidgetsMenu integration={definition.id} module={module} />
|
<WidgetsMenu integration={definition.id} widget={widget} />
|
||||||
<Center style={{ height: '100%' }}>
|
<Center style={{ height: '100%' }}>
|
||||||
<Stack spacing="xs">
|
<Stack spacing="xs">
|
||||||
<Title>{dayjs(date).format(formatString)}</Title>
|
<Title>{dayjs(date).format(formatString)}</Title>
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ const definition = defineWidget({
|
|||||||
export type ITorrentNetworkTraffic = IWidget<typeof definition['id'], typeof definition>;
|
export type ITorrentNetworkTraffic = IWidget<typeof definition['id'], typeof definition>;
|
||||||
|
|
||||||
interface TorrentNetworkTrafficTileProps extends BaseTileProps {
|
interface TorrentNetworkTrafficTileProps extends BaseTileProps {
|
||||||
module: ITorrentNetworkTraffic; // TODO: change to new type defined through widgetDefinition
|
widget: ITorrentNetworkTraffic; // TODO: change to new type defined through widgetDefinition
|
||||||
}
|
}
|
||||||
|
|
||||||
function TorrentNetworkTrafficTile({ className, module }: TorrentNetworkTrafficTileProps) {
|
function TorrentNetworkTrafficTile({ className, widget }: TorrentNetworkTrafficTileProps) {
|
||||||
return <HomarrCardWrapper>TorrentNetworkTraffic</HomarrCardWrapper>;
|
return <HomarrCardWrapper>TorrentNetworkTraffic</HomarrCardWrapper>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,11 +33,11 @@ const definition = defineWidget({
|
|||||||
export type IWeatherWidget = IWidget<typeof definition['id'], typeof definition>;
|
export type IWeatherWidget = IWidget<typeof definition['id'], typeof definition>;
|
||||||
|
|
||||||
interface WeatherTileProps extends BaseTileProps {
|
interface WeatherTileProps extends BaseTileProps {
|
||||||
module: IWeatherWidget;
|
widget: IWeatherWidget;
|
||||||
}
|
}
|
||||||
|
|
||||||
function WeatherTile({ className, module }: WeatherTileProps) {
|
function WeatherTile({ className, widget }: WeatherTileProps) {
|
||||||
const { data: weather, isLoading, isError } = useWeatherForCity(module.properties.location);
|
const { data: weather, isLoading, isError } = useWeatherForCity(widget.properties.location);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
@@ -67,7 +67,7 @@ function WeatherTile({ className, module }: WeatherTileProps) {
|
|||||||
// TODO: add widgetWrapper that is generic and uses the definition
|
// TODO: add widgetWrapper that is generic and uses the definition
|
||||||
return (
|
return (
|
||||||
<HomarrCardWrapper className={className}>
|
<HomarrCardWrapper className={className}>
|
||||||
<WidgetsMenu integration={definition.id} module={module} />
|
<WidgetsMenu integration={definition.id} widget={widget} />
|
||||||
<Center style={{ height: '100%' }}>
|
<Center style={{ height: '100%' }}>
|
||||||
<Group spacing="md" noWrap align="center">
|
<Group spacing="md" noWrap align="center">
|
||||||
<WeatherIcon code={weather!.current_weather.weathercode} />
|
<WeatherIcon code={weather!.current_weather.weathercode} />
|
||||||
@@ -75,7 +75,7 @@ function WeatherTile({ className, module }: WeatherTileProps) {
|
|||||||
<Title order={2}>
|
<Title order={2}>
|
||||||
{getPerferedUnit(
|
{getPerferedUnit(
|
||||||
weather!.current_weather.temperature,
|
weather!.current_weather.temperature,
|
||||||
module.properties.displayInFahrenheit
|
widget.properties.displayInFahrenheit
|
||||||
)}
|
)}
|
||||||
</Title>
|
</Title>
|
||||||
<Group spacing="xs" noWrap>
|
<Group spacing="xs" noWrap>
|
||||||
@@ -83,7 +83,7 @@ function WeatherTile({ className, module }: WeatherTileProps) {
|
|||||||
<span>
|
<span>
|
||||||
{getPerferedUnit(
|
{getPerferedUnit(
|
||||||
weather!.daily.temperature_2m_max[0],
|
weather!.daily.temperature_2m_max[0],
|
||||||
module.properties.displayInFahrenheit
|
widget.properties.displayInFahrenheit
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<IconArrowUpRight size={16} style={{ right: 15 }} />
|
<IconArrowUpRight size={16} style={{ right: 15 }} />
|
||||||
@@ -92,7 +92,7 @@ function WeatherTile({ className, module }: WeatherTileProps) {
|
|||||||
<span>
|
<span>
|
||||||
{getPerferedUnit(
|
{getPerferedUnit(
|
||||||
weather!.daily.temperature_2m_min[0],
|
weather!.daily.temperature_2m_min[0],
|
||||||
module.properties.displayInFahrenheit
|
widget.properties.displayInFahrenheit
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<IconArrowDownRight size={16} />
|
<IconArrowDownRight size={16} />
|
||||||
|
|||||||
Reference in New Issue
Block a user