Openstatus www.openstatus.dev

feat: improve playground (#593)

authored by

Maximilian Kaske and committed by
GitHub
c732e129 ad563157

+116 -22
+114 -21
apps/web/src/app/play/page.tsx
··· 1 - import * as z from "zod"; 1 + import Link from "next/link"; 2 + import type { LucideIcon } from "lucide-react"; 3 + import { Clock, FileCode, Gauge, Palette, PanelTop } from "lucide-react"; 2 4 3 - import { BackButton } from "@/components/layout/back-button"; 4 - import CheckerPlay from "./checker/_components/checker-play"; 5 - import StatusPlay from "./status/_components/status-play"; 5 + import { Button } from "@openstatus/ui"; 6 6 7 - /** 8 - * allowed URL search params 9 - */ 10 - const searchParamsSchema = z.object({ 11 - timezone: z.string().optional(), 12 - }); 13 - 14 - export default async function PlayPage({ 15 - searchParams, 16 - }: { 17 - searchParams: { [key: string]: string | string[] | undefined }; 18 - }) { 19 - const search = searchParamsSchema.safeParse(searchParams); 20 - const timezone = search.success ? search.data.timezone : undefined; 7 + import { Shell } from "@/components/dashboard/shell"; 8 + import { BackButton } from "@/components/layout/back-button"; 9 + import { cn } from "@/lib/utils"; 21 10 11 + export default async function PlayPage() { 22 12 return ( 23 13 <> 24 14 <BackButton href="/" /> 25 - <div className="grid w-full gap-4"> 26 - <StatusPlay timezone={timezone} /> 27 - <CheckerPlay /> 15 + <div className="grid w-full grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3"> 16 + {playgrounds.map((play, i) => { 17 + const isFirst = i === 0; 18 + return ( 19 + <Card 20 + key={i} 21 + className={isFirst ? "sm:col-span-2" : undefined} 22 + {...play} 23 + /> 24 + ); 25 + })} 28 26 </div> 29 27 </> 30 28 ); 31 29 } 30 + 31 + interface CardProps extends React.HTMLAttributes<HTMLDivElement>, Playground {} 32 + 33 + function Card({ 34 + label, 35 + description, 36 + href, 37 + variant = "default", 38 + icon, 39 + className, 40 + ...props 41 + }: CardProps) { 42 + const buttonVariant = variant === "default" ? "outline" : "default"; 43 + const shellClassName = 44 + variant === "default" ? "" : "bg-accent text-accent-foreground"; 45 + const Icon = icon; 46 + 47 + const isExternal = href.startsWith("http"); 48 + const externalProps = isExternal 49 + ? { target: "_blank", rel: "noreferrer" } 50 + : {}; 51 + 52 + return ( 53 + <Shell 54 + className={cn( 55 + "group flex flex-col gap-3 hover:shadow", 56 + shellClassName, 57 + className, 58 + )} 59 + {...props} 60 + > 61 + <div className="flex-1 space-y-2"> 62 + <h2 className={cn("font-cal text-xl")}>{label}</h2> 63 + <p className="text-muted-foreground">{description}</p> 64 + </div> 65 + <div className="flex items-center justify-between"> 66 + <Button variant={buttonVariant} className="rounded-full" asChild> 67 + <Link href={href} {...externalProps}> 68 + Learn more 69 + </Link> 70 + </Button> 71 + <div className="border-border bg-background rounded-full border p-2 transition-transform duration-200 group-hover:-rotate-12"> 72 + <Icon className="text-muted-foreground h-5 w-5" /> 73 + </div> 74 + </div> 75 + </Shell> 76 + ); 77 + } 78 + 79 + type Playground = { 80 + href: string; 81 + label: string; 82 + description: string; 83 + icon: LucideIcon; 84 + variant?: "default" | "primary"; 85 + }; 86 + 87 + const playgrounds: Playground[] = [ 88 + { 89 + href: "/play/checker", 90 + label: "Speed Checker", 91 + description: 92 + "Get speed insights for your api, website from multiple regions. No account needed.", 93 + icon: Gauge, 94 + variant: "primary", 95 + }, 96 + { 97 + href: "/play/status", 98 + label: "Status Page", 99 + description: 100 + "Get a status page for your website or api, supporting timezones.", 101 + icon: PanelTop, 102 + }, 103 + { 104 + href: "https://astro.openstat.us", 105 + label: "Custom Astro Status Page", 106 + description: 107 + "Grab your API key and create a custom status page with our Astro starter.", 108 + icon: Palette, 109 + }, 110 + { 111 + href: "https://time.openstatus.dev", 112 + label: "Shadcn UI Time Picker", 113 + description: 114 + "The missing time picker for your next project. Supports 12 hour and 24 hour formats. Fully accessible.", 115 + icon: Clock, 116 + }, 117 + { 118 + href: "https://openstat.us", 119 + label: "All Status Codes", 120 + description: 121 + "Use the endpoint to return the desired error code for testing purposes.", 122 + icon: FileCode, 123 + }, 124 + ];
+2 -1
apps/web/src/components/layout/marketing-menu.tsx
··· 16 16 import { AppLink } from "./app-link"; 17 17 18 18 const pages = [ 19 + { href: "/blog", label: "Blog", segment: "blog" }, 20 + { href: "/play", label: "Playground", segment: "play" }, 19 21 { href: "/changelog", label: "Changelog", segment: "changelog" }, 20 - { href: "/blog", label: "Blog", segment: "blog" }, 21 22 { href: "/pricing", label: "Pricing", segment: "pricing" }, 22 23 { href: "https://docs.openstatus.dev", label: "Documentation" }, 23 24 ];