"use client"; import React, { useCallback, useMemo, useState } from "react"; import { ActionIcon, Button, Divider, Fieldset, Group, Select, Stack, Text, TextInput } from "@mantine/core"; import type { FormErrors } from "@mantine/form"; import { IconEdit, IconTrash, IconTriangleFilled } from "@tabler/icons-react"; import { escapeForRegEx } from "@tiptap/react"; import { IconPicker } from "@homarr/forms-collection"; import { createModal, useModalAction } from "@homarr/modals"; import { useScopedI18n } from "@homarr/translation/client"; import { MaskedOrNormalImage } from "@homarr/ui"; import { Providers } from "../releases/releases-providers"; import type { ReleasesRepository, ReleasesVersionFilter } from "../releases/releases-repository"; import type { CommonWidgetInputProps } from "./common"; import { useWidgetInputTranslation } from "./common"; import { useFormContext } from "./form"; interface FormValidation { hasErrors: boolean; errors: FormErrors; } export const WidgetMultiReleasesRepositoriesInput = ({ property, kind, }: CommonWidgetInputProps<"multiReleasesRepositories">) => { const t = useWidgetInputTranslation(kind, property); const tRepository = useScopedI18n("widget.releases.option.repositories"); const form = useFormContext(); const repositories = form.values.options[property] as ReleasesRepository[]; const { openModal } = useModalAction(ReleaseEditModal); const versionFilterPrecisionOptions = useMemo( () => [tRepository("versionFilter.precision.options.none"), "#", "#.#", "#.#.#", "#.#.#.#", "#.#.#.#.#"], [tRepository], ); const onRepositorySave = useCallback( (repository: ReleasesRepository, index: number): FormValidation => { form.setFieldValue(`options.${property}.${index}.providerKey`, repository.providerKey); form.setFieldValue(`options.${property}.${index}.identifier`, repository.identifier); form.setFieldValue(`options.${property}.${index}.versionFilter`, repository.versionFilter); form.setFieldValue(`options.${property}.${index}.iconUrl`, repository.iconUrl); const formValidation = form.validate(); const fieldErrors: FormErrors = Object.entries(formValidation.errors).reduce((acc, [key, value]) => { if (key.startsWith(`options.${property}.${index}.`)) { acc[key] = value; } return acc; }, {} as FormErrors); return { hasErrors: Object.keys(fieldErrors).length > 0, errors: fieldErrors, }; }, [form, property], ); const addNewItem = () => { const item = { providerKey: "DockerHub", identifier: "", }; form.setValues((previous) => { const previousValues = previous.options?.[property] as ReleasesRepository[]; return { ...previous, options: { ...previous.options, [property]: [...previousValues, item], }, }; }); const index = repositories.length; openModal({ fieldPath: `options.${property}.${index}`, repository: item, onRepositorySave: (saved) => onRepositorySave(saved, index), versionFilterPrecisionOptions, }); }; const onReleaseRemove = (index: number) => { form.setValues((previous) => { const previousValues = previous.options?.[property] as ReleasesRepository[]; return { ...previous, options: { ...previous.options, [property]: previousValues.filter((_, i) => i !== index), }, }; }); }; return (
); }; const formatVersionFilterRegex = (versionFilter: ReleasesVersionFilter | undefined) => { if (!versionFilter) return undefined; const escapedPrefix = versionFilter.prefix ? escapeForRegEx(versionFilter.prefix) : ""; const precision = "[0-9]+\\.".repeat(versionFilter.precision).slice(0, -2); const escapedSuffix = versionFilter.suffix ? escapeForRegEx(versionFilter.suffix) : ""; return `^${escapedPrefix}${precision}${escapedSuffix}$`; }; interface ReleaseEditProps { fieldPath: string; repository: ReleasesRepository; onRepositorySave: (repository: ReleasesRepository) => FormValidation; versionFilterPrecisionOptions: string[]; } const ReleaseEditModal = createModal