feat: add board access settings (#249)
* wip: add board access settings * wip: add user access control * wip: add user access control * feat: add user access control * refactor: move away from mantine-modal-manager * fix: ci issues and failing tests * fix: lint issue * fix: format issue * fix: deepsource issues * chore: address pull request feedback
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
|
||||
import type { IntegrationKind, WidgetKind } from "@homarr/definitions";
|
||||
import { useModalAction } from "@homarr/modals";
|
||||
import { showSuccessNotification } from "@homarr/notifications";
|
||||
import { useScopedI18n } from "@homarr/translation/client";
|
||||
import {
|
||||
@@ -17,10 +18,11 @@ import {
|
||||
import {
|
||||
loadWidgetDynamic,
|
||||
reduceWidgetOptionsWithDefaultValues,
|
||||
WidgetEditModal,
|
||||
widgetImports,
|
||||
} from "@homarr/widgets";
|
||||
|
||||
import { modalEvents } from "../../modals";
|
||||
import { PreviewDimensionsModal } from "./_dimension-modal";
|
||||
import type { Dimensions } from "./_dimension-modal";
|
||||
|
||||
interface WidgetPreviewPageContentProps {
|
||||
@@ -38,6 +40,10 @@ export const WidgetPreviewPageContent = ({
|
||||
integrationData,
|
||||
}: WidgetPreviewPageContentProps) => {
|
||||
const t = useScopedI18n("widgetPreview");
|
||||
const { openModal: openWidgetEditModal } = useModalAction(WidgetEditModal);
|
||||
const { openModal: openPreviewDimensionsModal } = useModalAction(
|
||||
PreviewDimensionsModal,
|
||||
);
|
||||
const currentDefinition = useMemo(
|
||||
() => widgetImports[kind].definition,
|
||||
[kind],
|
||||
@@ -55,28 +61,25 @@ export const WidgetPreviewPageContent = ({
|
||||
integrations: [],
|
||||
});
|
||||
|
||||
const Comp = loadWidgetDynamic(kind);
|
||||
|
||||
const openWitgetEditModal = useCallback(() => {
|
||||
return modalEvents.openManagedModal({
|
||||
modal: "widgetEditModal",
|
||||
innerProps: {
|
||||
kind,
|
||||
value: state,
|
||||
onSuccessfulEdit: (value) => {
|
||||
setState(value);
|
||||
},
|
||||
integrationData: integrationData.filter(
|
||||
(integration) =>
|
||||
"supportedIntegrations" in currentDefinition &&
|
||||
(currentDefinition.supportedIntegrations as string[]).some(
|
||||
(kind) => kind === integration.kind,
|
||||
),
|
||||
),
|
||||
integrationSupport: "supportedIntegrations" in currentDefinition,
|
||||
const handleOpenEditWidgetModal = useCallback(() => {
|
||||
openWidgetEditModal({
|
||||
kind,
|
||||
value: state,
|
||||
onSuccessfulEdit: (value) => {
|
||||
setState(value);
|
||||
},
|
||||
integrationData: integrationData.filter(
|
||||
(integration) =>
|
||||
"supportedIntegrations" in currentDefinition &&
|
||||
(currentDefinition.supportedIntegrations as string[]).some(
|
||||
(kind) => kind === integration.kind,
|
||||
),
|
||||
),
|
||||
integrationSupport: "supportedIntegrations" in currentDefinition,
|
||||
});
|
||||
}, [kind, state, integrationData, currentDefinition]);
|
||||
}, [currentDefinition, integrationData, kind, openWidgetEditModal, state]);
|
||||
|
||||
const Comp = loadWidgetDynamic(kind);
|
||||
|
||||
const toggleEditMode = useCallback(() => {
|
||||
setEditMode((editMode) => !editMode);
|
||||
@@ -86,15 +89,11 @@ export const WidgetPreviewPageContent = ({
|
||||
}, [editMode, t]);
|
||||
|
||||
const openDimensionsModal = useCallback(() => {
|
||||
modalEvents.openManagedModal({
|
||||
modal: "dimensionsModal",
|
||||
title: t("dimensions.title"),
|
||||
innerProps: {
|
||||
dimensions,
|
||||
setDimensions,
|
||||
},
|
||||
openPreviewDimensionsModal({
|
||||
dimensions,
|
||||
setDimensions,
|
||||
});
|
||||
}, [dimensions, t]);
|
||||
}, [dimensions, openPreviewDimensionsModal]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -107,7 +106,8 @@ export const WidgetPreviewPageContent = ({
|
||||
<Comp
|
||||
options={state.options as never}
|
||||
integrations={state.integrations.map(
|
||||
(id) => integrationData.find((x) => x.id === id)!,
|
||||
(id) =>
|
||||
integrationData.find((integration) => integration.id === id)!,
|
||||
)}
|
||||
width={dimensions.width}
|
||||
height={dimensions.height}
|
||||
@@ -119,7 +119,7 @@ export const WidgetPreviewPageContent = ({
|
||||
size={48}
|
||||
variant="default"
|
||||
radius="xl"
|
||||
onClick={openWitgetEditModal}
|
||||
onClick={handleOpenEditWidgetModal}
|
||||
>
|
||||
<IconPencil size={24} />
|
||||
</ActionIcon>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import type { ManagedModal } from "mantine-modal-manager";
|
||||
|
||||
import { useForm } from "@homarr/form";
|
||||
import { createModal } from "@homarr/modals";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { Button, Group, InputWrapper, Slider, Stack } from "@homarr/ui";
|
||||
|
||||
@@ -11,49 +10,50 @@ interface InnerProps {
|
||||
setDimensions: (dimensions: Dimensions) => void;
|
||||
}
|
||||
|
||||
export const PreviewDimensionsModal: ManagedModal<InnerProps> = ({
|
||||
actions,
|
||||
innerProps,
|
||||
}) => {
|
||||
const t = useI18n();
|
||||
const form = useForm({
|
||||
initialValues: innerProps.dimensions,
|
||||
});
|
||||
export const PreviewDimensionsModal = createModal<InnerProps>(
|
||||
({ actions, innerProps }) => {
|
||||
const t = useI18n();
|
||||
const form = useForm({
|
||||
initialValues: innerProps.dimensions,
|
||||
});
|
||||
|
||||
const handleSubmit = (values: Dimensions) => {
|
||||
innerProps.setDimensions(values);
|
||||
actions.closeModal();
|
||||
};
|
||||
const handleSubmit = (values: Dimensions) => {
|
||||
innerProps.setDimensions(values);
|
||||
actions.closeModal();
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
<Stack>
|
||||
<InputWrapper label={t("item.move.field.width.label")}>
|
||||
<Slider
|
||||
min={64}
|
||||
max={1024}
|
||||
step={64}
|
||||
{...form.getInputProps("width")}
|
||||
/>
|
||||
</InputWrapper>
|
||||
<InputWrapper label={t("item.move.field.height.label")}>
|
||||
<Slider
|
||||
min={64}
|
||||
max={1024}
|
||||
step={64}
|
||||
{...form.getInputProps("height")}
|
||||
/>
|
||||
</InputWrapper>
|
||||
<Group justify="end">
|
||||
<Button variant="subtle" color="gray" onClick={actions.closeModal}>
|
||||
{t("common.action.cancel")}
|
||||
</Button>
|
||||
<Button type="submit">{t("common.action.confirm")}</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
<Stack>
|
||||
<InputWrapper label={t("item.move.field.width.label")}>
|
||||
<Slider
|
||||
min={64}
|
||||
max={1024}
|
||||
step={64}
|
||||
{...form.getInputProps("width")}
|
||||
/>
|
||||
</InputWrapper>
|
||||
<InputWrapper label={t("item.move.field.height.label")}>
|
||||
<Slider
|
||||
min={64}
|
||||
max={1024}
|
||||
step={64}
|
||||
{...form.getInputProps("height")}
|
||||
/>
|
||||
</InputWrapper>
|
||||
<Group justify="end">
|
||||
<Button variant="subtle" color="gray" onClick={actions.closeModal}>
|
||||
{t("common.action.cancel")}
|
||||
</Button>
|
||||
<Button type="submit">{t("common.action.confirm")}</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</form>
|
||||
);
|
||||
},
|
||||
).withOptions({
|
||||
defaultTitle: (t) => t("widgetPreview.dimensions.title"),
|
||||
});
|
||||
|
||||
export interface Dimensions {
|
||||
width: number;
|
||||
|
||||
Reference in New Issue
Block a user