feat: add notes for creation of apps and integrations in widget edit modal (#1297)
* feat: add notes for creation of apps and integrations in widget edit modal * fix: unit test failing when with-description flag missing
This commit is contained in:
@@ -576,6 +576,7 @@ export default {
|
|||||||
tryAgain: "Try again",
|
tryAgain: "Try again",
|
||||||
loading: "Loading",
|
loading: "Loading",
|
||||||
},
|
},
|
||||||
|
here: "here",
|
||||||
iconPicker: {
|
iconPicker: {
|
||||||
label: "Icon URL",
|
label: "Icon URL",
|
||||||
header: "Type name or objects to filter for icons... Homarr will search through {countIcons} icons for you.",
|
header: "Type name or objects to filter for icons... Homarr will search through {countIcons} icons for you.",
|
||||||
@@ -1157,6 +1158,14 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
integration: {
|
||||||
|
noData: "No integration found",
|
||||||
|
description: "Click {here} to create a new integration",
|
||||||
|
},
|
||||||
|
app: {
|
||||||
|
noData: "No app found",
|
||||||
|
description: "Click {here} to create a new app",
|
||||||
|
},
|
||||||
error: {
|
error: {
|
||||||
action: {
|
action: {
|
||||||
logs: "Check logs for more details",
|
logs: "Check logs for more details",
|
||||||
|
|||||||
@@ -1,19 +1,22 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { memo, useMemo } from "react";
|
import { memo, useMemo } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import type { SelectProps } from "@mantine/core";
|
import type { SelectProps } from "@mantine/core";
|
||||||
import { Group, Loader, Select } from "@mantine/core";
|
import { Anchor, Group, Loader, Select, Text } from "@mantine/core";
|
||||||
import { IconCheck } from "@tabler/icons-react";
|
import { IconCheck } from "@tabler/icons-react";
|
||||||
|
|
||||||
import type { RouterOutputs } from "@homarr/api";
|
import type { RouterOutputs } from "@homarr/api";
|
||||||
import { clientApi } from "@homarr/api/client";
|
import { clientApi } from "@homarr/api/client";
|
||||||
|
import { useI18n } from "@homarr/translation/client";
|
||||||
|
|
||||||
import type { CommonWidgetInputProps } from "./common";
|
import type { CommonWidgetInputProps } from "./common";
|
||||||
import { useWidgetInputTranslation } from "./common";
|
import { useWidgetInputTranslation } from "./common";
|
||||||
import { useFormContext } from "./form";
|
import { useFormContext } from "./form";
|
||||||
|
|
||||||
export const WidgetAppInput = ({ property, kind, options }: CommonWidgetInputProps<"app">) => {
|
export const WidgetAppInput = ({ property, kind }: CommonWidgetInputProps<"app">) => {
|
||||||
const t = useWidgetInputTranslation(kind, property);
|
const t = useI18n();
|
||||||
|
const tInput = useWidgetInputTranslation(kind, property);
|
||||||
const form = useFormContext();
|
const form = useFormContext();
|
||||||
const { data: apps, isPending } = clientApi.app.selectable.useQuery();
|
const { data: apps, isPending } = clientApi.app.selectable.useQuery();
|
||||||
|
|
||||||
@@ -24,10 +27,11 @@ export const WidgetAppInput = ({ property, kind, options }: CommonWidgetInputPro
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
label={t("label")}
|
label={tInput("label")}
|
||||||
searchable
|
searchable
|
||||||
limit={10}
|
limit={10}
|
||||||
leftSection={<MemoizedLeftSection isPending={isPending} currentApp={currentApp} />}
|
leftSection={<MemoizedLeftSection isPending={isPending} currentApp={currentApp} />}
|
||||||
|
nothingFoundMessage={t("widget.common.app.noData")}
|
||||||
renderOption={renderSelectOption}
|
renderOption={renderSelectOption}
|
||||||
data={
|
data={
|
||||||
apps?.map((app) => ({
|
apps?.map((app) => ({
|
||||||
@@ -36,7 +40,18 @@ export const WidgetAppInput = ({ property, kind, options }: CommonWidgetInputPro
|
|||||||
iconUrl: app.iconUrl,
|
iconUrl: app.iconUrl,
|
||||||
})) ?? []
|
})) ?? []
|
||||||
}
|
}
|
||||||
description={options.withDescription ? t("description") : undefined}
|
inputWrapperOrder={["label", "input", "description", "error"]}
|
||||||
|
description={
|
||||||
|
<Text size="xs">
|
||||||
|
{t("widget.common.app.description", {
|
||||||
|
here: (
|
||||||
|
<Anchor size="xs" component={Link} target="_blank" href="/manage/apps/new">
|
||||||
|
{t("common.here")}
|
||||||
|
</Anchor>
|
||||||
|
),
|
||||||
|
})}
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
{...form.getInputProps(`options.${property}`)}
|
{...form.getInputProps(`options.${property}`)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -104,10 +104,10 @@ const optionsFactory = {
|
|||||||
values: [] as string[],
|
values: [] as string[],
|
||||||
validate: input?.validate,
|
validate: input?.validate,
|
||||||
}),
|
}),
|
||||||
app: (input?: Omit<CommonInput<string>, "defaultValue">) => ({
|
app: () => ({
|
||||||
type: "app" as const,
|
type: "app" as const,
|
||||||
defaultValue: "",
|
defaultValue: "",
|
||||||
withDescription: input?.withDescription ?? false,
|
withDescription: false,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import type { FocusEventHandler } from "react";
|
import type { FocusEventHandler } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
import {
|
import {
|
||||||
|
Anchor,
|
||||||
Avatar,
|
Avatar,
|
||||||
CheckIcon,
|
CheckIcon,
|
||||||
CloseButton,
|
CloseButton,
|
||||||
@@ -86,7 +88,23 @@ export const WidgetIntegrationSelect = ({
|
|||||||
return (
|
return (
|
||||||
<Combobox store={combobox} onOptionSubmit={handleValueSelect} withinPortal={false}>
|
<Combobox store={combobox} onOptionSubmit={handleValueSelect} withinPortal={false}>
|
||||||
<Combobox.DropdownTarget>
|
<Combobox.DropdownTarget>
|
||||||
<PillsInput pointer onClick={() => combobox.toggleDropdown()} {...props}>
|
<PillsInput
|
||||||
|
inputWrapperOrder={["label", "input", "description", "error"]}
|
||||||
|
description={
|
||||||
|
<Text size="xs">
|
||||||
|
{t("widget.common.integration.description", {
|
||||||
|
here: (
|
||||||
|
<Anchor size="xs" component={Link} target="_blank" href="/manage/integrations">
|
||||||
|
{t("common.here")}
|
||||||
|
</Anchor>
|
||||||
|
),
|
||||||
|
})}
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
|
pointer
|
||||||
|
onClick={() => combobox.toggleDropdown()}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
<Pill.Group>
|
<Pill.Group>
|
||||||
{values.length > 0 ? values : <Input.Placeholder>{t("common.multiSelect.placeholder")}</Input.Placeholder>}
|
{values.length > 0 ? values : <Input.Placeholder>{t("common.multiSelect.placeholder")}</Input.Placeholder>}
|
||||||
|
|
||||||
@@ -108,7 +126,15 @@ export const WidgetIntegrationSelect = ({
|
|||||||
</Combobox.DropdownTarget>
|
</Combobox.DropdownTarget>
|
||||||
|
|
||||||
<Combobox.Dropdown>
|
<Combobox.Dropdown>
|
||||||
<Combobox.Options>{options}</Combobox.Options>
|
<Combobox.Options>
|
||||||
|
{options.length >= 1 ? (
|
||||||
|
options
|
||||||
|
) : (
|
||||||
|
<Text p={4} size="sm" ta="center" c="var(--mantine-color-dimmed)">
|
||||||
|
{t("widget.common.integration.noData")}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Combobox.Options>
|
||||||
</Combobox.Dropdown>
|
</Combobox.Dropdown>
|
||||||
</Combobox>
|
</Combobox>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user