Openstatus www.openstatus.dev

Revert "feat: nerd mode (#1375)" (#1378)

This reverts commit fdacaad8e155f396b18065ab8b6d81ae676dc437.

authored by

Maximilian Kaske and committed by
GitHub
ed3a8152 fdacaad8

+79 -266
-1
apps/dashboard/next-env.d.ts
··· 1 1 /// <reference types="next" /> 2 2 /// <reference types="next/image-types/global" /> 3 - /// <reference path="./.next/types/routes.d.ts" /> 4 3 5 4 // NOTE: This file should not be edited 6 5 // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
-1
apps/status-page/next-env.d.ts
··· 1 1 /// <reference types="next" /> 2 2 /// <reference types="next/image-types/global" /> 3 - /// <reference path="./.next/types/routes.d.ts" /> 4 3 5 4 // NOTE: This file should not be edited 6 5 // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
apps/status-page/public/fonts/CommitMono-400-Italic.otf

This is a binary file and will not be displayed.

apps/status-page/public/fonts/CommitMono-400-Regular.otf

This is a binary file and will not be displayed.

apps/status-page/public/fonts/CommitMono-700-Italic.otf

This is a binary file and will not be displayed.

apps/status-page/public/fonts/CommitMono-700-Regular.otf

This is a binary file and will not be displayed.

+2 -6
apps/status-page/src/app/(status-page)/[domain]/(public)/events/(list)/page.tsx
··· 21 21 import { Badge } from "@/components/ui/badge"; 22 22 import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; 23 23 import { formatDate } from "@/lib/formatter"; 24 - import { Check } from "lucide-react"; 24 + import { CircleCheck } from "lucide-react"; 25 25 import Link from "next/link"; 26 26 27 27 // TODO: include ?filter=maintenance/reports ··· 71 71 <StatusEventTitle className="inline-flex gap-1"> 72 72 {report.title} 73 73 {isReportResolvedOnly ? ( 74 - <div className="mt-1 ml-1.5"> 75 - <div className="rounded-full border border-success/20 bg-success/10 p-0.5 text-success"> 76 - <Check className="size-3 shrink-0" /> 77 - </div> 78 - </div> 74 + <CircleCheck className="size-4 text-success shrink-0 mt-1 ml-1.5" /> 79 75 ) : null} 80 76 </StatusEventTitle> 81 77 {report.monitorsToStatusReports.length > 0 ? (
+51 -71
apps/status-page/src/app/(status-page)/[domain]/(public)/page.tsx
··· 12 12 StatusBanner, 13 13 StatusBannerContainer, 14 14 StatusBannerContent, 15 - StatusBannerTabs, 16 - StatusBannerTabsContent, 17 - StatusBannerTabsList, 18 - StatusBannerTabsTrigger, 15 + StatusBannerTitle, 19 16 } from "@/components/status-page/status-banner"; 20 17 import { 21 18 StatusEventTimelineMaintenance, ··· 25 22 import { StatusMonitor } from "@/components/status-page/status-monitor"; 26 23 import { Separator } from "@/components/ui/separator"; 27 24 import { useTRPC } from "@/lib/trpc/client"; 28 - import { cn } from "@/lib/utils"; 29 25 import { useQuery } from "@tanstack/react-query"; 26 + import Link from "next/link"; 30 27 import { useParams } from "next/navigation"; 31 28 32 29 export default function Page() { ··· 58 55 </StatusHeader> 59 56 {page.openEvents.length > 0 ? ( 60 57 <StatusContent> 61 - <StatusBannerTabs 62 - defaultValue={`${page.openEvents[0].type}-${page.openEvents[0].id}`} 63 - > 64 - <StatusBannerTabsList> 65 - {page.openEvents.map((e, i) => { 66 - return ( 67 - <StatusBannerTabsTrigger 68 - value={`${e.type}-${e.id}`} 69 - status={e.status} 70 - key={e.id} 71 - className={cn( 72 - i === 0 && "rounded-tl-lg", 73 - i === page.openEvents.length - 1 && "rounded-tr-lg", 74 - )} 75 - > 76 - {e.name} 77 - </StatusBannerTabsTrigger> 78 - ); 79 - })} 80 - </StatusBannerTabsList> 81 - {page.openEvents.map((e) => { 82 - if (e.type === "report") { 83 - const report = page.statusReports.find( 84 - (report) => report.id === e.id, 85 - ); 86 - if (!report) return null; 87 - return ( 88 - <StatusBannerTabsContent 89 - value={`${e.type}-${e.id}`} 90 - key={e.id} 91 - > 92 - <StatusBannerContainer status={e.status}> 93 - <StatusBannerContent> 94 - <StatusEventTimelineReport 95 - updates={report.statusReportUpdates} 96 - withDot={false} 97 - /> 98 - </StatusBannerContent> 99 - </StatusBannerContainer> 100 - </StatusBannerTabsContent> 101 - ); 102 - } 103 - if (e.type === "maintenance") { 104 - const maintenance = page.maintenances.find( 105 - (maintenance) => maintenance.id === e.id, 106 - ); 107 - if (!maintenance) return null; 108 - return ( 109 - <StatusBannerTabsContent 110 - value={`${e.type}-${e.id}`} 111 - key={e.id} 112 - > 113 - <StatusBannerContainer status={e.status}> 114 - <StatusBannerContent> 115 - <StatusEventTimelineMaintenance 116 - maintenance={maintenance} 117 - withDot={false} 118 - /> 119 - </StatusBannerContent> 120 - </StatusBannerContainer> 121 - </StatusBannerTabsContent> 122 - ); 123 - } 124 - return null; 125 - })} 126 - </StatusBannerTabs> 58 + {page.openEvents.map((e) => { 59 + if (e.type === "maintenance") { 60 + const maintenance = page.maintenances.find( 61 + (maintenance) => maintenance.id === e.id, 62 + ); 63 + if (!maintenance) return null; 64 + return ( 65 + <Link 66 + href={`./events/maintenance/${e.id}`} 67 + key={e.id} 68 + className="rounded-lg" 69 + > 70 + <StatusBannerContainer status={e.status}> 71 + <StatusBannerTitle>{e.name}</StatusBannerTitle> 72 + <StatusBannerContent> 73 + <StatusEventTimelineMaintenance 74 + maintenance={maintenance} 75 + withDot={false} 76 + /> 77 + </StatusBannerContent> 78 + </StatusBannerContainer> 79 + </Link> 80 + ); 81 + } 82 + if (e.type === "report") { 83 + const report = page.statusReports.find( 84 + (report) => report.id === e.id, 85 + ); 86 + if (!report) return null; 87 + return ( 88 + <Link 89 + href={`./events/report/${e.id}`} 90 + key={e.id} 91 + className="rounded-lg" 92 + > 93 + <StatusBannerContainer status={e.status}> 94 + <StatusBannerTitle>{e.name}</StatusBannerTitle> 95 + <StatusBannerContent> 96 + <StatusEventTimelineReport 97 + updates={report.statusReportUpdates} 98 + withDot={false} 99 + /> 100 + </StatusBannerContent> 101 + </StatusBannerContainer> 102 + </Link> 103 + ); 104 + } 105 + return null; 106 + })} 127 107 </StatusContent> 128 108 ) : ( 129 109 <StatusBanner status={page.status} />
+1 -1
apps/status-page/src/app/(status-page)/[domain]/layout.tsx
··· 19 19 20 20 const DISPLAY_FLOATING_BUTTON = 21 21 process.env.NODE_ENV === "development" || 22 - process.env.NEXT_PUBLIC_ENABLE_FLOATING_BUTTON === "true"; 22 + process.env.ENABLE_FLOATING_BUTTON === "true"; 23 23 24 24 export default async function Layout({ 25 25 children,
+2 -10
apps/status-page/src/app/globals.css
··· 14 14 --color-background: var(--background); 15 15 --color-foreground: var(--foreground); 16 16 --font-sans: var(--font-geist-sans); 17 - --font-mono: var(--font-commit-mono, var(--font-geist-mono)); 17 + --font-mono: var(--font-geist-mono); 18 18 --color-sidebar-ring: var(--sidebar-ring); 19 19 --color-sidebar-border: var(--sidebar-border); 20 20 --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); ··· 55 55 } 56 56 57 57 :root { 58 - /* --radius: 0.625rem; */ 59 - --radius: 0px; 58 + --radius: 0.625rem; 60 59 --background: oklch(1 0 0); 61 60 --foreground: oklch(0.145 0 0); 62 61 --card: oklch(1 0 0); ··· 176 175 @apply bg-background text-foreground; 177 176 } 178 177 } 179 - 180 - @layer utilities { 181 - /* NOTE: allows us to --radius: 0px and avoid rounding issues - otherwise it is 'infinite * 1px' */ 182 - .rounded-full { 183 - border-radius: calc(var(--radius) * 99999999); 184 - } 185 - }
-27
apps/status-page/src/app/layout.tsx
··· 24 24 subsets: ["latin"], 25 25 }); 26 26 27 - const commitMono = LocalFont({ 28 - src: [ 29 - { 30 - path: "../../public/fonts/CommitMono-400-Regular.otf", 31 - weight: "400", 32 - style: "normal", 33 - }, 34 - { 35 - path: "../../public/fonts/CommitMono-400-Italic.otf", 36 - weight: "400", 37 - style: "italic", 38 - }, 39 - { 40 - path: "../../public/fonts/CommitMono-700-Regular.otf", 41 - weight: "700", 42 - style: "normal", 43 - }, 44 - { 45 - path: "../../public/fonts/CommitMono-700-Italic.otf", 46 - weight: "700", 47 - style: "italic", 48 - }, 49 - ], 50 - variable: "--font-commit-mono", 51 - }); 52 - 53 27 export const metadata: Metadata = { 54 28 ...defaultMetadata, 55 29 twitter: { ··· 74 48 geistSans.variable, 75 49 geistMono.variable, 76 50 cal.variable, 77 - commitMono.variable, 78 51 "antialiased", 79 52 )} 80 53 >
+3 -3
apps/status-page/src/components/nav/footer.tsx
··· 20 20 return ( 21 21 <footer {...props}> 22 22 <div className="mx-auto flex max-w-2xl items-center justify-between gap-4 px-3 py-2"> 23 - <div> 24 - <p className="font-mono text-muted-foreground text-sm leading-none"> 25 - powered by <Link href="#">openstatus</Link> 23 + <div className="leading-[0.9]"> 24 + <p className="text-muted-foreground text-sm"> 25 + Powered by <Link href="#">OpenStatus</Link> 26 26 </p> 27 27 <TimestampHoverCard date={new Date(dataUpdatedAt)} side="top"> 28 28 <span className="text-muted-foreground/70 text-xs">
+1 -1
apps/status-page/src/components/status-page/floating-button.tsx
··· 154 154 <Button 155 155 size="icon" 156 156 variant="outline" 157 - className="size-12 rounded-full dark:bg-background" 157 + className="size-12 rounded-full" 158 158 > 159 159 <Settings className="size-5" /> 160 160 <span className="sr-only">Open status page settings</span>
+5 -99
apps/status-page/src/components/status-page/status-banner.tsx
··· 1 - import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; 2 1 import { cn } from "@/lib/utils"; 3 2 import { 4 3 AlertCircleIcon, ··· 49 48 data-status={status} 50 49 className={cn( 51 50 "group/status-banner overflow-hidden rounded-lg border", 52 - "data-[status=success]:border-success data-[status=success]:bg-success/5 dark:data-[status=success]:bg-success/10", 53 - "data-[status=degraded]:border-warning data-[status=degraded]:bg-warning/5 dark:data-[status=degraded]:bg-warning/10", 54 - "data-[status=error]:border-destructive data-[status=error]:bg-destructive/5 dark:data-[status=error]:bg-destructive/10", 55 - "data-[status=info]:border-info data-[status=info]:bg-info/5 dark:data-[status=info]:bg-info/10", 51 + "data-[status=success]:border-success", 52 + "data-[status=degraded]:border-warning", 53 + "data-[status=error]:border-destructive", 54 + "data-[status=info]:border-info", 56 55 className, 57 56 )} 58 57 > ··· 91 90 return ( 92 91 <div 93 92 className={cn( 94 - "px-3 py-2 font-medium text-background", 93 + "px-3 py-2 font-medium text-background sm:px-4 sm:py-3", 95 94 "group-data-[status=success]/status-banner:bg-success", 96 95 "group-data-[status=degraded]/status-banner:bg-warning", 97 96 "group-data-[status=error]/status-banner:bg-destructive", ··· 140 139 </div> 141 140 ); 142 141 } 143 - 144 - // Tabs Components 145 - 146 - export function StatusBannerTabs({ 147 - className, 148 - children, 149 - status, 150 - ...props 151 - }: React.ComponentProps<typeof Tabs> & { 152 - status?: "success" | "degraded" | "error" | "info"; 153 - }) { 154 - return ( 155 - <Tabs 156 - data-slot="status-banner-tabs" 157 - data-status={status} 158 - className={cn( 159 - "gap-0", 160 - "data-[status=success]:bg-success/20", 161 - "data-[status=degraded]:bg-warning/20", 162 - "data-[status=error]:bg-destructive/20", 163 - "data-[status=info]:bg-info/20", 164 - className, 165 - )} 166 - {...props} 167 - > 168 - {children} 169 - </Tabs> 170 - ); 171 - } 172 - 173 - export function StatusBannerTabsList({ 174 - className, 175 - children, 176 - ...props 177 - }: React.ComponentProps<typeof TabsList>) { 178 - return ( 179 - <div className={cn("rounded-t-lg", "w-full overflow-x-auto")}> 180 - <TabsList 181 - className={cn( 182 - "rounded-none rounded-t-lg p-0", 183 - "border-none", 184 - className, 185 - )} 186 - {...props} 187 - > 188 - {children} 189 - </TabsList> 190 - </div> 191 - ); 192 - } 193 - 194 - export function StatusBannerTabsTrigger({ 195 - className, 196 - children, 197 - status, 198 - ...props 199 - }: React.ComponentProps<typeof TabsTrigger> & { 200 - status?: "success" | "degraded" | "error" | "info"; 201 - }) { 202 - return ( 203 - <TabsTrigger 204 - data-slot="status-banner-tabs-trigger" 205 - data-status={status} 206 - className={cn( 207 - "font-mono", 208 - "rounded-none border-none focus-visible:ring-inset", 209 - "h-full text-foreground data-[state=active]:text-background dark:text-foreground dark:data-[state=active]:text-background", 210 - "data-[state=active]:data-[status=success]:bg-success data-[status=success]:bg-success/50 dark:data-[state=active]:data-[status=success]:bg-success dark:data-[status=success]:bg-success/50", 211 - "data-[state=active]:data-[status=degraded]:bg-warning data-[status=degraded]:bg-warning/50 dark:data-[state=active]:data-[status=degraded]:bg-warning dark:data-[status=degraded]:bg-warning/50", 212 - "data-[state=active]:data-[status=error]:bg-destructive data-[status=error]:bg-destructive/50 dark:data-[state=active]:data-[status=error]:bg-destructive dark:data-[status=error]:bg-destructive/50", 213 - "data-[state=active]:data-[status=info]:bg-info data-[status=info]:bg-info/50 dark:data-[state=active]:data-[status=info]:bg-info dark:data-[status=info]:bg-info/50", 214 - "data-[state=active]:shadow-none", 215 - className, 216 - )} 217 - {...props} 218 - > 219 - {children} 220 - </TabsTrigger> 221 - ); 222 - } 223 - 224 - // NOTE: tabing into content is not being highlighted 225 - export function StatusBannerTabsContent({ 226 - className, 227 - children, 228 - ...props 229 - }: React.ComponentProps<typeof TabsContent>) { 230 - return ( 231 - <TabsContent className={cn("-mx-3", className)} {...props}> 232 - {children} 233 - </TabsContent> 234 - ); 235 - }
+3 -10
apps/status-page/src/components/status-page/status-events.tsx
··· 124 124 } 125 125 withSeparator={index !== updates.length - 1} 126 126 withDot={withDot} 127 - isLast={index === updates.length - 1} 128 127 /> 129 128 ))} 130 129 </div> ··· 136 135 duration, 137 136 withSeparator = true, 138 137 withDot = true, 139 - isLast = false, 140 138 }: { 141 139 report: { 142 140 date: Date; ··· 146 144 withSeparator?: boolean; 147 145 duration?: string; 148 146 withDot?: boolean; 149 - isLast?: boolean; 150 147 }) { 151 148 return ( 152 149 <div data-variant={report.status} className="group"> ··· 160 157 {withSeparator ? <StatusEventTimelineSeparator /> : null} 161 158 </div> 162 159 ) : null} 163 - <div className={cn(isLast ? "mb-0" : "mb-2")}> 160 + <div className="mb-2"> 164 161 <StatusEventTimelineTitle> 165 162 <span>{status[report.status]}</span>{" "} 166 163 {/* underline decoration-dashed underline-offset-2 decoration-muted-foreground/30 */} ··· 212 209 </div> 213 210 </div> 214 211 ) : null} 215 - {/* NOTE: is always last, no need for className="mb-2" */} 216 - <div> 212 + <div className="mb-2"> 217 213 <StatusEventTimelineTitle> 218 214 <span>Maintenance</span>{" "} 219 215 <span className="font-mono text-muted-foreground/70 text-xs"> ··· 263 259 ...props 264 260 }: React.ComponentProps<"div">) { 265 261 return ( 266 - <div 267 - className={cn("font-mono text-muted-foreground text-sm", className)} 268 - {...props} 269 - > 262 + <div className={cn("text-muted-foreground text-sm", className)} {...props}> 270 263 {children} 271 264 </div> 272 265 );
+1 -1
apps/status-page/src/components/status-page/status-feed.tsx
··· 82 82 return ( 83 83 <StatusEmptyState> 84 84 <Newspaper className="size-4 text-muted-foreground" /> 85 - <StatusEmptyStateTitle>No recent notifications</StatusEmptyStateTitle> 85 + <StatusEmptyStateTitle>No recent reports</StatusEmptyStateTitle> 86 86 <StatusEmptyStateDescription> 87 87 There have been no reports within the last 7 days. 88 88 </StatusEmptyStateDescription>
+5 -12
apps/status-page/src/components/status-page/status-monitor.tsx
··· 56 56 {...props} 57 57 > 58 58 <div className="flex flex-row items-center justify-between gap-4"> 59 - <div className="flex flex-row min-w-0 items-center gap-2"> 59 + <div className="flex flex-row items-center gap-2"> 60 60 <StatusMonitorTitle>{monitor.name}</StatusMonitorTitle> 61 61 <StatusMonitorDescription> 62 62 {monitor.description} ··· 90 90 ...props 91 91 }: React.ComponentProps<"div">) { 92 92 return ( 93 - <div 94 - className={cn( 95 - "flex-1 truncate font-medium font-mono text-foreground leading-none", 96 - className, 97 - )} 98 - {...props} 99 - > 93 + <div className={cn("font-medium", className)} {...props}> 100 94 {children} 101 95 </div> 102 96 ); ··· 140 134 return ( 141 135 <div 142 136 className={cn( 143 - "flex size-[12.5px] items-center justify-center rounded-full bg-muted text-background [&>svg]:size-[9px]", 137 + "flex size-4 items-center justify-center rounded-full bg-muted text-background [&>svg]:size-2.5", 144 138 "group-data-[variant=success]/monitor:bg-success", 145 139 "group-data-[variant=degraded]/monitor:bg-warning", 146 140 "group-data-[variant=error]/monitor:bg-destructive", ··· 165 159 isLoading?: boolean; 166 160 }) { 167 161 return ( 168 - <div className="flex flex-row items-center justify-between font-mono text-muted-foreground text-xs"> 162 + <div className="flex flex-row items-center justify-between text-muted-foreground text-xs"> 169 163 <div> 170 164 {isLoading ? ( 171 165 <Skeleton className="h-4 w-18" /> ··· 191 185 return ( 192 186 <div 193 187 {...props} 194 - className={cn("font-mono text-foreground text-sm leading-non", className)} 188 + className={cn("font-mono text-muted-foreground text-sm", className)} 195 189 > 196 190 {children} 197 191 </div> ··· 212 206 return ( 213 207 <div 214 208 className={cn( 215 - "font-mono", 216 209 "group-data-[variant=success]/monitor:text-success", 217 210 "group-data-[variant=degraded]/monitor:text-warning", 218 211 "group-data-[variant=error]/monitor:text-destructive",
+1 -1
apps/status-page/src/components/status-page/status-tracker.tsx
··· 189 189 <HoverCardTrigger asChild> 190 190 <div 191 191 className={cn( 192 - "group relative flex h-full w-full cursor-pointer flex-col px-px outline-none first:pl-0 last:pr-0 hover:opacity-80 focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 data-[aria-pressed=true]:opacity-80", 192 + "group relative flex h-full w-full cursor-pointer flex-col px-px outline-none hover:opacity-80 focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 data-[aria-pressed=true]:opacity-80", 193 193 )} 194 194 onClick={() => handleBarClick(index)} 195 195 onFocus={() => handleBarFocus(index)}
+1 -4
apps/status-page/src/components/status-page/status.tsx
··· 181 181 ...props 182 182 }: React.ComponentProps<"div">) { 183 183 return ( 184 - <div 185 - className={cn("font-mono text-muted-foreground text-sm", className)} 186 - {...props} 187 - > 184 + <div className={cn("text-muted-foreground text-sm", className)} {...props}> 188 185 {children} 189 186 </div> 190 187 );
+3 -18
apps/web/next.config.js
··· 45 45 async headers() { 46 46 return [{ source: "/(.*)", headers: securityHeaders }]; 47 47 }, 48 + trailingSlash: true, 48 49 async redirects() { 49 50 return [ 50 51 { ··· 65 66 ]; 66 67 }, 67 68 async rewrites() { 68 - const HOST = 69 - process.env.NODE_ENV === "development" ? "localhost:3001" : "stpg.dev"; 70 - const PROTOCOL = process.env.NODE_ENV === "development" ? "http" : "https"; 71 69 return { 72 70 beforeFiles: [ 73 71 // Proxy app subdomain to /app ··· 81 79 ], 82 80 destination: "/app/:path*", 83 81 }, 84 - // New design: proxy Next.js assets from external host when cookie indicates "new" 85 - { 86 - source: "/_next/:path*", 87 - has: [ 88 - { type: "cookie", key: "sp_mode", value: "new" }, 89 - { 90 - type: "host", 91 - value: "(?<slug>[^.]+)\\.(openstatus\\.dev|localhost)", 92 - }, 93 - ], 94 - destination: `${PROTOCOL}://${HOST}/_next/:path*`, 95 - }, 96 82 // New design: proxy app routes to external host with slug prefix 97 83 { 98 - source: "/:path((?!_next/).*)", 84 + source: "/:path*", 99 85 has: [ 100 86 { type: "cookie", key: "sp_mode", value: "new" }, 101 87 { ··· 103 89 value: "(?<slug>[^.]+)\\.(openstatus\\.dev|localhost)", 104 90 }, 105 91 ], 106 - // NOTE: might be different on prod and localhost (without :slug) 107 - destination: `${PROTOCOL}://${HOST}/:slug/:path*`, 92 + destination: "https://:slug.stpg.dev/:path*", 108 93 }, 109 94 ], 110 95 };