feat(docker): add endpoint filtering and setup assistant
This commit is contained in:
1 parent
315245e048
commit
f9e62a4932
13 files changed
+426
-190
No files matched your search
@@ -4,6 +4,7 @@ import { WidgetAppInput } from "./widget-app-input";
|
||||
import { WidgetCustomWidgetSelectInput } from "./widget-custom-widget-select-input";
|
||||
import { WidgetCustomWidgetConfigurationInput } from "./widget-custom-widget-configuration-input";
|
||||
import { WidgetDynamicSelectInput } from "./widget-dynamic-select-input";
|
||||
import { WidgetDynamicMultiSelectInput } from "./widget-dynamic-multiselect-input";
|
||||
import { WidgetLocationInput } from "./widget-location-input";
|
||||
import { WidgetMultiTextInput } from "./widget-multi-text-input";
|
||||
import { WidgetMultiReleasesRepositoriesInput } from "./widget-multiReleasesRepositories-input";
|
||||
@@ -36,6 +37,7 @@ const mapping = {
|
||||
sortableItemList: WidgetSortedItemListInput,
|
||||
multiReleasesRepositories: WidgetMultiReleasesRepositoriesInput,
|
||||
dynamicSelect: WidgetDynamicSelectInput,
|
||||
dynamicMultiSelect: WidgetDynamicMultiSelectInput,
|
||||
umamiEventName: WidgetUmamiEventInput,
|
||||
umamiEventNames: WidgetUmamiEventNamesInput,
|
||||
umamiWebsite: WidgetUmamiWebsiteInput,
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
"use client";
|
||||
|
||||
import { Loader, MultiSelect } from "@mantine/core";
|
||||
|
||||
import { useScopedI18n } from "@homarr/translation/client";
|
||||
|
||||
import type { CommonWidgetInputProps } from "./common";
|
||||
import { useWidgetInputTranslation } from "./common";
|
||||
import { useFormContext } from "./form";
|
||||
|
||||
export const WidgetDynamicMultiSelectInput = ({
|
||||
property,
|
||||
kind,
|
||||
options,
|
||||
}: CommonWidgetInputProps<"dynamicMultiSelect">) => {
|
||||
const t = useScopedI18n("widget.dynamicSelect");
|
||||
const tInput = useWidgetInputTranslation(kind, property);
|
||||
const form = useFormContext();
|
||||
const inputProps = form.getInputProps(`options.${property}`);
|
||||
const selectedValues = (inputProps.value as string[] | undefined) ?? [];
|
||||
const { data: selectData, isPending, isError } = options.useOptions();
|
||||
const data = [
|
||||
...selectData,
|
||||
...selectedValues
|
||||
.filter((selectedValue) => !selectData.some((option) => option.value === selectedValue))
|
||||
.map((selectedValue) => ({ value: selectedValue, label: selectedValue })),
|
||||
];
|
||||
|
||||
return (
|
||||
<MultiSelect
|
||||
{...inputProps}
|
||||
label={tInput("label")}
|
||||
description={options.withDescription ? tInput("description") : undefined}
|
||||
placeholder={isPending ? t("loading") : tInput("placeholder")}
|
||||
leftSection={isPending ? <Loader size="xs" /> : undefined}
|
||||
searchable
|
||||
clearable
|
||||
nothingFoundMessage={t("noResults")}
|
||||
data={data}
|
||||
disabled={isError}
|
||||
error={isError ? t("loadError") : inputProps.error}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -248,7 +248,7 @@ export default function DockerWidget({
|
||||
const isAdvanced = displayMode === "advanced";
|
||||
|
||||
const utils = clientApi.useUtils();
|
||||
const containersQuery = clientApi.docker.getContainers.useQuery();
|
||||
const containersQuery = clientApi.docker.getContainers.useQuery({ endpointIds: options.endpointIds });
|
||||
const data = getUsableWidgetQueryData(containersQuery);
|
||||
const { isFetching } = containersQuery;
|
||||
const refreshInventory = clientApi.docker.refreshInventory.useMutation({
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { IconBrandDocker, IconServerOff } from "@tabler/icons-react";
|
||||
|
||||
import type { RouterOutputs } from "@homarr/api";
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
|
||||
import { createWidgetDefinition } from "../definition";
|
||||
import { optionsBuilder } from "../options";
|
||||
@@ -30,6 +31,18 @@ export const { definition, componentLoader } = createWidgetDefinition("dockerCon
|
||||
createOptions() {
|
||||
return optionsBuilder.from(
|
||||
(factory) => ({
|
||||
endpointIds: factory.dynamicMultiSelect({
|
||||
defaultValue: [],
|
||||
withDescription: true,
|
||||
useOptions: () => {
|
||||
const { data = [], isPending, isError } = clientApi.docker.getEndpoints.useQuery();
|
||||
return {
|
||||
data: data.map((endpoint) => ({ value: endpoint.id, label: endpoint.name })),
|
||||
isPending,
|
||||
isError,
|
||||
};
|
||||
},
|
||||
}),
|
||||
columns: factory.multiSelect({
|
||||
defaultValue: [...allColumnsList],
|
||||
options: allColumnsList.map((value) => ({
|
||||
|
||||
@@ -69,6 +69,14 @@ interface DynamicSelectInput extends CommonInput<DynamicSelectOption | null> {
|
||||
};
|
||||
}
|
||||
|
||||
interface DynamicMultiSelectInput extends CommonInput<string[]> {
|
||||
useOptions: () => {
|
||||
data: DynamicSelectOption[];
|
||||
isPending: boolean;
|
||||
isError: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface IntegrationSelectInput extends CommonInput<string> {
|
||||
clearable?: boolean;
|
||||
searchable?: boolean;
|
||||
@@ -141,6 +149,12 @@ const optionsFactory = {
|
||||
useOptions: input.useOptions,
|
||||
withDescription: input.withDescription ?? false,
|
||||
}),
|
||||
dynamicMultiSelect: (input: DynamicMultiSelectInput) => ({
|
||||
type: "dynamicMultiSelect" as const,
|
||||
defaultValue: input.defaultValue ?? [],
|
||||
useOptions: input.useOptions,
|
||||
withDescription: input.withDescription ?? false,
|
||||
}),
|
||||
select: <const TOptions extends SelectOption[]>(input: SelectInput<TOptions>) => ({
|
||||
type: "select" as const,
|
||||
defaultValue: (input.defaultValue ?? input.options[0]) as inferSelectOptionValue<TOptions[number]>,
|
||||
|
||||
Reference in new issue
Block a user