feat(integrations): add ICal (#3980)

Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
This commit is contained in:
Nicolas Newman
2025-09-17 11:57:50 -05:00
committed by GitHub
parent 0a1a75dc5f
commit fedbff3fd1
22 changed files with 395 additions and 196 deletions

View File

@@ -2,6 +2,7 @@ import {
IconCode,
IconGrid3x3,
IconKey,
IconLink,
IconMessage,
IconPassword,
IconPasswordUser,
@@ -21,6 +22,7 @@ export const integrationSecretIcons = {
tokenId: IconGrid3x3,
personalAccessToken: IconPasswordUser,
topic: IconMessage,
url: IconLink,
opnsenseApiKey: IconKey,
opnsenseApiSecret: IconPassword,
githubAppId: IconCode,

View File

@@ -33,6 +33,8 @@ export const EditIntegrationForm = ({ integration }: EditIntegrationForm) => {
integration.secrets.every((secret) => secretKinds.includes(secret.kind)),
) ?? getDefaultSecretKinds(integration.kind);
const hasUrlSecret = secretsKinds.includes("url");
const router = useRouter();
const form = useZodForm(integrationUpdateSchema.omit({ id: true }), {
initialValues: {
@@ -50,10 +52,14 @@ export const EditIntegrationForm = ({ integration }: EditIntegrationForm) => {
const secretsMap = new Map(integration.secrets.map((secret) => [secret.kind, secret]));
const handleSubmitAsync = async (values: FormType) => {
const url = hasUrlSecret
? new URL(values.secrets.find((secret) => secret.kind === "url")?.value ?? values.url).origin
: values.url;
await mutateAsync(
{
id: integration.id,
...values,
url,
secrets: values.secrets.map((secret) => ({
kind: secret.kind,
value: secret.value === "" ? null : secret.value,
@@ -92,7 +98,9 @@ export const EditIntegrationForm = ({ integration }: EditIntegrationForm) => {
<Stack>
<TextInput withAsterisk label={t("integration.field.name.label")} {...form.getInputProps("name")} />
<TextInput withAsterisk label={t("integration.field.url.label")} {...form.getInputProps("url")} />
{hasUrlSecret ? null : (
<TextInput withAsterisk label={t("integration.field.url.label")} {...form.getInputProps("url")} />
)}
<Fieldset legend={t("integration.secrets.title")}>
<Stack gap="sm">

View File

@@ -55,12 +55,19 @@ const formSchema = integrationCreateSchema.omit({ kind: true }).and(
export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) => {
const t = useI18n();
const secretKinds = getAllSecretKindOptions(searchParams.kind);
const hasUrlSecret = secretKinds.some((kinds) => kinds.includes("url"));
const router = useRouter();
const [opened, setOpened] = useState(false);
let url = searchParams.url ?? getIntegrationDefaultUrl(searchParams.kind) ?? "";
if (hasUrlSecret) {
// Placeholder Url, replaced with origin of the secret Url on submit
url = "http://localhost";
}
const form = useZodForm(formSchema, {
initialValues: {
name: searchParams.name ?? getIntegrationName(searchParams.kind),
url: searchParams.url ?? getIntegrationDefaultUrl(searchParams.kind) ?? "",
url,
secrets: secretKinds[0].map((kind) => ({
kind,
value: "",
@@ -83,10 +90,14 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) =>
const [error, setError] = useState<null | AnyMappedTestConnectionError>(null);
const handleSubmitAsync = async (values: FormType) => {
const url = hasUrlSecret
? new URL(values.secrets.find((secret) => secret.kind === "url")?.value ?? values.url).origin
: values.url;
await createIntegrationAsync(
{
kind: searchParams.kind,
...values,
url,
},
{
async onSuccess(data) {
@@ -114,10 +125,10 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) =>
await createAppAsync(
{
name: values.name,
href: hasCustomHref ? values.appHref : values.url,
href: hasCustomHref ? values.appHref : url,
iconUrl: getIconUrl(searchParams.kind),
description: null,
pingUrl: values.url,
pingUrl: url,
},
{
async onSettled() {
@@ -149,7 +160,9 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) =>
<Stack>
<TextInput withAsterisk label={t("integration.field.name.label")} autoFocus {...form.getInputProps("name")} />
<TextInput withAsterisk label={t("integration.field.url.label")} {...form.getInputProps("url")} />
{hasUrlSecret ? null : (
<TextInput withAsterisk label={t("integration.field.url.label")} {...form.getInputProps("url")} />
)}
<Fieldset legend={t("integration.secrets.title")}>
<Stack gap="sm">