From 9c64fe2d57ed5f74ce8f9e93d8b9bea22cd80112 Mon Sep 17 00:00:00 2001 From: Thibault Le Ouay Date: Sat, 2 Dec 2023 13:10:10 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20rename=20table=20(#487)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * πŸ“ rename table * πŸ“ rename table * πŸ“ rename table * πŸ“ rename table * πŸ“ rename table * πŸ“ rename table * πŸ“ rename table * πŸ“ rename table * πŸ“ rename table * πŸ“reaname table * πŸ”₯ * wip: fix zod and replace incident with status report * fix: wrong status-report url * fix: tests * wip: rename * fix: status --------- Co-authored-by: mxkaske --- apps/server/fly.toml | 1 - apps/server/src/public/status.ts | 46 +- apps/server/src/v1/index.ts | 8 +- ...{incident.test.ts => statusReport.test.ts} | 17 +- .../src/v1/{incident.ts => statusReport.ts} | 180 +-- ...ate.test.ts => statusReportUpdate.test.ts} | 10 +- ...ncidentUpdate.ts => statusReportUpdate.ts} | 76 +- .../[workspaceSlug]/integrations/page.tsx | 2 +- .../_components/action-button.tsx | 20 +- .../_components/delete-status-update.tsx} | 6 +- .../_components/empty-state.tsx | 6 +- .../edit/loading.tsx | 0 .../edit/page.tsx | 25 +- .../{incidents => status-reports}/loading.tsx | 0 .../{incidents => status-reports}/page.tsx | 36 +- .../update/edit/loading.tsx | 0 .../update/edit/page.tsx | 24 +- apps/web/src/app/page.tsx | 1 + .../status-page/[domain]/incidents/page.tsx | 2 +- .../web/src/app/status-page/[domain]/page.tsx | 10 +- ...cident-form.tsx => status-report-form.tsx} | 50 +- ...form.tsx => status-report-update-form.tsx} | 33 +- apps/web/src/components/icons.tsx | 2 + .../components/status-page/incident-list.tsx | 18 +- .../components/status-page/status-check.tsx | 8 +- .../affected-monitors.tsx | 0 .../{incidents => status-update}/events.tsx | 18 +- .../status-badge.tsx | 0 apps/web/src/config/features.ts | 4 +- apps/web/src/config/pages.ts | 12 +- fly.toml | 18 - packages/api/src/analytics.ts | 2 +- packages/api/src/edge.ts | 4 +- packages/api/src/router/incident.ts | 317 ----- packages/api/src/router/page.test.ts | 2 +- packages/api/src/router/page.ts | 56 +- packages/api/src/router/statusReport.ts | 329 +++++ packages/db/drizzle/0011_bright_jazinda.sql | 7 + packages/db/drizzle/meta/0011_snapshot.json | 1099 +++++++++++++++++ packages/db/drizzle/meta/_journal.json | 9 +- packages/db/src/schema/incidents/incident.ts | 126 -- .../db/src/schema/incidents/validation.ts | 41 - packages/db/src/schema/index.ts | 2 +- packages/db/src/schema/monitors/monitor.ts | 4 +- packages/db/src/schema/pages/page.ts | 4 +- packages/db/src/schema/shared.ts | 23 +- .../{incidents => status_reports}/index.ts | 2 +- .../schema/status_reports/status_reports.ts | 132 ++ .../src/schema/status_reports/validation.ts | 53 + packages/db/src/seed.mts | 12 +- packages/emails/index.ts | 2 +- .../tinybird/src/audit-log/base-validation.ts | 3 +- 52 files changed, 2012 insertions(+), 850 deletions(-) rename apps/server/src/v1/{incident.test.ts => statusReport.test.ts} (59%) rename apps/server/src/v1/{incident.ts => statusReport.ts} (50%) rename apps/server/src/v1/{incidentUpdate.test.ts => statusReportUpdate.test.ts} (71%) rename apps/server/src/v1/{incidentUpdate.ts => statusReportUpdate.ts} (61%) rename apps/web/src/app/app/(dashboard)/[workspaceSlug]/{incidents => status-reports}/_components/action-button.tsx (87%) rename apps/web/src/app/app/(dashboard)/[workspaceSlug]/{incidents/_components/delete-incident-update.tsx => status-reports/_components/delete-status-update.tsx} (92%) rename apps/web/src/app/app/(dashboard)/[workspaceSlug]/{incidents => status-reports}/_components/empty-state.tsx (69%) rename apps/web/src/app/app/(dashboard)/[workspaceSlug]/{incidents => status-reports}/edit/loading.tsx (100%) rename apps/web/src/app/app/(dashboard)/[workspaceSlug]/{incidents => status-reports}/edit/page.tsx (67%) rename apps/web/src/app/app/(dashboard)/[workspaceSlug]/{incidents => status-reports}/loading.tsx (100%) rename apps/web/src/app/app/(dashboard)/[workspaceSlug]/{incidents => status-reports}/page.tsx (72%) rename apps/web/src/app/app/(dashboard)/[workspaceSlug]/{incidents => status-reports}/update/edit/loading.tsx (100%) rename apps/web/src/app/app/(dashboard)/[workspaceSlug]/{incidents => status-reports}/update/edit/page.tsx (58%) rename apps/web/src/components/forms/{incident-form.tsx => status-report-form.tsx} (93%) rename apps/web/src/components/forms/{incident-update-form.tsx => status-report-update-form.tsx} (88%) rename apps/web/src/components/{incidents => status-update}/affected-monitors.tsx (100%) rename apps/web/src/components/{incidents => status-update}/events.tsx (85%) rename apps/web/src/components/{incidents => status-update}/status-badge.tsx (100%) delete mode 100644 fly.toml delete mode 100644 packages/api/src/router/incident.ts create mode 100644 packages/api/src/router/statusReport.ts create mode 100644 packages/db/drizzle/0011_bright_jazinda.sql create mode 100644 packages/db/drizzle/meta/0011_snapshot.json delete mode 100644 packages/db/src/schema/incidents/incident.ts delete mode 100644 packages/db/src/schema/incidents/validation.ts rename packages/db/src/schema/{incidents => status_reports}/index.ts (65%) create mode 100644 packages/db/src/schema/status_reports/status_reports.ts create mode 100644 packages/db/src/schema/status_reports/validation.ts diff --git a/apps/server/fly.toml b/apps/server/fly.toml index 084e461d09..3afda97853 100644 --- a/apps/server/fly.toml +++ b/apps/server/fly.toml @@ -15,7 +15,6 @@ primary_region = "ams" force_https = true auto_stop_machines = false auto_start_machines = false - min_machines_running = 12 processes = ["app"] [http_service.concurrency] type = "requests" diff --git a/apps/server/src/public/status.ts b/apps/server/src/public/status.ts index d6b0b530c1..aeb6b62e4a 100644 --- a/apps/server/src/public/status.ts +++ b/apps/server/src/public/status.ts @@ -1,14 +1,14 @@ import { Hono } from "hono"; import { endTime, setMetric, startTime } from "hono/timing"; -import { db, eq } from "@openstatus/db"; +import { and, db, eq } from "@openstatus/db"; import { - incident, monitor, - monitorsToIncidents, monitorsToPages, + monitorsToStatusReport, page, - pagesToIncidents, + pagesToStatusReports, + statusReport, } from "@openstatus/db/src/schema"; import { getMonitorList, Tinybird } from "@openstatus/tinybird"; import { Redis } from "@openstatus/upstash"; @@ -36,40 +36,44 @@ status.get("/:slug", async (c) => { const { slug } = c.req.param(); const cache = await redis.get(slug); + if (cache) { setMetric(c, "OpenStatus-Cache", "HIT"); - return c.json({ status: cache }); } startTime(c, "database"); - // { monitors, pages, monitors_to_pages } + const monitorData = await db .select() .from(monitorsToPages) - .leftJoin(monitor, eq(monitorsToPages.monitorId, monitor.id)) + .leftJoin(monitor, and(eq(monitorsToPages.monitorId, monitor.id))) + .leftJoin( + monitorsToStatusReport, + eq(monitor.id, monitorsToStatusReport.monitorId), + ) .leftJoin( - monitorsToIncidents, - eq(monitor.id, monitorsToIncidents.monitorId), + statusReport, + eq(monitorsToStatusReport.statusReportId, statusReport.id), ) - .leftJoin(incident, eq(monitorsToIncidents.incidentId, incident.id)) .leftJoin(page, eq(monitorsToPages.pageId, page.id)) - .where(eq(page.slug, slug)) + .where(eq(page.slug, slug)) // TODO: query only active monitors .all(); - const pageIncidentData = await db + const pageStatusReportData = await db .select() - .from(pagesToIncidents) - .leftJoin(incident, eq(pagesToIncidents.incidentId, incident.id)) - .leftJoin(page, eq(pagesToIncidents.pageId, page.id)) + .from(pagesToStatusReports) + .leftJoin( + statusReport, + eq(pagesToStatusReports.statusReportId, statusReport.id), + ) + .leftJoin(page, eq(pagesToStatusReports.pageId, page.id)) .where(eq(page.slug, slug)) .all(); - endTime(c, "database"); - - const isIncident = [...pageIncidentData, ...monitorData].some((data) => { - if (!data.incident) return false; - return !["monitoring", "resolved"].includes(data.incident.status); + const isIncident = [...pageStatusReportData, ...monitorData].some((data) => { + if (!data.status_report) return false; + return !["monitoring", "resolved"].includes(data.status_report.status); }); startTime(c, "clickhouse"); @@ -83,7 +87,7 @@ status.get("/:slug", async (c) => { }), ); endTime(c, "clickhouse"); - // { ok, count } + const data = lastMonitorPings.reduce( (prev, curr) => { if (curr.status === "fulfilled") { diff --git a/apps/server/src/v1/index.ts b/apps/server/src/v1/index.ts index 10cb90d7ef..7d36a77259 100644 --- a/apps/server/src/v1/index.ts +++ b/apps/server/src/v1/index.ts @@ -3,10 +3,10 @@ import { logger } from "hono/logger"; import type { Plan } from "@openstatus/plans"; -import { incidentApi } from "./incident"; -import { incidenUpdateApi } from "./incidentUpdate"; import { middleware } from "./middleware"; import { monitorApi } from "./monitor"; +import { statusReportApi } from "./statusReport"; +import { statusReportUpdateApi } from "./statusReportUpdate"; export type Variables = { workspaceId: string; @@ -29,6 +29,6 @@ api.doc("/openapi", { api.use("/*", middleware); api.use("/*", logger()); api.route("/monitor", monitorApi); -api.route("/incident_update", incidenUpdateApi); +api.route("/status_report_update", statusReportUpdateApi); -api.route("/incident", incidentApi); +api.route("/status_report", statusReportApi); diff --git a/apps/server/src/v1/incident.test.ts b/apps/server/src/v1/statusReport.test.ts similarity index 59% rename from apps/server/src/v1/incident.test.ts rename to apps/server/src/v1/statusReport.test.ts index 336cc9ab3a..302953b064 100644 --- a/apps/server/src/v1/incident.test.ts +++ b/apps/server/src/v1/statusReport.test.ts @@ -2,8 +2,8 @@ import { expect, test } from "bun:test"; import { api } from "."; -test("GET one incident update ", async () => { - const res = await api.request("/incident/1", { +test("GET one status report", async () => { + const res = await api.request("/status_report/1", { headers: { "x-openstatus-key": "1", }, @@ -12,13 +12,14 @@ test("GET one incident update ", async () => { expect(await res.json()).toMatchObject({ id: 1, status: "investigating", - title: "Test Incident", - incident_updates: expect.any(Array), + title: "Test Status Report", + // TODO: discuss if we should return `updates` instead of `status_report_updates` + status_report_updates: expect.any(Array), }); }); -test("create one incident ", async () => { - const res = await api.request("/incident", { +test("create one status report", async () => { + const res = await api.request("/status_report", { method: "POST", headers: { "x-openstatus-key": "1", @@ -27,13 +28,13 @@ test("create one incident ", async () => { body: JSON.stringify({ status: "investigating", date: "2023-11-08T21:03:13.000Z", - title: "Test Incident", + title: "Test Status Report", }), }); expect(res.status).toBe(200); expect(await res.json()).toMatchObject({ id: expect.any(Number), status: "investigating", - title: "Test Incident", + title: "Test Status Report", }); }); diff --git a/apps/server/src/v1/incident.ts b/apps/server/src/v1/statusReport.ts similarity index 50% rename from apps/server/src/v1/incident.ts rename to apps/server/src/v1/statusReport.ts index ceca4336de..e030279246 100644 --- a/apps/server/src/v1/incident.ts +++ b/apps/server/src/v1/statusReport.ts @@ -2,16 +2,16 @@ import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi"; import { and, db, eq } from "@openstatus/db"; import { - incident, - incidentStatus, - incidentUpdate, + statusReport, + statusReportStatus, + statusReportUpdate, } from "@openstatus/db/src/schema"; -import { incidentUpdateSchema } from "./incidentUpdate"; import type { Variables } from "./index"; import { ErrorSchema } from "./shared"; +import { statusUpdateSchema } from "./statusReportUpdate"; -const incidentApi = new OpenAPIHono<{ Variables: Variables }>(); +const statusReportApi = new OpenAPIHono<{ Variables: Variables }>(); const ParamsSchema = z.object({ id: z @@ -22,13 +22,13 @@ const ParamsSchema = z.object({ name: "id", in: "path", }, - description: "The id of the incident", + description: "The id of the status report", example: "1", }), }); -const createIncidentUpdateSchema = z.object({ - status: z.enum(incidentStatus).openapi({ +const createStatusReportUpdateSchema = z.object({ + status: z.enum(statusReportStatus).openapi({ description: "The status of the update", }), date: z.string().openapi({ @@ -39,43 +39,39 @@ const createIncidentUpdateSchema = z.object({ }), }); -const incidentSchema = z.object({ +const statusSchema = z.object({ title: z.string().openapi({ example: "Documenso", - description: "The title of the incident", + description: "The title of the status report", }), - status: z.enum(incidentStatus).openapi({ - description: "The current status of the incident", + status: z.enum(statusReportStatus).openapi({ + description: "The current status of the report", }), }); -const incidentResultSchema = incidentSchema.extend({ - id: z.number().openapi({ description: "The id of the incident" }), -}); - -const incidentExtendedSchema = incidentSchema.extend({ - id: z.number().openapi({ description: "The id of the incident" }), - incident_updates: z +const statusReportExtendedSchema = statusSchema.extend({ + id: z.number().openapi({ description: "The id of the status report" }), + status_report_updates: z .array(z.number()) .openapi({ - description: "The ids of the incident updates", + description: "The ids of the status report updates", }) .default([]), }); const getAllRoute = createRoute({ method: "get", - tags: ["incident"], - description: "Get all incidents", + tags: ["status_report"], + description: "Get all status reports", path: "/", request: {}, responses: { 200: { content: { "application/json": { - schema: z.array(incidentExtendedSchema), + schema: z.array(statusReportExtendedSchema), }, }, - description: "Get all incidents", + description: "Get all status reports", }, 400: { content: { @@ -88,23 +84,25 @@ const getAllRoute = createRoute({ }, }); -incidentApi.openapi(getAllRoute, async (c) => { +statusReportApi.openapi(getAllRoute, async (c) => { const workspaceId = Number(c.get("workspaceId")); - const _incidents = await db.query.incident.findMany({ + const _statusReports = await db.query.statusReport.findMany({ with: { - incidentUpdates: true, + statusReportUpdates: true, }, - where: eq(incident.workspaceId, workspaceId), + where: eq(statusReport.workspaceId, workspaceId), }); - if (!_incidents) return c.jsonT({ code: 404, message: "Not Found" }); + if (!_statusReports) return c.jsonT({ code: 404, message: "Not Found" }); - const data = z.array(incidentExtendedSchema).parse( - _incidents.map((incident) => ({ - ...incident, - incident_updates: incident.incidentUpdates.map((incidentUpdate) => { - return incidentUpdate.id; - }), + const data = z.array(statusReportExtendedSchema).parse( + _statusReports.map((statusReport) => ({ + ...statusReport, + status_report_updates: statusReport.statusReportUpdates.map( + (statusReportUpdate) => { + return statusReportUpdate.id; + }, + ), })), ); @@ -113,8 +111,8 @@ incidentApi.openapi(getAllRoute, async (c) => { const getRoute = createRoute({ method: "get", - tags: ["incident"], - description: "Get an incident", + tags: ["status_report"], + description: "Get an status report", path: "/:id", request: { params: ParamsSchema, @@ -123,10 +121,10 @@ const getRoute = createRoute({ 200: { content: { "application/json": { - schema: incidentExtendedSchema, + schema: statusReportExtendedSchema, }, }, - description: "Get all incidents", + description: "Get all status reports", }, 400: { content: { @@ -139,26 +137,26 @@ const getRoute = createRoute({ }, }); -incidentApi.openapi(getRoute, async (c) => { +statusReportApi.openapi(getRoute, async (c) => { const workspaceId = Number(c.get("workspaceId")); const { id } = c.req.valid("param"); - const incidentId = Number(id); - const _incident = await db.query.incident.findFirst({ + const statusUpdateId = Number(id); + const _statusUpdate = await db.query.statusReport.findFirst({ with: { - incidentUpdates: true, + statusReportUpdates: true, }, where: and( - eq(incident.workspaceId, workspaceId), - eq(incident.id, incidentId), + eq(statusReport.workspaceId, workspaceId), + eq(statusReport.id, statusUpdateId), ), }); - if (!_incident) return c.jsonT({ code: 404, message: "Not Found" }); - const data = incidentExtendedSchema.parse({ - ..._incident, - incident_updates: _incident.incidentUpdates.map( - (incidentUpdate) => incidentUpdate.id, + if (!_statusUpdate) return c.jsonT({ code: 404, message: "Not Found" }); + const data = statusReportExtendedSchema.parse({ + ..._statusUpdate, + status_report_updates: _statusUpdate.statusReportUpdates.map( + (update) => update.id, ), }); @@ -167,15 +165,15 @@ incidentApi.openapi(getRoute, async (c) => { const postRoute = createRoute({ method: "post", - tags: ["incident"], - description: "Create an incident", + tags: ["status_report"], + description: "Create an status report", path: "/", request: { body: { - description: "The incident to create", + description: "The status report to create", content: { "application/json": { - schema: incidentSchema, + schema: statusSchema, }, }, }, @@ -184,10 +182,10 @@ const postRoute = createRoute({ 200: { content: { "application/json": { - schema: incidentExtendedSchema, + schema: statusReportExtendedSchema, }, }, - description: "Incident created", + description: "Status report created", }, 400: { content: { @@ -200,12 +198,12 @@ const postRoute = createRoute({ }, }); -incidentApi.openapi(postRoute, async (c) => { +statusReportApi.openapi(postRoute, async (c) => { const input = c.req.valid("json"); const workspaceId = Number(c.get("workspaceId")); - const _newIncident = await db - .insert(incident) + const _newStatusReport = await db + .insert(statusReport) .values({ ...input, workspaceId: workspaceId, @@ -213,15 +211,15 @@ incidentApi.openapi(postRoute, async (c) => { .returning() .get(); - const data = incidentExtendedSchema.parse(_newIncident); + const data = statusReportExtendedSchema.parse(_newStatusReport); return c.jsonT(data); }); const deleteRoute = createRoute({ method: "delete", - tags: ["incident"], - description: "Delete an incident", + tags: ["status_report"], + description: "Delete an status report", path: "/:id", request: { params: ParamsSchema, @@ -237,7 +235,7 @@ const deleteRoute = createRoute({ }), }, }, - description: "Incident deleted", + description: "Status report deleted", }, 400: { content: { @@ -250,38 +248,41 @@ const deleteRoute = createRoute({ }, }); -incidentApi.openapi(deleteRoute, async (c) => { +statusReportApi.openapi(deleteRoute, async (c) => { const workspaceId = Number(c.get("workspaceId")); const { id } = c.req.valid("param"); - const incidentId = Number(id); - const _incident = await db + const statusReportId = Number(id); + const _statusReport = await db .select() - .from(incident) - .where(eq(incident.id, incidentId)) + .from(statusReport) + .where(eq(statusReport.id, statusReportId)) .get(); - if (!_incident) return c.jsonT({ code: 404, message: "Not Found" }); + if (!_statusReport) return c.jsonT({ code: 404, message: "Not Found" }); - if (workspaceId !== _incident.workspaceId) + if (workspaceId !== _statusReport.workspaceId) return c.jsonT({ code: 401, message: "Unauthorized" }); - await db.delete(incident).where(eq(incident.id, incidentId)).run(); + await db + .delete(statusReport) + .where(eq(statusReport.id, statusReportId)) + .run(); return c.jsonT({ message: "Deleted" }); }); const postRouteUpdate = createRoute({ method: "post", - tags: ["incident"], + tags: ["status_report"], path: "/:id/update", - description: "Create an incident update", + description: "Create an status report update", request: { params: ParamsSchema, body: { - description: "the incident update", + description: "the status report update", content: { "application/json": { - schema: createIncidentUpdateSchema, + schema: createStatusReportUpdateSchema, }, }, }, @@ -290,10 +291,10 @@ const postRouteUpdate = createRoute({ 200: { content: { "application/json": { - schema: incidentUpdateSchema, + schema: statusUpdateSchema, }, }, - description: "Incident updated", + description: "Status report updated", }, 400: { content: { @@ -306,37 +307,40 @@ const postRouteUpdate = createRoute({ }, }); -incidentApi.openapi(postRouteUpdate, async (c) => { +statusReportApi.openapi(postRouteUpdate, async (c) => { const input = c.req.valid("json"); const { id } = c.req.valid("param"); const workspaceId = Number(c.get("workspaceId")); - const incidentId = Number(id); - const _incident = await db + const statusReportId = Number(id); + const _statusReport = await db .select() - .from(incident) + .from(statusReport) .where( - and(eq(incident.id, incidentId), eq(incident.workspaceId, workspaceId)), + and( + eq(statusReport.id, statusReportId), + eq(statusReport.workspaceId, workspaceId), + ), ) .get(); - if (!_incident) return c.jsonT({ code: 401, message: "Not authorized" }); + if (!_statusReport) return c.jsonT({ code: 401, message: "Not authorized" }); - const _incidentUpdate = await db - .insert(incidentUpdate) + const _statusReportUpdate = await db + .insert(statusReportUpdate) .values({ ...input, date: new Date(input.date), - incidentId: Number(id), + statusReportId: Number(id), }) .returning() .get(); - const data = incidentUpdateSchema.parse(_incidentUpdate); + const data = statusUpdateSchema.parse(_statusReportUpdate); return c.jsonT({ ...data, }); }); -export { incidentApi }; +export { statusReportApi }; diff --git a/apps/server/src/v1/incidentUpdate.test.ts b/apps/server/src/v1/statusReportUpdate.test.ts similarity index 71% rename from apps/server/src/v1/incidentUpdate.test.ts rename to apps/server/src/v1/statusReportUpdate.test.ts index 74ff1eafe0..45116c461a 100644 --- a/apps/server/src/v1/incidentUpdate.test.ts +++ b/apps/server/src/v1/statusReportUpdate.test.ts @@ -2,8 +2,8 @@ import { expect, test } from "bun:test"; import { api } from "."; -test("GET one incident update ", async () => { - const res = await api.request("/incident_update/1", { +test("GET one status report update ", async () => { + const res = await api.request("/status_report_update/1", { headers: { "x-openstatus-key": "1", }, @@ -15,8 +15,8 @@ test("GET one incident update ", async () => { }); }); -test("create one incident update ", async () => { - const res = await api.request("/incident_update", { +test("create one status report update ", async () => { + const res = await api.request("/status_report_update", { method: "POST", headers: { "x-openstatus-key": "1", @@ -26,7 +26,7 @@ test("create one incident update ", async () => { status: "investigating", date: "2023-11-08T21:03:13.000Z", message: "test", - incident_id: 1, + status_report_id: 1, }), }); expect(res.status).toBe(200); diff --git a/apps/server/src/v1/incidentUpdate.ts b/apps/server/src/v1/statusReportUpdate.ts similarity index 61% rename from apps/server/src/v1/incidentUpdate.ts rename to apps/server/src/v1/statusReportUpdate.ts index d56b52e85d..00187275d6 100644 --- a/apps/server/src/v1/incidentUpdate.ts +++ b/apps/server/src/v1/statusReportUpdate.ts @@ -2,15 +2,15 @@ import { createRoute, OpenAPIHono, z } from "@hono/zod-openapi"; import { and, db, eq } from "@openstatus/db"; import { - incident, - incidentStatus, - incidentUpdate, + statusReport, + statusReportStatus, + statusReportUpdate, } from "@openstatus/db/src/schema"; import type { Variables } from "."; import { ErrorSchema } from "./shared"; -const incidenUpdateApi = new OpenAPIHono<{ Variables: Variables }>(); +const statusReportUpdateApi = new OpenAPIHono<{ Variables: Variables }>(); const ParamsSelectSchema = z.object({ id: z @@ -26,8 +26,8 @@ const ParamsSelectSchema = z.object({ }), }); -export const incidentUpdateSchema = z.object({ - status: z.enum(incidentStatus).openapi({ +export const statusUpdateSchema = z.object({ + status: z.enum(statusReportStatus).openapi({ description: "The status of the update", }), id: z.coerce.string().openapi({ description: "The id of the update" }), @@ -41,11 +41,11 @@ export const incidentUpdateSchema = z.object({ }), }); -const createIncidentUpdateSchema = z.object({ - incident_id: z.number().openapi({ - description: "The id of the incident", +const createStatusReportUpdateSchema = z.object({ + status_report_id: z.number().openapi({ + description: "The id of the status report", }), - status: z.enum(incidentStatus).openapi({ + status: z.enum(statusReportStatus).openapi({ description: "The status of the update", }), date: z.string().datetime().openapi({ @@ -57,7 +57,7 @@ const createIncidentUpdateSchema = z.object({ }); const getUpdateRoute = createRoute({ method: "get", - tags: ["incident_update"], + tags: ["status_report_update"], path: "/:id", request: { params: ParamsSelectSchema, @@ -66,10 +66,10 @@ const getUpdateRoute = createRoute({ 200: { content: { "application/json": { - schema: incidentUpdateSchema, + schema: statusUpdateSchema, }, }, - description: "Get all incidents", + description: "Get all status report updates", }, 400: { content: { @@ -82,46 +82,46 @@ const getUpdateRoute = createRoute({ }, }); -incidenUpdateApi.openapi(getUpdateRoute, async (c) => { +statusReportUpdateApi.openapi(getUpdateRoute, async (c) => { const workspaceId = Number(c.get("workspaceId")); const { id } = c.req.valid("param"); const update = await db .select() - .from(incidentUpdate) - .where(eq(incidentUpdate.id, Number(id))) + .from(statusReportUpdate) + .where(eq(statusReportUpdate.id, Number(id))) .get(); if (!update) return c.jsonT({ code: 404, message: "Not Found" }); - const currentIncident = await db + const currentStatusReport = await db .select() - .from(incident) + .from(statusReport) .where( and( - eq(incident.id, update.incidentId), - eq(incident.workspaceId, workspaceId), + eq(statusReport.id, update.statusReportId), + eq(statusReport.workspaceId, workspaceId), ), ) .get(); - if (!currentIncident) + if (!currentStatusReport) return c.jsonT({ code: 401, message: "Not Authorized" }); - const data = incidentUpdateSchema.parse(update); + const data = statusUpdateSchema.parse(update); return c.jsonT(data); }); -const createIncidentUpdate = createRoute({ +const createStatusUpdate = createRoute({ method: "post", - tags: ["incident_update"], + tags: ["status_report_update"], path: "/", request: { body: { - description: "the incident update", + description: "the status report update", content: { "application/json": { - schema: createIncidentUpdateSchema, + schema: createStatusReportUpdateSchema, }, }, }, @@ -130,10 +130,10 @@ const createIncidentUpdate = createRoute({ 200: { content: { "application/json": { - schema: incidentUpdateSchema, + schema: statusUpdateSchema, }, }, - description: "Get all incidents", + description: "Get all status report updates", }, 400: { content: { @@ -146,35 +146,35 @@ const createIncidentUpdate = createRoute({ }, }); -incidenUpdateApi.openapi(createIncidentUpdate, async (c) => { +statusReportUpdateApi.openapi(createStatusUpdate, async (c) => { const workspaceId = Number(c.get("workspaceId")); const input = c.req.valid("json"); - const currentIncident = await db + const _currentStatusReport = await db .select() - .from(incident) + .from(statusReport) .where( and( - eq(incident.id, input.incident_id), - eq(incident.workspaceId, workspaceId), + eq(statusReport.id, input.status_report_id), + eq(statusReport.workspaceId, workspaceId), ), ) .get(); - if (!currentIncident) + if (!_currentStatusReport) return c.jsonT({ code: 401, message: "Not Authorized" }); const res = await db - .insert(incidentUpdate) + .insert(statusReportUpdate) .values({ ...input, date: new Date(input.date), - incidentId: input.incident_id, + statusReportId: input.status_report_id, }) .returning() .get(); - const data = incidentUpdateSchema.parse(res); + const data = statusUpdateSchema.parse(res); return c.jsonT(data); }); -export { incidenUpdateApi }; +export { statusReportUpdateApi }; diff --git a/apps/web/src/app/app/(dashboard)/[workspaceSlug]/integrations/page.tsx b/apps/web/src/app/app/(dashboard)/[workspaceSlug]/integrations/page.tsx index 467e0c4fe7..7aba2ba966 100644 --- a/apps/web/src/app/app/(dashboard)/[workspaceSlug]/integrations/page.tsx +++ b/apps/web/src/app/app/(dashboard)/[workspaceSlug]/integrations/page.tsx @@ -6,7 +6,7 @@ import { Container } from "@/components/dashboard/container"; import { Header } from "@/components/dashboard/header"; import { api } from "@/trpc/server"; -export default async function IncidentPage({ +export default async function IntegrationPage({ params, }: { params: { workspaceSlug: string }; diff --git a/apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/_components/action-button.tsx b/apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/_components/action-button.tsx similarity index 87% rename from apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/_components/action-button.tsx rename to apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/_components/action-button.tsx index 580ba0396f..458d9e1e17 100644 --- a/apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/_components/action-button.tsx +++ b/apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/_components/action-button.tsx @@ -6,10 +6,7 @@ import { useRouter } from "next/navigation"; import { MoreVertical } from "lucide-react"; import type * as z from "zod"; -import { - insertIncidentSchema, - // insertIncidentUpdateSchema, -} from "@openstatus/db/src/schema"; +import { insertStatusReportSchema } from "@openstatus/db/src/schema"; import { AlertDialog, AlertDialogAction, @@ -31,7 +28,10 @@ import { LoadingAnimation } from "@/components/loading-animation"; import { useToastAction } from "@/hooks/use-toast-action"; import { api } from "@/trpc/client"; -const temporary = insertIncidentSchema.pick({ id: true, workspaceSlug: true }); +const temporary = insertStatusReportSchema.pick({ + id: true, + workspaceSlug: true, +}); type Schema = z.infer; @@ -41,11 +41,11 @@ export function ActionButton(props: Schema) { const [alertOpen, setAlertOpen] = React.useState(false); const [isPending, startTransition] = React.useTransition(); - async function deleteIncident() { + async function deeteStatusReport() { startTransition(async () => { try { if (!props.id) return; - await api.incident.deleteIncident.mutate({ id: props.id }); + await api.statusReport.deleteStatusReport.mutate({ id: props.id }); toast("deleted"); router.refresh(); setAlertOpen(false); @@ -68,7 +68,7 @@ export function ActionButton(props: Schema) { - + Edit @@ -83,7 +83,7 @@ export function ActionButton(props: Schema) { Are you absolutely sure? This action cannot be undone. This will permanently delete the - incident. + status report. @@ -91,7 +91,7 @@ export function ActionButton(props: Schema) { { e.preventDefault(); - deleteIncident(); + deeteStatusReport(); }} disabled={isPending} className="bg-destructive text-destructive-foreground hover:bg-destructive/90" diff --git a/apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/_components/delete-incident-update.tsx b/apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/_components/delete-status-update.tsx similarity index 92% rename from apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/_components/delete-incident-update.tsx rename to apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/_components/delete-status-update.tsx index 9450e7d9a1..3107b36b1c 100644 --- a/apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/_components/delete-incident-update.tsx +++ b/apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/_components/delete-status-update.tsx @@ -21,7 +21,7 @@ import { LoadingAnimation } from "@/components/loading-animation"; import { useToastAction } from "@/hooks/use-toast-action"; import { api } from "@/trpc/client"; -export function DeleteIncidentUpdateButtonIcon({ id }: { id: number }) { +export function DeleteStatusReportUpdateButtonIcon({ id }: { id: number }) { const router = useRouter(); const { toast } = useToastAction(); const [alertOpen, setAlertOpen] = React.useState(false); @@ -30,7 +30,7 @@ export function DeleteIncidentUpdateButtonIcon({ id }: { id: number }) { async function onDelete() { startTransition(async () => { try { - await api.incident.deleteIncidentUpdate.mutate({ id }); + await api.statusReport.deleteStatusReportUpdate.mutate({ id }); toast("deleted"); router.refresh(); setAlertOpen(false); @@ -56,7 +56,7 @@ export function DeleteIncidentUpdateButtonIcon({ id }: { id: number }) { Are you absolutely sure? This action cannot be undone. This will permanently delete the - monitor. + status report update. diff --git a/apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/_components/empty-state.tsx b/apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/_components/empty-state.tsx similarity index 69% rename from apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/_components/empty-state.tsx rename to apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/_components/empty-state.tsx index 2c09dcdaf7..75fa4cafce 100644 --- a/apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/_components/empty-state.tsx +++ b/apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/_components/empty-state.tsx @@ -8,11 +8,11 @@ export function EmptyState() { return ( - Create + Create } /> diff --git a/apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/edit/loading.tsx b/apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/edit/loading.tsx similarity index 100% rename from apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/edit/loading.tsx rename to apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/edit/loading.tsx diff --git a/apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/edit/page.tsx b/apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/edit/page.tsx similarity index 67% rename from apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/edit/page.tsx rename to apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/edit/page.tsx index 416baa0712..4cf02abe2f 100644 --- a/apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/edit/page.tsx +++ b/apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/edit/page.tsx @@ -2,7 +2,7 @@ import { notFound } from "next/navigation"; import * as z from "zod"; import { Header } from "@/components/dashboard/header"; -import { IncidentForm } from "@/components/forms/incident-form"; +import { StatusReportForm } from "@/components/forms/status-report-form"; import { api } from "@/trpc/server"; /** @@ -27,8 +27,8 @@ export default async function EditPage({ const { id } = search.data; - const incident = id - ? await api.incident.getIncidentById.query({ + const statusUpdate = id + ? await api.statusReport.getStatusReportById.query({ id, }) : undefined; @@ -39,22 +39,27 @@ export default async function EditPage({ return (
-
+
- monitorId, ), - pages: incident?.pagesToIncidents.map(({ pageId }) => pageId), + pages: statusUpdate?.pagesToStatusReports.map( + ({ pageId }) => pageId, + ), message: "", } : undefined diff --git a/apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/loading.tsx b/apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/loading.tsx similarity index 100% rename from apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/loading.tsx rename to apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/loading.tsx diff --git a/apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/page.tsx b/apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/page.tsx similarity index 72% rename from apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/page.tsx rename to apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/page.tsx index 2bd6b1e006..1bbce27a0d 100644 --- a/apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/page.tsx +++ b/apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/page.tsx @@ -8,63 +8,63 @@ import { Container } from "@/components/dashboard/container"; import { Header } from "@/components/dashboard/header"; import { HelpCallout } from "@/components/dashboard/help-callout"; import { Icons } from "@/components/icons"; -import { AffectedMonitors } from "@/components/incidents/affected-monitors"; -import { Events } from "@/components/incidents/events"; -import { StatusBadge } from "@/components/incidents/status-badge"; +import { AffectedMonitors } from "@/components/status-update/affected-monitors"; +import { Events } from "@/components/status-update/events"; +import { StatusBadge } from "@/components/status-update/status-badge"; import { statusDict } from "@/data/incidents-dictionary"; import { api } from "@/trpc/server"; import { ActionButton } from "./_components/action-button"; import { EmptyState } from "./_components/empty-state"; -export default async function IncidentPage({ +export default async function StatusReportsPage({ params, }: { params: { workspaceSlug: string }; }) { - const incidents = await api.incident.getIncidentByWorkspace.query(); + const reports = await api.statusReport.getStatusReportByWorkspace.query(); return (
- Create + Create } /> - {Boolean(incidents?.length) ? ( + {Boolean(reports?.length) ? (
    - {incidents?.map((incident, i) => { + {reports?.map((report, i) => { const { label, icon } = - statusDict[incident.status as keyof typeof statusDict]; - const monitors = incident.monitorsToIncidents.map( + statusDict[report.status as keyof typeof statusDict]; + const monitors = report.monitorsToStatusReports.map( ({ monitor }) => monitor, ); return (
  • - {incident.title} - + {report.title} + } actions={[ , - , + , ]} >
    @@ -82,7 +82,7 @@ export default async function IncidentPage({

    {/* Make it ordered by desc and make it toggable if you want the whole history! */}
    diff --git a/apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/update/edit/loading.tsx b/apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/update/edit/loading.tsx similarity index 100% rename from apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/update/edit/loading.tsx rename to apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/update/edit/loading.tsx diff --git a/apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/update/edit/page.tsx b/apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/update/edit/page.tsx similarity index 58% rename from apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/update/edit/page.tsx rename to apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/update/edit/page.tsx index 0690d78331..aebe1d6511 100644 --- a/apps/web/src/app/app/(dashboard)/[workspaceSlug]/incidents/update/edit/page.tsx +++ b/apps/web/src/app/app/(dashboard)/[workspaceSlug]/status-reports/update/edit/page.tsx @@ -2,15 +2,15 @@ import { notFound } from "next/navigation"; import * as z from "zod"; import { Header } from "@/components/dashboard/header"; -import { IncidentUpdateForm } from "@/components/forms/incident-update-form"; +import { StatusReportUpdateForm } from "@/components/forms/status-report-update-form"; import { api } from "@/trpc/server"; /** * allowed URL search params */ const searchParamsSchema = z.object({ - id: z.coerce.number().optional(), - incidentId: z.coerce.number(), + id: z.coerce.number(), + statusUpdate: z.coerce.number().optional(), }); export default async function EditPage({ @@ -26,24 +26,24 @@ export default async function EditPage({ return notFound(); } - const { id, incidentId } = search.data; + const { id, statusUpdate } = search.data; - const incidentUpdate = id - ? await api.incident.getIncidentUpdateById.query({ - id, + const data = statusUpdate + ? await api.statusReport.getStatusReportUpdateById.query({ + id: statusUpdate, }) : undefined; return (
    -
    diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 42f333e939..37e23872ff 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -19,6 +19,7 @@ export default async function Page() { + {/* TODO: rename to `reports` */} diff --git a/apps/web/src/app/status-page/[domain]/incidents/page.tsx b/apps/web/src/app/status-page/[domain]/incidents/page.tsx index 9620955816..8d48273de8 100644 --- a/apps/web/src/app/status-page/[domain]/incidents/page.tsx +++ b/apps/web/src/app/status-page/[domain]/incidents/page.tsx @@ -28,7 +28,7 @@ export default async function Page({ params }: Props) { description={page.description} className="text-left" /> - +
); } diff --git a/apps/web/src/app/status-page/[domain]/page.tsx b/apps/web/src/app/status-page/[domain]/page.tsx index c636bfb4ad..e96bdb4064 100644 --- a/apps/web/src/app/status-page/[domain]/page.tsx +++ b/apps/web/src/app/status-page/[domain]/page.tsx @@ -36,7 +36,7 @@ export default async function Page({ params }: Props) { } const isEmptyState = !( - Boolean(page.monitors.length) || Boolean(page.incidents.length) + Boolean(page.monitors.length) || Boolean(page.statusReports.length) ); return ( @@ -59,10 +59,14 @@ export default async function Page({ params }: Props) { /> ) : ( <> - + + {/* TODO: rename to StatusReportList */} diff --git a/apps/web/src/components/forms/incident-form.tsx b/apps/web/src/components/forms/status-report-form.tsx similarity index 93% rename from apps/web/src/components/forms/incident-form.tsx rename to apps/web/src/components/forms/status-report-form.tsx index 48f84a75b3..04644dff49 100644 --- a/apps/web/src/components/forms/incident-form.tsx +++ b/apps/web/src/components/forms/status-report-form.tsx @@ -5,11 +5,15 @@ import { useRouter } from "next/navigation"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; -import type { InsertIncident, Monitor, Page } from "@openstatus/db/src/schema"; import { - incidentStatus, - incidentStatusSchema, - insertIncidentSchema, + insertStatusReportSchema, + statusReportStatus, + statusReportStatusSchema, +} from "@openstatus/db/src/schema"; +import type { + InsertStatusReport, + Monitor, + Page, } from "@openstatus/db/src/schema"; import { Accordion, @@ -45,14 +49,14 @@ import { cn } from "@/lib/utils"; import { api } from "@/trpc/client"; interface Props { - defaultValues?: InsertIncident; + defaultValues?: InsertStatusReport; monitors?: Monitor[]; pages?: Page[]; } -export function IncidentForm({ defaultValues, monitors, pages }: Props) { - const form = useForm({ - resolver: zodResolver(insertIncidentSchema), +export function StatusReportForm({ defaultValues, monitors, pages }: Props) { + const form = useForm({ + resolver: zodResolver(insertStatusReportSchema), defaultValues: defaultValues ? { id: defaultValues.id, @@ -73,26 +77,27 @@ export function IncidentForm({ defaultValues, monitors, pages }: Props) { const [isPending, startTransition] = React.useTransition(); const { toast } = useToastAction(); - const onSubmit = ({ ...props }: InsertIncident) => { + const onSubmit = ({ ...props }: InsertStatusReport) => { startTransition(async () => { try { if (defaultValues) { - await api.incident.updateIncident.mutate({ ...props }); + await api.statusReport.updateStatusReport.mutate({ ...props }); } else { - // or use createIncident to create automaticaaly an IncidentUpdate? const { message, date, status, ...rest } = props; - const incident = await api.incident.createIncident.mutate({ - status, - message, - ...rest, - }); + const statusReport = await api.statusReport.createStatusReport.mutate( + { + status, + message, + ...rest, + }, + ); // include update on creation - if (incident?.id) { - await api.incident.createIncidentUpdate.mutate({ + if (statusReport?.id) { + await api.statusReport.createStatusReportUpdate.mutate({ message, date, status, - incidentId: incident.id, + statusReportId: statusReport.id, }); } } @@ -104,7 +109,6 @@ export function IncidentForm({ defaultValues, monitors, pages }: Props) { }); }; - console.log(form.formState.errors); return (
- field.onChange(incidentStatusSchema.parse(value)) + field.onChange(statusReportStatusSchema.parse(value)) } // value is a string defaultValue={field.value} className="grid grid-cols-2 gap-4 sm:grid-cols-4" > - {incidentStatus.map((status) => { + {statusReportStatus.map((status) => { const { value, label, icon } = statusDict[status]; const Icon = Icons[icon]; return ( @@ -317,7 +321,7 @@ export function IncidentForm({ defaultValues, monitors, pages }: Props) {

- Incident Update + Status Update

What is actually going wrong? diff --git a/apps/web/src/components/forms/incident-update-form.tsx b/apps/web/src/components/forms/status-report-update-form.tsx similarity index 88% rename from apps/web/src/components/forms/incident-update-form.tsx rename to apps/web/src/components/forms/status-report-update-form.tsx index d9d8432718..f8a8dd280c 100644 --- a/apps/web/src/components/forms/incident-update-form.tsx +++ b/apps/web/src/components/forms/status-report-update-form.tsx @@ -5,11 +5,11 @@ import { useRouter } from "next/navigation"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; -import type { InsertIncidentUpdate } from "@openstatus/db/src/schema"; +import type { InsertStatusReportUpdate } from "@openstatus/db/src/schema"; import { - incidentStatus, - incidentStatusSchema, - insertIncidentUpdateSchema, + insertStatusReportUpdateSchema, + statusReportStatus, + statusReportStatusSchema, } from "@openstatus/db/src/schema"; import { Button, @@ -38,32 +38,35 @@ import { useToastAction } from "@/hooks/use-toast-action"; import { api } from "@/trpc/client"; interface Props { - defaultValues?: InsertIncidentUpdate; - incidentId: number; + defaultValues?: InsertStatusReportUpdate; + statusReportId: number; } -export function IncidentUpdateForm({ defaultValues, incidentId }: Props) { - const form = useForm({ - resolver: zodResolver(insertIncidentUpdateSchema), +export function StatusReportUpdateForm({ + defaultValues, + statusReportId, +}: Props) { + const form = useForm({ + resolver: zodResolver(insertStatusReportUpdateSchema), defaultValues: { id: defaultValues?.id || 0, status: defaultValues?.status || "investigating", message: defaultValues?.message, date: defaultValues?.date || new Date(), - incidentId, + statusReportId, }, }); const router = useRouter(); const [isPending, startTransition] = React.useTransition(); const { toast } = useToastAction(); - const onSubmit = ({ ...props }: InsertIncidentUpdate) => { + const onSubmit = ({ ...props }: InsertStatusReportUpdate) => { startTransition(async () => { try { if (defaultValues) { - await api.incident.updateIncidentUpdate.mutate({ ...props }); + await api.statusReport.updateStatusReportUpdate.mutate({ ...props }); } else { - await api.incident.createIncidentUpdate.mutate({ ...props }); + await api.statusReport.createStatusReportUpdate.mutate({ ...props }); } toast("saved"); router.refresh(); @@ -100,12 +103,12 @@ export function IncidentUpdateForm({ defaultValues, incidentId }: Props) { - field.onChange(incidentStatusSchema.parse(value)) + field.onChange(statusReportStatusSchema.parse(value)) } // value is a string defaultValue={field.value} className="grid grid-cols-2 gap-4 sm:grid-cols-4" > - {incidentStatus.map((status) => { + {statusReportStatus.map((status) => { const { value, label, icon } = statusDict[status]; const Icon = Icons[icon]; return ( diff --git a/apps/web/src/components/icons.tsx b/apps/web/src/components/icons.tsx index d7af584a49..2a805f173e 100644 --- a/apps/web/src/components/icons.tsx +++ b/apps/web/src/components/icons.tsx @@ -13,6 +13,7 @@ import { Laptop, LayoutDashboard, Link, + Megaphone, MessageCircle, Minus, Moon, @@ -66,6 +67,7 @@ export const Icons = { bell: Bell, zap: Zap, "alert-triangle": AlertTriangle, + megaphone: Megaphone, minus: Minus, sun: SunMedium, moon: Moon, diff --git a/apps/web/src/components/status-page/incident-list.tsx b/apps/web/src/components/status-page/incident-list.tsx index 0dd0db4247..7e15c288cd 100644 --- a/apps/web/src/components/status-page/incident-list.tsx +++ b/apps/web/src/components/status-page/incident-list.tsx @@ -1,14 +1,14 @@ import type { z } from "zod"; import type { - selectIncidentsPageSchema, selectPublicMonitorSchema, + selectStatusReportPageSchema, } from "@openstatus/db/src/schema"; import { notEmpty } from "@/lib/utils"; -import { AffectedMonitors } from "../incidents/affected-monitors"; -import { Events } from "../incidents/events"; -import { StatusBadge } from "../incidents/status-badge"; +import { AffectedMonitors } from "../status-update/affected-monitors"; +import { Events } from "../status-update/events"; +import { StatusBadge } from "../status-update/status-badge"; // TODO: change layout - it is too packed with data rn @@ -17,7 +17,7 @@ export const IncidentList = ({ monitors, context = "all", }: { - incidents: z.infer; + incidents: z.infer; monitors: z.infer[]; context?: "all" | "latest"; // latest 7 days }) => { @@ -25,7 +25,7 @@ export const IncidentList = ({ function getLastWeeksIncidents() { return incidents.filter((incident) => { - return incident.incidentUpdates.some( + return incident.statusReportUpdates.some( (update) => update.date.getTime() > lastWeek, ); }); @@ -45,7 +45,7 @@ export const IncidentList = ({ {context === "all" ? "All incidents" : "Latest incidents"} {_incidents.map((incident) => { - const affectedMonitors = incident.monitorsToIncidents + const affectedMonitors = incident.monitorsToStatusReport .map(({ monitorId }) => { const monitor = monitors.find(({ id }) => monitorId === id); return monitor || undefined; @@ -63,7 +63,7 @@ export const IncidentList = ({ Affected Monitors

{ const monitor = monitors.find( ({ id }) => monitorId === id, @@ -78,7 +78,7 @@ export const IncidentList = ({

Latest Updates

- +
); diff --git a/apps/web/src/components/status-page/status-check.tsx b/apps/web/src/components/status-page/status-check.tsx index 050a37ccc2..3807d384e6 100644 --- a/apps/web/src/components/status-page/status-check.tsx +++ b/apps/web/src/components/status-page/status-check.tsx @@ -2,8 +2,8 @@ import { cva } from "class-variance-authority"; import type { z } from "zod"; import type { - selectIncidentsPageSchema, selectPublicMonitorSchema, + selectStatusReportPageSchema, } from "@openstatus/db/src/schema"; import { getResponseListData } from "@/lib/tb"; @@ -28,13 +28,13 @@ const check = cva("border-border rounded-full border p-2", { }); export async function StatusCheck({ - incidents, + statusReports, monitors, }: { - incidents: z.infer; + statusReports: z.infer; monitors: z.infer[]; }) { - const isIncident = incidents.some( + const isIncident = statusReports.some( (incident) => !["monitoring", "resolved"].includes(incident.status), ); diff --git a/apps/web/src/components/incidents/affected-monitors.tsx b/apps/web/src/components/status-update/affected-monitors.tsx similarity index 100% rename from apps/web/src/components/incidents/affected-monitors.tsx rename to apps/web/src/components/status-update/affected-monitors.tsx diff --git a/apps/web/src/components/incidents/events.tsx b/apps/web/src/components/status-update/events.tsx similarity index 85% rename from apps/web/src/components/incidents/events.tsx rename to apps/web/src/components/status-update/events.tsx index fde4a2ac8e..44aa8339be 100644 --- a/apps/web/src/components/incidents/events.tsx +++ b/apps/web/src/components/status-update/events.tsx @@ -5,7 +5,7 @@ import { useRouter } from "next/navigation"; import { format, formatDistance } from "date-fns"; import type * as z from "zod"; -import type { selectIncidentUpdateSchema } from "@openstatus/db/src/schema"; +import type { selectStatusReportUpdateSchema } from "@openstatus/db/src/schema"; import { Button, Tooltip, @@ -14,26 +14,26 @@ import { TooltipTrigger, } from "@openstatus/ui"; +import { DeleteStatusReportUpdateButtonIcon } from "@/app/app/(dashboard)/[workspaceSlug]/status-reports/_components/delete-status-update"; import { Icons } from "@/components/icons"; import { statusDict } from "@/data/incidents-dictionary"; import { useProcessor } from "@/hooks/use-preprocessor"; import { cn } from "@/lib/utils"; -import { DeleteIncidentUpdateButtonIcon } from "../../app/app/(dashboard)/[workspaceSlug]/incidents/_components/delete-incident-update"; -type IncidentUpdateProps = z.infer; +type StatusReportUpdateProps = z.infer; export function Events({ - incidentUpdates, + statusReportUpdates, editable = false, }: { - incidentUpdates: IncidentUpdateProps[]; + statusReportUpdates: StatusReportUpdateProps[]; editable?: boolean; }) { const [open, toggle] = React.useReducer((open) => !open, false); const router = useRouter(); // TODO: make it simpler.. - const sortedArray = incidentUpdates.sort((a, b) => { + const sortedArray = statusReportUpdates.sort((a, b) => { const orderA = statusDict[a.status].order; const orderB = statusDict[b.status].order; return orderB - orderA; @@ -75,13 +75,13 @@ export function Events({ className="h-7 w-7 p-0" onClick={() => { router.push( - `./incidents/update/edit?incidentId=${update.incidentId}&id=${update.id}`, + `./status-reports/update/edit?id=${update.statusReportId}&statusUpdate=${update.id}`, ); }} > - +
) : undefined}
@@ -106,7 +106,7 @@ export function Events({ ); })} - {incidentUpdates.length > 1 ? ( + {statusReportUpdates.length > 1 ? (