Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. ๐ซ Status page with uptime monitoring & API monitoring as code ๐ซ openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor
Something went wrong. Try again.
5.3 kB ยท 219 lines
TSX
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220import type { PageComponentImpact } from "@openstatus/db/src/schema/page_components/constants";import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue,} from "@openstatus/ui/components/ui/select";import { cn } from "@openstatus/ui/lib/utils";import { Fragment } from "react";
import { StatusDot } from "@/components/common/status-dot";import { toComponentSections } from "@/data/page-components.client";import { impactDisplay } from "@/data/status-report-updates.client";
type Component = { id: number; name: string; groupId?: number | null; order?: number | null; groupOrder?: number | null;};
export function ComponentList({ children, className, ...props}: React.ComponentProps<"ul">) { return ( <ul data-slot="component-list" className={cn("flex flex-col gap-1 text-sm", className)} {...props} > {children} </ul> );}
export function ComponentListItem({ children, className, ...props}: React.ComponentProps<"li">) { return ( <li data-slot="component-list-item" className={cn("group flex min-h-7 items-center gap-2", className)} {...props} > {children} </li> );}
/** `group` renders muted after the name for components in a group. */export function ComponentListName({ children, group, className, ...props}: React.ComponentProps<"span"> & { group?: string | null }) { return ( <span data-slot="component-list-name" className={cn("truncate font-mono", className)} {...props} > {children} {group ? ( <span className="text-muted-foreground ml-1.5">{group}</span> ) : null} </span> );}
/** Dot + label for an impact; nullish reads as untriaged. */export function ComponentImpact({ impact, className, ...props}: React.ComponentProps<"span"> & { impact: PageComponentImpact | null | undefined;}) { const display = impactDisplay(impact); return ( <span data-slot="component-impact" className={cn( "inline-flex shrink-0 items-center gap-1.5 font-mono", className, )} {...props} > <StatusDot variant={display.variant} /> {display.label} </span> );}
/** Right-aligned controls on a row, like `DetailActions`. */export function ComponentListActions({ children, className, ...props}: React.ComponentProps<"div">) { return ( <div data-slot="component-list-actions" className={cn("ml-auto flex shrink-0 items-center gap-1", className)} {...props} > {children} </div> );}
export function ComponentListEmpty({ children = "No components affected.", className, ...props}: React.ComponentProps<"p">) { return ( <p data-slot="component-list-empty" className={cn("text-muted-foreground text-sm", className)} {...props} > {children} </p> );}
// Borderless until hovered, like PropertySelectTrigger.export function ComponentListSelectTrigger({ className, ...props}: React.ComponentProps<typeof SelectTrigger>) { return ( <SelectTrigger size="sm" className={cn( "hover:bg-accent dark:hover:bg-accent/50 data-[state=open]:bg-accent border-transparent bg-transparent shadow-none dark:bg-transparent", className, )} {...props} /> );}
/** * Picker for `components` not yet on the list, in page order: a group's * members sit under its label, runs of ungrouped components in between, * separated. */export function ComponentListAdd({ components, groups = [], onAdd, disabled = false, placeholder, className,}: { components: Component[]; groups?: { id: number; name: string }[]; onAdd: (id: number) => void; disabled?: boolean; /** Replaces the "Add component" / "All components added" copy. */ placeholder?: string; className?: string;}) { const sections = toComponentSections(components, groups); const option = (c: Component) => ( <SelectItem key={c.id} value={String(c.id)} className="font-mono"> {c.name} </SelectItem> );
return ( <Select value="" disabled={disabled || components.length === 0} onValueChange={(value) => onAdd(Number(value))} > <ComponentListSelectTrigger aria-label="Add component" className={cn("text-muted-foreground", className)} > <SelectValue placeholder={ placeholder ?? (components.length ? "Add component" : "All components added") } /> </ComponentListSelectTrigger> <SelectContent> {sections.map((section, i) => { const key = section.group?.id ?? `ungrouped-${i}`; return ( <Fragment key={key}> {i > 0 ? <SelectSeparator /> : null} {section.group ? ( <SelectGroup> <SelectLabel>{section.group.name}</SelectLabel> {section.items.map(option)} </SelectGroup> ) : ( section.items.map(option) )} </Fragment> ); })} </SelectContent> </Select> );}