// --------------------------------------------------------------------------- // Integration add-flow helpers — the plugin-agnostic pieces every Add-source // form repeats: decoding a mutation Exit into a user-facing message, the // IntegrationAlreadyExistsError guard, the live slug-collision check against // the catalog, and the inline error/collision alerts. // --------------------------------------------------------------------------- import { useMemo } from "react"; import { useAtomValue } from "@effect/atom-react"; import { Link } from "@tanstack/react-router"; import * as Exit from "effect/Exit"; import * as Option from "effect/Option"; import * as Predicate from "effect/Predicate"; import * as Schema from "effect/Schema"; import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"; import { integrationsOptimisticAtom } from "../api/atoms"; const ErrorMessage = Schema.Struct({ message: Schema.String }); const decodeErrorMessage = Schema.decodeUnknownOption(ErrorMessage); /** The failed Exit's `message`, or `fallback` when the error carries none. */ export const errorMessageFromExit = (exit: Exit.Exit, fallback: string): string => Option.match(Option.flatMap(Exit.findErrorOption(exit), decodeErrorMessage), { onNone: () => fallback, onSome: ({ message }) => message, }); export const isIntegrationAlreadyExistsExit = (exit: Exit.Exit): boolean => Option.match(Exit.findErrorOption(exit), { onNone: () => false, onSome: Predicate.isTagged("IntegrationAlreadyExistsError"), }); export const integrationExistsMessage = (slug: string): string => `An integration named "${slug}" already exists. To add more authentication, update your existing integration.`; /** Decode an add-integration failure into its inline message: the * already-exists guard message for a slug collision, otherwise the error's * own message (or `fallback`). */ export const addIntegrationErrorMessage = ( exit: Exit.Exit, slug: string, fallback: string, ): string => isIntegrationAlreadyExistsExit(exit) ? integrationExistsMessage(slug) : errorMessageFromExit(exit, fallback); /** Whether `slug` is already taken in the tenant-scoped catalog (pre-empting * the API's `IntegrationAlreadyExistsError`). Blank slugs never collide — * they let the server assign one. */ export function useSlugAlreadyExists(slug: string): boolean { const integrationsResult = useAtomValue(integrationsOptimisticAtom); return useMemo( () => slug.length > 0 && AsyncResult.isSuccess(integrationsResult) && integrationsResult.value.some((integration) => String(integration.slug) === slug), [integrationsResult, slug], ); } /** Inline destructive alert for a form-level error message. */ export function FormErrorAlert(props: { readonly message: string }) { return (

{props.message}

); } /** The slug-collision alert with a link to the existing integration. Render * gated by the caller (typically `show && !adding`). */ export function SlugCollisionAlert(props: { readonly slug: string }) { return (

An integration named "{props.slug}" already exists. To add more authentication, update your existing integration.{" "} Open it

); }