From de559508dcad26f4f803485a19d136a2e18282b7 Mon Sep 17 00:00:00 2001 From: Maximilian Kaske <56969857+mxkaske@users.noreply.github.com> Date: Thu, 2 Jan 2025 20:09:23 +0100 Subject: [PATCH] feat: add quicklinks for status reports (#1148) --- .../_components/status-report-button.tsx | 53 +++++++++++++++++++ .../status-pages/(overview)/page.tsx | 3 ++ .../notification/data-table-row-actions.tsx | 2 + .../data-table/status-page/columns.tsx | 41 +++++++++++++- .../status-page/data-table-row-actions.tsx | 6 +++ packages/api/src/router/page.ts | 9 ++++ packages/db/src/schema/pages/page.ts | 2 + packages/db/src/schema/shared.ts | 4 ++ .../src/schema/status_reports/validation.ts | 6 ++- packages/emails/src/client.tsx | 2 +- 10 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 apps/web/src/app/app/[workspaceSlug]/(dashboard)/status-pages/(overview)/_components/status-report-button.tsx diff --git a/apps/web/src/app/app/[workspaceSlug]/(dashboard)/status-pages/(overview)/_components/status-report-button.tsx b/apps/web/src/app/app/[workspaceSlug]/(dashboard)/status-pages/(overview)/_components/status-report-button.tsx new file mode 100644 index 000000000..e1a6f0193 --- /dev/null +++ b/apps/web/src/app/app/[workspaceSlug]/(dashboard)/status-pages/(overview)/_components/status-report-button.tsx @@ -0,0 +1,53 @@ +import { cn } from "@/lib/utils"; +import type { Page } from "@openstatus/db/src/schema"; +import { + Alert, + AlertDescription, + AlertTitle, +} from "@openstatus/ui/src/components/alert"; +import { buttonVariants } from "@openstatus/ui/src/components/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@openstatus/ui/src/components/dropdown-menu"; +import { ChevronDown, Megaphone } from "lucide-react"; +import Link from "next/link"; + +export function StatusReportButton({ pages }: { pages: Page[] }) { + return ( + + Status Reports + + Start a new report. If you want to update your users about a current + report, please hover the Last Report column and click on{" "} + Go to report. + + + + + New Status Report + + + + + Select Page + + {pages.map((page) => ( + + {page.title} + + ))} + + + + ); +} diff --git a/apps/web/src/app/app/[workspaceSlug]/(dashboard)/status-pages/(overview)/page.tsx b/apps/web/src/app/app/[workspaceSlug]/(dashboard)/status-pages/(overview)/page.tsx index 8e7600928..b6101a8ba 100644 --- a/apps/web/src/app/app/[workspaceSlug]/(dashboard)/status-pages/(overview)/page.tsx +++ b/apps/web/src/app/app/[workspaceSlug]/(dashboard)/status-pages/(overview)/page.tsx @@ -8,6 +8,7 @@ import { Limit } from "@/components/dashboard/limit"; import { columns } from "@/components/data-table/status-page/columns"; import { DataTable } from "@/components/data-table/status-page/data-table"; import { api } from "@/trpc/server"; +import { StatusReportButton } from "./_components/status-report-button"; export default async function MonitorPage() { const pages = await api.page.getPagesByWorkspace.query(); @@ -27,10 +28,12 @@ export default async function MonitorPage() { ); } const isLimitReached = await api.page.isPageLimitReached.query(); + return ( <> {isLimitReached ? : null} + ); } diff --git a/apps/web/src/components/data-table/notification/data-table-row-actions.tsx b/apps/web/src/components/data-table/notification/data-table-row-actions.tsx index 894e33045..c0547f221 100644 --- a/apps/web/src/components/data-table/notification/data-table-row-actions.tsx +++ b/apps/web/src/components/data-table/notification/data-table-row-actions.tsx @@ -21,6 +21,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, + DropdownMenuSeparator, DropdownMenuTrigger, } from "@openstatus/ui"; @@ -72,6 +73,7 @@ export function DataTableRowActions({ Edit + Delete diff --git a/apps/web/src/components/data-table/status-page/columns.tsx b/apps/web/src/components/data-table/status-page/columns.tsx index 62cd03083..d95b10c68 100644 --- a/apps/web/src/components/data-table/status-page/columns.tsx +++ b/apps/web/src/components/data-table/status-page/columns.tsx @@ -5,7 +5,12 @@ import Image from "next/image"; import Link from "next/link"; import * as z from "zod"; -import type { Maintenance, Page } from "@openstatus/db/src/schema"; +import type { + Maintenance, + Page, + StatusReport, + StatusReportUpdate, +} from "@openstatus/db/src/schema"; import { Tooltip, TooltipContent, @@ -13,6 +18,7 @@ import { TooltipTrigger, } from "@openstatus/ui"; +import { formatDate } from "@/lib/utils"; import { ArrowUpRight, Check } from "lucide-react"; import { DataTableBadges } from "../data-table-badges"; import { DataTableRowActions } from "./data-table-row-actions"; @@ -21,6 +27,9 @@ export const columns: ColumnDef< Page & { monitorsToPages: { monitor: { name: string } }[]; maintenancesToPages: Maintenance[]; // we get only the active maintenances! + statusReports: (StatusReport & { + statusReportUpdates: StatusReportUpdate[]; + })[]; } >[] = [ { @@ -85,6 +94,36 @@ export const columns: ColumnDef< ); }, }, + { + accessorKey: "statusReports", + header: "Last Report", + cell: ({ row }) => { + const lastReport = row.original.statusReports?.[0]; + + if (!lastReport) { + return -; + } + + const date = + lastReport.statusReportUpdates?.[0].date || lastReport.updatedAt; + + return ( +
+ + {formatDate(date)} + +
+ + Go to report + +
+
+ ); + }, + }, { accessorKey: "icon", header: "Favicon", diff --git a/apps/web/src/components/data-table/status-page/data-table-row-actions.tsx b/apps/web/src/components/data-table/status-page/data-table-row-actions.tsx index 8706eb9ac..bec49a950 100644 --- a/apps/web/src/components/data-table/status-page/data-table-row-actions.tsx +++ b/apps/web/src/components/data-table/status-page/data-table-row-actions.tsx @@ -21,6 +21,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, + DropdownMenuSeparator, DropdownMenuTrigger, } from "@openstatus/ui"; @@ -80,6 +81,11 @@ export function DataTableRowActions({ > Visit + + + Create Report + + Delete diff --git a/packages/api/src/router/page.ts b/packages/api/src/router/page.ts index 6f792293f..a449e45d0 100644 --- a/packages/api/src/router/page.ts +++ b/packages/api/src/router/page.ts @@ -224,8 +224,17 @@ export const pageRouter = createTRPCRouter({ gte(maintenance.to, new Date()), ), }, + statusReports: { + orderBy: (reports, { desc }) => desc(reports.updatedAt), + with: { + statusReportUpdates: { + orderBy: (updates, { desc }) => desc(updates.date), + }, + }, + }, }, }); + console.log(allPages.map((page) => page.statusReports)); return z.array(selectPageSchemaWithMonitorsRelation).parse(allPages); }), diff --git a/packages/db/src/schema/pages/page.ts b/packages/db/src/schema/pages/page.ts index 402d1b962..c0e27c5bd 100644 --- a/packages/db/src/schema/pages/page.ts +++ b/packages/db/src/schema/pages/page.ts @@ -3,6 +3,7 @@ import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"; import { maintenance } from "../maintenances"; import { monitorsToPages } from "../monitors"; +import { statusReport } from "../status_reports"; import { workspace } from "../workspaces"; export const page = sqliteTable("page", { @@ -43,6 +44,7 @@ export const page = sqliteTable("page", { export const pageRelations = relations(page, ({ many, one }) => ({ monitorsToPages: many(monitorsToPages), maintenancesToPages: many(maintenance), + statusReports: many(statusReport), workspace: one(workspace, { fields: [page.workspaceId], references: [workspace.id], diff --git a/packages/db/src/schema/shared.ts b/packages/db/src/schema/shared.ts index 67b17df58..a930ced0c 100644 --- a/packages/db/src/schema/shared.ts +++ b/packages/db/src/schema/shared.ts @@ -59,6 +59,10 @@ export const selectPageSchemaWithMonitorsRelation = selectPageSchema.extend({ }), ), maintenancesToPages: selectMaintenanceSchema.array().default([]), + statusReports: selectStatusReportSchema + .extend({ statusReportUpdates: selectStatusReportUpdateSchema.array() }) + .array() + .default([]), }); export const selectPublicPageSchemaWithRelation = selectPageSchema diff --git a/packages/db/src/schema/status_reports/validation.ts b/packages/db/src/schema/status_reports/validation.ts index 2f8c35680..048bcc5fb 100644 --- a/packages/db/src/schema/status_reports/validation.ts +++ b/packages/db/src/schema/status_reports/validation.ts @@ -14,13 +14,15 @@ export const insertStatusReportUpdateSchema = createInsertSchema( { status: statusReportStatusSchema, }, -); +).extend({ + date: z.coerce.date().optional().default(new Date()), +}); export const insertStatusReportSchema = createInsertSchema(statusReport, { status: statusReportStatusSchema, }) .extend({ - date: z.date().optional().default(new Date()), + date: z.coerce.date().optional().default(new Date()), /** * relationship to monitors and pages */ diff --git a/packages/emails/src/client.tsx b/packages/emails/src/client.tsx index a1050e5c1..efd2fbdfb 100644 --- a/packages/emails/src/client.tsx +++ b/packages/emails/src/client.tsx @@ -14,8 +14,8 @@ export class EmailClient { public async sendFollowUp(req: { to: string }) { if (process.env.NODE_ENV === "development") return; - const html = await render(); try { + const html = await render(); const result = await this.client.emails.send({ from: "Thibault Le Ouay Ducasse ", subject: "How's it going with OpenStatus?", -- 2.54.0