Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. ๐ซ Status page with uptime monitoring & API monitoring as code ๐ซ openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor
Something went wrong. Try again.
7.9 kB ยท 238 lines
TSX
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239import { Info } from "@openstatus/icons";import type { ThemeKey } from "@openstatus/theme-store";import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";import { useParams, useRouter } from "next/navigation";
import { Link } from "@/components/common/link";import { Note } from "@/components/common/note";import { FormCardGroup } from "@/components/forms/form-card";import { useTRPC } from "@/lib/trpc/client";
import { FormAppearance } from "./form-appearance";import { FormCustomDomain } from "./form-custom-domain";import { FormCustomTheme } from "./form-custom-theme";import { FormDangerZone } from "./form-danger-zone";import { FormGeneral } from "./form-general";import { FormLinks } from "./form-links";import { FormLocale } from "./form-locale";import { FormPageAccess } from "./form-page-access";
export function FormStatusPageUpdate() { const { id } = useParams<{ id: string }>(); const router = useRouter(); const trpc = useTRPC(); const { data: statusPage, refetch } = useQuery( trpc.page.get.queryOptions({ id: Number.parseInt(id) }), ); const queryClient = useQueryClient(); const { data: monitors } = useQuery(trpc.monitor.list.queryOptions()); const { data: workspace } = useQuery(trpc.workspace.get.queryOptions()); const updateStatusPageMutation = useMutation( trpc.page.updateGeneral.mutationOptions({ onSuccess: () => { refetch(); // NOTE: invalidate status page list to update name queryClient.invalidateQueries({ queryKey: trpc.page.list.queryKey(), }); }, }), );
const updatePasswordProtectionMutation = useMutation( trpc.page.updatePasswordProtection.mutationOptions({ onSuccess: () => refetch(), }), );
const updateCustomDomainMutation = useMutation( trpc.page.updateCustomDomain.mutationOptions({ onSuccess: () => refetch(), }), );
const updatePageAppearanceMutation = useMutation( trpc.page.updateAppearance.mutationOptions({ onSuccess: () => refetch(), }), );
const deleteStatusPageMutation = useMutation( trpc.page.delete.mutationOptions({ onSuccess: () => { router.push("/status-pages"); // NOTE: invalidate workspace to update the usage queryClient.invalidateQueries({ queryKey: trpc.workspace.get.queryKey(), }); // NOTE: invalidate status page list to update the usage queryClient.invalidateQueries({ queryKey: trpc.page.list.queryKey(), }); }, }), );
const updateCustomThemeMutation = useMutation( trpc.page.updateCustomTheme.mutationOptions({ onSuccess: () => refetch(), }), );
const updateLinksMutation = useMutation( trpc.page.updateLinks.mutationOptions({ onSuccess: () => refetch(), }), );
const updateLocalesMutation = useMutation( trpc.page.updateLocales.mutationOptions({ onSuccess: () => refetch(), }), );
if (!statusPage || !monitors || !workspace) return null;
return ( <FormCardGroup> <Note color="info"> <Info /> <p> Looking to connect monitors to your status page? Configure them on the{" "} <Link href={`/status-pages/${id}/components`}>Components</Link> page. </p> </Note> <FormGeneral defaultValues={{ title: statusPage.title, slug: statusPage.slug, description: statusPage.description, icon: statusPage.icon ?? undefined, }} onSubmit={async (values) => { await updateStatusPageMutation.mutateAsync({ id: Number.parseInt(id), title: values.title, slug: values.slug, description: values.description ?? "", icon: values.icon ?? "", }); }} /> <FormCustomDomain locked={workspace.limits["custom-domain"] === false} defaultValues={{ domain: statusPage.customDomain ?? undefined, }} onSubmit={async (values) => { await updateCustomDomainMutation.mutateAsync({ id: Number.parseInt(id), customDomain: values.domain, }); }} /> <FormLinks defaultValues={{ homepageUrl: statusPage.homepageUrl ?? "", contactUrl: statusPage.contactUrl ?? "", }} onSubmit={async (values) => { await updateLinksMutation.mutateAsync({ id: Number.parseInt(id), homepageUrl: values.homepageUrl ?? undefined, contactUrl: values.contactUrl ?? undefined, }); }} /> <FormAppearance defaultValues={{ forceTheme: statusPage.forceTheme ?? "system", configuration: { theme: statusPage.configuration?.theme ?? "default", }, }} onSubmit={async (values) => { await updatePageAppearanceMutation.mutateAsync({ id: Number.parseInt(id), forceTheme: values.forceTheme, // `FormAppearance` keeps its internal `theme` value as a // loose `string`; cast to the canonical `ThemeKey` from // `@openstatus/theme-store` (same source the service input // enum is derived from). Invalid submits are caught by the // router's zod parse. configuration: { theme: values.configuration.theme as ThemeKey, }, }); }} /> <FormCustomTheme locked={workspace.limits["custom-theme"] === false} themeKey={statusPage.configuration?.theme ?? "default"} defaultValue={statusPage.customTheme} onSubmit={async (values) => { await updateCustomThemeMutation.mutateAsync({ id: Number.parseInt(id), customTheme: values.customTheme, }); }} /> <FormLocale locked={workspace.limits.i18n === false} defaultValues={{ defaultLocale: statusPage.defaultLocale ?? "en", locales: statusPage.locales, }} onSubmit={async (values) => { await updateLocalesMutation.mutateAsync({ id: Number.parseInt(id), defaultLocale: values.defaultLocale, locales: values.locales, }); }} /> <FormPageAccess lockedMap={ new Map([ ["public", false], ["password", workspace.limits["password-protection"] === false], [ "email-domain", workspace.limits["email-domain-protection"] === false, ], ["ip-restriction", workspace.limits["ip-restriction"] === false], ]) } allowIndexLocked={workspace.limits["no-index"] === false} defaultValues={{ accessType: statusPage.accessType, password: statusPage.password ?? undefined, authEmailDomains: statusPage.authEmailDomains ?? [], allowedIpRanges: statusPage.allowedIpRanges ?? [], allowIndex: statusPage.allowIndex ?? true, }} onSubmit={async (values) => { await updatePasswordProtectionMutation.mutateAsync({ id: Number.parseInt(id), accessType: values.accessType, password: values.password, authEmailDomains: values.authEmailDomains, allowedIpRanges: values.accessType === "ip-restriction" ? (values.allowedIpRanges ?? null) : null, allowIndex: values.allowIndex, }); }} /> <FormDangerZone title={statusPage.title} onSubmit={async () => { await deleteStatusPageMutation.mutateAsync({ id: Number.parseInt(id), }); }} /> </FormCardGroup> );}