Openstatus
www.openstatus.dev
1"use client";
2
3import { TableCellBadge } from "@/components/data-table/table-cell-badge";
4import { TableCellDate } from "@/components/data-table/table-cell-date";
5import { TableCellNumber } from "@/components/data-table/table-cell-number";
6import { Button } from "@/components/ui/button";
7import { DataTableColumnHeader } from "@/components/ui/data-table/data-table-column-header";
8import { colors } from "@/data/status-report-updates.client";
9import { cn } from "@/lib/utils";
10import type { RouterOutputs } from "@openstatus/api";
11import type { ColumnDef } from "@tanstack/react-table";
12import { ChevronDown, ChevronUp } from "lucide-react";
13import Link from "next/link";
14import { DataTableRowActions } from "./data-table-row-actions";
15
16type StatusReport = RouterOutputs["statusReport"]["list"][number];
17
18export const columns: ColumnDef<StatusReport>[] = [
19 {
20 id: "expander",
21 header: () => null,
22 cell: ({ row }) => {
23 return row.getCanExpand() ? (
24 <Button
25 {...{
26 className: "size-7 shadow-none text-muted-foreground",
27 onClick: (e) => {
28 e.stopPropagation();
29 row.toggleExpanded();
30 },
31 "aria-expanded": row.getIsExpanded(),
32 "aria-label": row.getIsExpanded()
33 ? `Collapse details for ${row.original.title}`
34 : `Expand details for ${row.original.title}`,
35 size: "icon",
36 variant: "ghost",
37 }}
38 >
39 {row.getIsExpanded() ? (
40 <ChevronUp className="opacity-60" size={16} aria-hidden="true" />
41 ) : (
42 <ChevronDown className="opacity-60" size={16} aria-hidden="true" />
43 )}
44 </Button>
45 ) : undefined;
46 },
47 meta: {
48 headerClassName: "w-7",
49 },
50 },
51 {
52 accessorKey: "title",
53 header: "Title",
54 enableSorting: false,
55 enableHiding: false,
56 meta: {
57 cellClassName: "max-w-[200px] truncate",
58 },
59 },
60 {
61 accessorKey: "status",
62 header: "Current Status",
63 cell: ({ row }) => {
64 const value = String(row.getValue("status"));
65 return (
66 <div
67 className={cn(
68 "font-mono capitalize",
69 colors[value as keyof typeof colors],
70 )}
71 >
72 {value}
73 </div>
74 );
75 },
76 enableSorting: false,
77 enableHiding: false,
78 },
79 {
80 id: "updates",
81 accessorFn: (row) => row.updates.length,
82 header: "Total Updates",
83 cell: ({ row }) => {
84 const value = row.getValue("updates");
85 return <TableCellNumber value={value} />;
86 },
87 },
88 {
89 id: "pageComponents",
90 accessorFn: (row) => row?.pageComponents,
91 header: "Affected",
92 cell: ({ row }) => {
93 const value = row.getValue("pageComponents");
94 if (Array.isArray(value) && value.length > 0 && "name" in value[0]) {
95 return (
96 <div className="flex flex-wrap gap-1">
97 {value.map((m) =>
98 m.monitorId ? (
99 <Link href={`/monitors/${m.monitorId}/overview`} key={m.id}>
100 <TableCellBadge value={m.name} />
101 </Link>
102 ) : (
103 <TableCellBadge value={m.name} key={m.id} />
104 ),
105 )}
106 </div>
107 );
108 }
109 return <div className="text-muted-foreground">-</div>;
110 },
111 },
112 {
113 id: "startedAt",
114 accessorFn: (row) =>
115 row.updates.sort((a, b) => a.date.getTime() - b.date.getTime())[0]?.date,
116 header: ({ column }) => (
117 <DataTableColumnHeader column={column} title="Started At" />
118 ),
119 cell: ({ row }) => <TableCellDate value={row.getValue("startedAt")} />,
120 enableHiding: false,
121 meta: {
122 cellClassName: "w-[170px]",
123 },
124 },
125 {
126 id: "actions",
127 cell: ({ row }) => <DataTableRowActions row={row} />,
128 meta: {
129 cellClassName: "w-8",
130 },
131 },
132];