Openstatus
www.openstatus.dev
1"use client";
2
3import { FormCardGroup } from "@/components/forms/form-card";
4import { useTRPC } from "@/lib/trpc/client";
5import { deserialize } from "@openstatus/assertions";
6import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
7import { useParams, useRouter } from "next/navigation";
8import { FormDangerZone } from "./form-danger-zone";
9import {
10 FOLLOW_REDIRECTS_DEFAULT,
11 FormFollowRedirect,
12} from "./form-follow-redirect";
13import { FormGeneral } from "./form-general";
14import { FormNotifiers } from "./form-notifiers";
15import { FormOtel } from "./form-otel";
16import { FormResponseTime } from "./form-response-time";
17import { FormRetry, RETRY_DEFAULT } from "./form-retry";
18import { FormSchedulingRegions } from "./form-scheduling-regions";
19import { FormTags } from "./form-tags";
20import { FormVisibility } from "./form-visibility";
21
22export function FormMonitorUpdate() {
23 const { id } = useParams<{ id: string }>();
24 const trpc = useTRPC();
25 const router = useRouter();
26 const queryClient = useQueryClient();
27 const { data: monitor, refetch } = useQuery(
28 trpc.monitor.get.queryOptions({ id: Number.parseInt(id) }),
29 );
30 const { data: statusPages } = useQuery(trpc.page.list.queryOptions());
31 const { data: privateLocations } = useQuery(
32 trpc.privateLocation.list.queryOptions(),
33 );
34 const { data: notifications } = useQuery(
35 trpc.notification.list.queryOptions(),
36 );
37 const { data: workspace } = useQuery(trpc.workspace.get.queryOptions());
38 const updateRetryMutation = useMutation(
39 trpc.monitor.updateRetry.mutationOptions({
40 onSuccess: () => refetch(),
41 }),
42 );
43 const updateOtelMutation = useMutation(
44 trpc.monitor.updateOtel.mutationOptions({
45 onSuccess: () => refetch(),
46 }),
47 );
48 const updatePublicMutation = useMutation(
49 trpc.monitor.updatePublic.mutationOptions({
50 onSuccess: () => refetch(),
51 }),
52 );
53 const updateSchedulingRegionsMutation = useMutation(
54 trpc.monitor.updateSchedulingRegions.mutationOptions({
55 onSuccess: () => refetch(),
56 }),
57 );
58 const updateResponseTimeMutation = useMutation(
59 trpc.monitor.updateResponseTime.mutationOptions({
60 onSuccess: () => refetch(),
61 }),
62 );
63 const updateTagsMutation = useMutation(
64 trpc.monitor.updateTags.mutationOptions({
65 onSuccess: () => refetch(),
66 }),
67 );
68 const updateFollowRedirectsMutation = useMutation(
69 trpc.monitor.updateFollowRedirects.mutationOptions({
70 onSuccess: () => refetch(),
71 }),
72 );
73
74 const updateGeneralMutation = useMutation(
75 trpc.monitor.updateGeneral.mutationOptions({
76 onSuccess: () => {
77 // NOTE: invalidate the list query to update the monitor in the list (especially the name)
78 queryClient.invalidateQueries({
79 queryKey: trpc.monitor.list.queryKey(),
80 });
81 refetch();
82 },
83 onError: (err) => {
84 // TODO: open dialog
85 console.error(err);
86 },
87 }),
88 );
89
90 const updateNotifiersMutation = useMutation(
91 trpc.monitor.updateNotifiers.mutationOptions({
92 onSuccess: () => refetch(),
93 }),
94 );
95
96 const deleteMonitorMutation = useMutation(
97 trpc.monitor.delete.mutationOptions({
98 onSuccess: () => {
99 queryClient.invalidateQueries({
100 queryKey: trpc.monitor.list.queryKey(),
101 });
102 router.push("/monitors");
103 },
104 }),
105 );
106
107 if (
108 !monitor ||
109 !statusPages ||
110 !notifications ||
111 !workspace ||
112 !privateLocations
113 )
114 return null;
115
116 return (
117 <FormCardGroup>
118 <FormGeneral
119 defaultValues={{
120 type: monitor.jobType as "http" | "tcp",
121 url: monitor.url,
122 name: monitor.name,
123 method: monitor.method as "GET" | "POST" | "PUT" | "PATCH" | "DELETE",
124 headers: monitor.headers ?? [],
125 body: monitor.body,
126 active: monitor.active ?? true,
127 // TODO: move to server after migration
128 assertions: monitor?.assertions
129 ? deserialize(monitor?.assertions).map((a) => a.schema)
130 : [],
131 skipCheck: false,
132 saveCheck: false,
133 }}
134 onSubmit={async (values) => {
135 await updateGeneralMutation.mutateAsync({
136 id: Number.parseInt(id),
137 name: values.name,
138 jobType: values.type,
139 url: values.url,
140 method: values.method,
141 headers: values.headers,
142 body: values.body,
143 assertions: values.assertions,
144 skipCheck: values.skipCheck,
145 saveCheck: values.saveCheck,
146 active: values.active,
147 });
148 }}
149 />
150 <FormResponseTime
151 defaultValues={{
152 timeout: monitor.timeout,
153 degradedAfter: monitor.degradedAfter ?? undefined,
154 }}
155 onSubmit={async (values) => {
156 await updateResponseTimeMutation.mutateAsync({
157 id: Number.parseInt(id),
158 timeout: values.timeout,
159 degradedAfter: values.degradedAfter ?? undefined,
160 });
161 }}
162 />
163 <FormTags
164 defaultValues={{
165 tags: monitor.tags,
166 }}
167 onSubmit={async (values) => {
168 await updateTagsMutation.mutateAsync({
169 id: Number.parseInt(id),
170 tags: values.tags.map((tag) => tag.id),
171 });
172 }}
173 />
174 <FormSchedulingRegions
175 privateLocations={privateLocations}
176 defaultValues={{
177 regions: monitor.regions,
178 periodicity: monitor.periodicity,
179 privateLocations: monitor.privateLocations.map(({ id }) => id),
180 }}
181 onSubmit={async (values) => {
182 await updateSchedulingRegionsMutation.mutateAsync({
183 id: Number.parseInt(id),
184 regions: values.regions,
185 periodicity: values.periodicity,
186 privateLocations: values.privateLocations,
187 });
188 }}
189 />
190 <FormNotifiers
191 notifiers={notifications}
192 defaultValues={{
193 notifiers: monitor.notifications.map(({ id }) => id),
194 }}
195 onSubmit={async (values) => {
196 await updateNotifiersMutation.mutateAsync({
197 id: Number.parseInt(id),
198 notifiers: values.notifiers,
199 });
200 }}
201 />
202 <FormRetry
203 defaultValues={{
204 retry: monitor.retry ?? RETRY_DEFAULT,
205 }}
206 onSubmit={async (values) =>
207 await updateRetryMutation.mutateAsync({
208 id: Number.parseInt(id),
209 retry: values.retry ?? RETRY_DEFAULT,
210 })
211 }
212 />
213 <FormFollowRedirect
214 defaultValues={{
215 followRedirects: monitor.followRedirects ?? FOLLOW_REDIRECTS_DEFAULT,
216 }}
217 onSubmit={async (values) =>
218 await updateFollowRedirectsMutation.mutateAsync({
219 id: Number.parseInt(id),
220 followRedirects: values.followRedirects ?? FOLLOW_REDIRECTS_DEFAULT,
221 })
222 }
223 />
224 <FormOtel
225 locked={workspace.limits.otel === false}
226 defaultValues={{
227 endpoint: monitor.otelEndpoint ?? "",
228 headers: monitor.otelHeaders ?? [],
229 }}
230 onSubmit={async (values) => {
231 await updateOtelMutation.mutateAsync({
232 id: Number.parseInt(id),
233 otelEndpoint: values.endpoint,
234 otelHeaders: values.headers,
235 });
236 }}
237 />
238 <FormVisibility
239 defaultValues={{
240 visibility: monitor.public ?? false,
241 }}
242 onSubmit={async (values) => {
243 await updatePublicMutation.mutateAsync({
244 id: Number.parseInt(id),
245 public: values.visibility,
246 });
247 }}
248 />
249 <FormDangerZone
250 title={monitor.name}
251 onSubmit={async () => {
252 await deleteMonitorMutation.mutateAsync({ id: Number.parseInt(id) });
253 }}
254 />
255 </FormCardGroup>
256 );
257}