Openstatus www.openstatus.dev

feat: input search (#8)

* chore: extend tiny schema and improve cron

* chore: small adjustments

* feat: input search

* wip: small changes

* fix: state issue on mobile and url

* wip: cache-control and escape key down

* chore: small adjustments

authored by

Maximilian Kaske and committed by
GitHub
1190ee94 369034a6

+1197 -51
+2
apps/web/package.json
··· 13 13 "@openstatus/db": "workspace:^", 14 14 "@openstatus/tinybird": "workspace:*", 15 15 "@openstatus/upstash": "workspace:*", 16 + "@radix-ui/react-dialog": "^1.0.4", 16 17 "@radix-ui/react-slot": "^1.0.2", 17 18 "@radix-ui/react-toast": "^1.1.4", 18 19 "@t3-oss/env-nextjs": "0.4.1", 19 20 "@upstash/redis": "^1.21.0", 20 21 "class-variance-authority": "^0.6.0", 21 22 "clsx": "^1.2.1", 23 + "cmdk": "^0.2.0", 22 24 "date-fns": "^2.30.0", 23 25 "lucide-react": "^0.244.0", 24 26 "next": "13.4.6",
+101
apps/web/src/app/_components/event-table.tsx
··· 1 + "use client"; 2 + 3 + import { Badge } from "@/components/ui/badge"; 4 + import { cn } from "@/lib/utils"; 5 + import { formatDistance } from "date-fns"; 6 + import { 7 + Table, 8 + TableBody, 9 + TableCaption, 10 + TableCell, 11 + TableHead, 12 + TableHeader, 13 + TableRow, 14 + } from "@/components/ui/table"; 15 + import React from "react"; 16 + import { Button } from "@/components/ui/button"; 17 + 18 + export function EventTable({ 19 + events, 20 + }: { 21 + // FIXME: should be return type 22 + events: { 23 + id: string; 24 + timestamp: number; 25 + statusCode: number; 26 + latency: number; 27 + url: string; 28 + }[]; 29 + }) { 30 + const [open, toggle] = React.useReducer((state) => !state, false); 31 + return ( 32 + <div className="relative overflow-hidden max-h-56"> 33 + <Table> 34 + <TableCaption>A list of the latest pings.</TableCaption> 35 + <TableHeader> 36 + <TableRow> 37 + <TableHead>Time</TableHead> 38 + <TableHead>Status</TableHead> 39 + <TableHead>Latency</TableHead> 40 + <TableHead className="text-right">URL</TableHead> 41 + </TableRow> 42 + </TableHeader> 43 + <TableBody> 44 + {events.map((event) => { 45 + const isOk = event.statusCode === 200; 46 + const url = new URL(event.url); 47 + return ( 48 + <TableRow key={event.timestamp}> 49 + <TableCell className="font-medium"> 50 + {formatDistance(new Date(event.timestamp), new Date(), { 51 + addSuffix: true, 52 + includeSeconds: true, 53 + })} 54 + </TableCell> 55 + <TableCell> 56 + <Badge 57 + variant="outline" 58 + className={cn( 59 + "text-xs py-0.5 px-2", 60 + isOk 61 + ? "border-green-100 bg-green-50" 62 + : "border-red-100 bg-red-50" 63 + )} 64 + > 65 + {event.statusCode} 66 + <div 67 + className={cn( 68 + "rounded-full bg-foreground h-1.5 w-1.5 ml-1", 69 + isOk ? "bg-green-500" : "bg-red-500" 70 + )} 71 + /> 72 + </Badge> 73 + </TableCell> 74 + <TableCell className="text-muted-foreground"> 75 + {event.latency} 76 + </TableCell> 77 + <TableCell className="text-right font-light truncate"> 78 + {url.pathname} 79 + </TableCell> 80 + </TableRow> 81 + ); 82 + })} 83 + </TableBody> 84 + </Table> 85 + {!open && ( 86 + <div className="absolute inset-0 flex items-end justify-center bg-gradient bg-gradient-to-b from-transparent from-20% to-background"> 87 + {/* TODO: enable button */} 88 + <Button 89 + onClick={toggle} 90 + variant="outline" 91 + size="sm" 92 + className="rounded-full backdrop-blur-sm" 93 + // disabled 94 + > 95 + {`A total of ${events.length} events.`} 96 + </Button> 97 + </div> 98 + )} 99 + </div> 100 + ); 101 + }
+198
apps/web/src/app/_components/input-search.tsx
··· 1 + "use client"; 2 + 3 + import * as React from "react"; 4 + import { 5 + Command, 6 + CommandEmpty, 7 + CommandGroup, 8 + CommandItem, 9 + } from "@/components/ui/command"; 10 + import { Command as CommandPrimitive, useCommandState } from "cmdk"; 11 + 12 + // TODO: once stable, use the shallow route to store the search params inside of the search params 13 + 14 + export function InputSearch({ 15 + events, 16 + onSearch, 17 + }: { 18 + onSearch(value: Record<string, string>): void; 19 + // FIXME: should be return type 20 + events: { 21 + id: string; 22 + timestamp: number; 23 + statusCode: number; 24 + latency: number; 25 + url: string; 26 + }[]; 27 + }) { 28 + const inputRef = React.useRef<HTMLInputElement>(null); 29 + const [open, setOpen] = React.useState<boolean>(false); 30 + const [inputValue, setInputValue] = React.useState<string>(""); 31 + const [currentWord, setCurrentWord] = React.useState(""); 32 + 33 + // TODO: check if there is a move efficient way 34 + React.useEffect(() => { 35 + const searchparams = inputValue 36 + .trim() 37 + .split(" ") 38 + .reduce((prev, curr) => { 39 + const [name, value] = curr.split(":"); 40 + if (value && name && curr !== currentWord) { 41 + // TODO: support multiple value with value.split(",") 42 + prev[name] = value; 43 + } 44 + return prev; 45 + }, {} as Record<string, string>); 46 + onSearch(searchparams); 47 + }, [onSearch, inputValue]); 48 + 49 + // DEFINE YOUR SEARCH PARAMETERS 50 + const search = React.useMemo( 51 + () => 52 + events.reduce( 53 + (prev, curr) => { 54 + const { pathname } = new URL(curr.url); 55 + return { 56 + ...prev, 57 + status: [...new Set([curr.statusCode, ...(prev.status || [])])], 58 + pathname: [...new Set([pathname, ...(prev.pathname || [])])], 59 + }; 60 + }, 61 + // defaultState 62 + { limit: [10, 25, 50], status: [], pathname: [] } as { 63 + status: number[]; 64 + limit: number[]; 65 + pathname: string[]; 66 + } 67 + ), 68 + [events] 69 + ); 70 + 71 + type SearchKey = keyof typeof search; 72 + 73 + return ( 74 + <Command 75 + className="overflow-visible bg-transparent" 76 + filter={(value, search) => { 77 + if (value.includes(currentWord.toLowerCase())) return 1; 78 + return 0; 79 + }} 80 + > 81 + <CommandPrimitive.Input 82 + ref={inputRef} 83 + value={inputValue} 84 + onValueChange={setInputValue} 85 + onKeyDown={(e) => { 86 + if (e.key === "Escape") inputRef?.current.blur(); 87 + }} 88 + onBlur={() => setOpen(false)} 89 + onFocus={() => setOpen(true)} 90 + onInput={(e) => { 91 + const caretPositionStart = e.currentTarget?.selectionStart || -1; 92 + const inputValue = e.currentTarget?.value || ""; 93 + 94 + let start = caretPositionStart; 95 + let end = caretPositionStart; 96 + 97 + while (start > 0 && inputValue[start - 1] !== " ") { 98 + start--; 99 + } 100 + while (end < inputValue.length && inputValue[end] !== " ") { 101 + end++; 102 + } 103 + 104 + const word = inputValue.substring(start, end); 105 + setCurrentWord(word); 106 + }} 107 + placeholder={`${events.length} total logs found...`} 108 + className="flex-1 rounded-md border border-input bg-transparent px-3 py-2 text-sm outline-none ring-offset-background placeholder:text-muted-foreground focus:ring-2 focus:ring-ring focus:ring-offset-2" 109 + /> 110 + <div className="relative mt-2"> 111 + {open ? ( 112 + <div className="z-10 absolute top-0 w-full rounded-md border bg-popover text-popover-foreground shadow-md outline-none animate-in"> 113 + <CommandGroup className="h-full overflow-auto"> 114 + {Object.keys(search).map((key) => { 115 + if ( 116 + inputValue.includes(`${key}:`) && 117 + !currentWord.includes(`${key}:`) 118 + ) 119 + return null; 120 + return ( 121 + <React.Fragment key={key}> 122 + <CommandItem 123 + value={key} 124 + onMouseDown={(e) => { 125 + e.preventDefault(); 126 + e.stopPropagation(); 127 + }} 128 + onSelect={(value) => { 129 + setInputValue((prev) => { 130 + // console.log({ prev, currentWord, value }); 131 + if (currentWord.trim() === "") { 132 + const input = `${prev}${value}`; 133 + return `${input}:`; 134 + } 135 + // lots of cheat 136 + const isStarting = currentWord === prev; 137 + const prefix = isStarting ? "" : " "; 138 + const input = prev.replace( 139 + `${prefix}${currentWord}`, 140 + `${prefix}${value}` 141 + ); 142 + return `${input}:`; 143 + }); 144 + setCurrentWord(`${value}:`); 145 + }} 146 + className="group" 147 + > 148 + {key} 149 + <span className="ml-1 hidden truncate text-muted-foreground/90 group-aria-[selected=true]:block"> 150 + {search[key as SearchKey] 151 + .map((str) => `[${str}]`) 152 + .join(" ")} 153 + </span> 154 + </CommandItem> 155 + {search[key as SearchKey].map((option) => { 156 + return ( 157 + <SubItem 158 + key={option} 159 + value={`${key}:${option}`} 160 + onMouseDown={(e) => { 161 + e.preventDefault(); 162 + e.stopPropagation(); 163 + }} 164 + onSelect={(value) => { 165 + setInputValue((prev) => { 166 + const input = prev.replace(currentWord, value); 167 + return `${input.trim()} `; 168 + }); 169 + setCurrentWord(""); 170 + }} 171 + {...{ currentWord }} 172 + > 173 + {option} 174 + </SubItem> 175 + ); 176 + })} 177 + </React.Fragment> 178 + ); 179 + })} 180 + </CommandGroup> 181 + <CommandEmpty>No results found.</CommandEmpty> 182 + </div> 183 + ) : null} 184 + </div> 185 + </Command> 186 + ); 187 + } 188 + 189 + interface SubItemProps 190 + extends React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item> { 191 + currentWord: string; 192 + } 193 + 194 + const SubItem = ({ currentWord, ...props }: SubItemProps) => { 195 + const search = useCommandState((state) => state.search); 196 + if (!search.includes(":") || !currentWord.includes(":")) return null; 197 + return <CommandItem {...props} />; 198 + };
-41
apps/web/src/app/_components/status-container.tsx
··· 1 - import { Badge } from "@/components/ui/badge"; 2 - import { cn } from "@/lib/utils"; 3 - import { formatDistance } from "date-fns"; 4 - 5 - // TODO: consistency in wording 6 - export function StatusContainer({ 7 - events, 8 - }: { 9 - // FIXME: should be return type 10 - events: { id: string; timestamp: number; statusCode: number }[]; 11 - }) { 12 - return ( 13 - // OVERFLOW-HIDDEN 14 - <div className="relative border border-border rounded-lg backdrop-blur-[2px] p-4 mx-auto max-h-28 overflow-hidden"> 15 - <div className="absolute inset-0 bg-gradient-to-b from-background from-0% via-transparent via-50% to-background to-100%" /> 16 - <ul className="grid gap-2 text-xs text-muted-foreground"> 17 - {events.map(({ timestamp, statusCode }) => { 18 - return ( 19 - <li key={timestamp} className="flex items-center justify-between"> 20 - <p className="font-light"> 21 - {formatDistance(new Date(timestamp), new Date(), { 22 - addSuffix: true, 23 - includeSeconds: true, 24 - })} 25 - </p> 26 - <Badge variant="outline" className="text-xs ml-1 py-0.5 px-2"> 27 - {statusCode} 28 - <div 29 - className={cn( 30 - "rounded-full bg-foreground h-1.5 w-1.5 ml-1", 31 - statusCode === 200 ? "bg-green-500" : "bg-red-500" 32 - )} 33 - /> 34 - </Badge> 35 - </li> 36 - ); 37 - })} 38 - </ul> 39 - </div> 40 - ); 41 - }
+41
apps/web/src/app/_components/table-input-container.tsx
··· 1 + "use client"; 2 + 3 + import React from "react"; 4 + import { EventTable } from "./event-table"; 5 + import { InputSearch } from "./input-search"; 6 + 7 + // TODO: once stable, use the shallow route to store the search params inside of the search params 8 + 9 + export function TableInputContainer({ 10 + events, 11 + }: { 12 + // FIXME: should be return type 13 + events: { 14 + id: string; 15 + timestamp: number; 16 + statusCode: number; 17 + latency: number; 18 + url: string; 19 + }[]; 20 + }) { 21 + const [search, setSearch] = React.useState<Record<string, string>>({}); 22 + 23 + const filteredEvents = events 24 + .filter((event) => { 25 + const url = new URL(event.url); 26 + if (search?.status && event.statusCode !== Number(search.status)) { 27 + return false; 28 + } else if (search?.pathname && url.pathname !== search.pathname) { 29 + return false; 30 + } 31 + return true; 32 + }) 33 + .slice(0, search.limit ? Number(search.limit) : 100); 34 + 35 + return ( 36 + <> 37 + <InputSearch events={events} onSearch={setSearch} /> 38 + <EventTable events={filteredEvents} /> 39 + </> 40 + ); 41 + }
+16 -2
apps/web/src/app/_mocks/response-list.json
··· 8 8 }, 9 9 { 10 10 "id": "openstatus", 11 - "timestamp": 1687718326838, 12 - "statusCode": 500, 11 + "timestamp": 16877183268, 12 + "statusCode": 200, 13 13 "latency": 660, 14 14 "url": "http://localhost:3000/api/v0/ping" 15 15 }, ··· 17 17 "id": "openstatus", 18 18 "timestamp": 1687718289489, 19 19 "statusCode": 500, 20 + "latency": 332, 21 + "url": "http://localhost:3000/api/v0/ping" 22 + }, 23 + { 24 + "id": "openstatus", 25 + "timestamp": 168771832688, 26 + "statusCode": 500, 27 + "latency": 660, 28 + "url": "http://localhost:3000/api/v0/ping" 29 + }, 30 + { 31 + "id": "openstatus", 32 + "timestamp": 16877182894, 33 + "statusCode": 200, 20 34 "latency": 332, 21 35 "url": "http://localhost:3000/api/v0/ping" 22 36 }
+13 -3
apps/web/src/app/api/v0/ping/route.ts
··· 3 3 const redis = Redis.fromEnv(); 4 4 5 5 export async function GET(req: Request) { 6 - const RANDOM = Math.random() > 0.9; 6 + const RANDOM = Math.random() > 0.6; 7 7 try { 8 8 // TODO: connect tinybird, upstash and planetscale 9 9 await redis.ping(); 10 10 if (RANDOM) { 11 11 throw new Error("Arg"); 12 12 } 13 - return new Response("OK", { status: 200 }); 13 + return new Response("OK", { 14 + status: 200, 15 + headers: { 16 + "cache-control": "public, max-age=0, s-maxage=0", 17 + }, 18 + }); 14 19 } catch { 15 - return new Response("Error", { status: 500 }); 20 + return new Response("Error", { 21 + status: 500, 22 + headers: { 23 + "cache-control": "public, max-age=0, s-maxage=0", 24 + }, 25 + }); 16 26 } 17 27 }
+5 -3
apps/web/src/app/page.tsx
··· 2 2 import { HeroForm } from "./_components/hero-form"; 3 3 import { Tinybird, getResponseList } from "@openstatus/tinybird"; 4 4 import { env } from "@/env.mjs"; 5 - import { StatusContainer } from "./_components/status-container"; 6 5 import MOCK from "@/app/_mocks/response-list.json"; 6 + import { TableInputContainer } from "./_components/table-input-container"; 7 7 8 8 const tb = new Tinybird({ token: env.TINY_BIRD_API_KEY }); 9 9 ··· 30 30 <HeroForm /> 31 31 </div> 32 32 </div> 33 - <div className="md:fixed bottom-8 right-8 max-w-max z-10"> 34 - <StatusContainer events={data} /> 33 + <div className="mx-auto max-w-xl w-full z-10 backdrop-blur-[2px]"> 34 + <div className="p-8 border border-border rounded-lg"> 35 + <TableInputContainer events={data} /> 36 + </div> 35 37 </div> 36 38 </div> 37 39 <footer className="text-center text-sm text-muted-foreground mx-auto rounded-full px-4 py-2 border border-border backdrop-blur-[2px]">
+155
apps/web/src/components/ui/command.tsx
··· 1 + "use client" 2 + 3 + import * as React from "react" 4 + import { DialogProps } from "@radix-ui/react-dialog" 5 + import { Command as CommandPrimitive } from "cmdk" 6 + import { Search } from "lucide-react" 7 + 8 + import { cn } from "@/lib/utils" 9 + import { Dialog, DialogContent } from "@/components/ui/dialog" 10 + 11 + const Command = React.forwardRef< 12 + React.ElementRef<typeof CommandPrimitive>, 13 + React.ComponentPropsWithoutRef<typeof CommandPrimitive> 14 + >(({ className, ...props }, ref) => ( 15 + <CommandPrimitive 16 + ref={ref} 17 + className={cn( 18 + "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground", 19 + className 20 + )} 21 + {...props} 22 + /> 23 + )) 24 + Command.displayName = CommandPrimitive.displayName 25 + 26 + interface CommandDialogProps extends DialogProps {} 27 + 28 + const CommandDialog = ({ children, ...props }: CommandDialogProps) => { 29 + return ( 30 + <Dialog {...props}> 31 + <DialogContent className="overflow-hidden p-0 shadow-2xl"> 32 + <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5"> 33 + {children} 34 + </Command> 35 + </DialogContent> 36 + </Dialog> 37 + ) 38 + } 39 + 40 + const CommandInput = React.forwardRef< 41 + React.ElementRef<typeof CommandPrimitive.Input>, 42 + React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input> 43 + >(({ className, ...props }, ref) => ( 44 + <div className="flex items-center border-b px-3" cmdk-input-wrapper=""> 45 + <Search className="mr-2 h-4 w-4 shrink-0 opacity-50" /> 46 + <CommandPrimitive.Input 47 + ref={ref} 48 + className={cn( 49 + "placeholder:text-foreground-muted flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none disabled:cursor-not-allowed disabled:opacity-50", 50 + className 51 + )} 52 + {...props} 53 + /> 54 + </div> 55 + )) 56 + 57 + CommandInput.displayName = CommandPrimitive.Input.displayName 58 + 59 + const CommandList = React.forwardRef< 60 + React.ElementRef<typeof CommandPrimitive.List>, 61 + React.ComponentPropsWithoutRef<typeof CommandPrimitive.List> 62 + >(({ className, ...props }, ref) => ( 63 + <CommandPrimitive.List 64 + ref={ref} 65 + className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)} 66 + {...props} 67 + /> 68 + )) 69 + 70 + CommandList.displayName = CommandPrimitive.List.displayName 71 + 72 + const CommandEmpty = React.forwardRef< 73 + React.ElementRef<typeof CommandPrimitive.Empty>, 74 + React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty> 75 + >((props, ref) => ( 76 + <CommandPrimitive.Empty 77 + ref={ref} 78 + className="py-6 text-center text-sm" 79 + {...props} 80 + /> 81 + )) 82 + 83 + CommandEmpty.displayName = CommandPrimitive.Empty.displayName 84 + 85 + const CommandGroup = React.forwardRef< 86 + React.ElementRef<typeof CommandPrimitive.Group>, 87 + React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group> 88 + >(({ className, ...props }, ref) => ( 89 + <CommandPrimitive.Group 90 + ref={ref} 91 + className={cn( 92 + "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground", 93 + className 94 + )} 95 + {...props} 96 + /> 97 + )) 98 + 99 + CommandGroup.displayName = CommandPrimitive.Group.displayName 100 + 101 + const CommandSeparator = React.forwardRef< 102 + React.ElementRef<typeof CommandPrimitive.Separator>, 103 + React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator> 104 + >(({ className, ...props }, ref) => ( 105 + <CommandPrimitive.Separator 106 + ref={ref} 107 + className={cn("-mx-1 h-px bg-border", className)} 108 + {...props} 109 + /> 110 + )) 111 + CommandSeparator.displayName = CommandPrimitive.Separator.displayName 112 + 113 + const CommandItem = React.forwardRef< 114 + React.ElementRef<typeof CommandPrimitive.Item>, 115 + React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item> 116 + >(({ className, ...props }, ref) => ( 117 + <CommandPrimitive.Item 118 + ref={ref} 119 + className={cn( 120 + "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", 121 + className 122 + )} 123 + {...props} 124 + /> 125 + )) 126 + 127 + CommandItem.displayName = CommandPrimitive.Item.displayName 128 + 129 + const CommandShortcut = ({ 130 + className, 131 + ...props 132 + }: React.HTMLAttributes<HTMLSpanElement>) => { 133 + return ( 134 + <span 135 + className={cn( 136 + "ml-auto text-xs tracking-widest text-muted-foreground", 137 + className 138 + )} 139 + {...props} 140 + /> 141 + ) 142 + } 143 + CommandShortcut.displayName = "CommandShortcut" 144 + 145 + export { 146 + Command, 147 + CommandDialog, 148 + CommandInput, 149 + CommandList, 150 + CommandEmpty, 151 + CommandGroup, 152 + CommandItem, 153 + CommandShortcut, 154 + CommandSeparator, 155 + }
+128
apps/web/src/components/ui/dialog.tsx
··· 1 + "use client"; 2 + 3 + import * as React from "react"; 4 + import * as DialogPrimitive from "@radix-ui/react-dialog"; 5 + import { X } from "lucide-react"; 6 + 7 + import { cn } from "@/lib/utils"; 8 + 9 + const Dialog = DialogPrimitive.Root; 10 + 11 + const DialogTrigger = DialogPrimitive.Trigger; 12 + 13 + const DialogPortal = ({ 14 + className, 15 + children, 16 + ...props 17 + }: DialogPrimitive.DialogPortalProps) => ( 18 + <DialogPrimitive.Portal className={cn(className)} {...props}> 19 + <div className="fixed inset-0 z-50 flex items-start justify-center sm:items-center"> 20 + {children} 21 + </div> 22 + </DialogPrimitive.Portal> 23 + ); 24 + DialogPortal.displayName = DialogPrimitive.Portal.displayName; 25 + 26 + const DialogOverlay = React.forwardRef< 27 + React.ElementRef<typeof DialogPrimitive.Overlay>, 28 + React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> 29 + >(({ className, ...props }, ref) => ( 30 + <DialogPrimitive.Overlay 31 + ref={ref} 32 + className={cn( 33 + "fixed inset-0 z-50 bg-background/80 backdrop-blur-sm transition-all duration-100 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in", 34 + className 35 + )} 36 + {...props} 37 + /> 38 + )); 39 + DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; 40 + 41 + const DialogContent = React.forwardRef< 42 + React.ElementRef<typeof DialogPrimitive.Content>, 43 + React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> 44 + >(({ className, children, ...props }, ref) => ( 45 + <DialogPortal> 46 + <DialogOverlay /> 47 + <DialogPrimitive.Content 48 + ref={ref} 49 + className={cn( 50 + "fixed z-50 grid w-full gap-4 rounded-b-lg border bg-background p-6 shadow-lg animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:max-w-lg sm:rounded-lg sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0", 51 + className 52 + )} 53 + {...props} 54 + > 55 + {children} 56 + <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"> 57 + <X className="h-4 w-4" /> 58 + <span className="sr-only">Close</span> 59 + </DialogPrimitive.Close> 60 + </DialogPrimitive.Content> 61 + </DialogPortal> 62 + )); 63 + DialogContent.displayName = DialogPrimitive.Content.displayName; 64 + 65 + const DialogHeader = ({ 66 + className, 67 + ...props 68 + }: React.HTMLAttributes<HTMLDivElement>) => ( 69 + <div 70 + className={cn( 71 + "flex flex-col space-y-1.5 text-center sm:text-left", 72 + className 73 + )} 74 + {...props} 75 + /> 76 + ); 77 + DialogHeader.displayName = "DialogHeader"; 78 + 79 + const DialogFooter = ({ 80 + className, 81 + ...props 82 + }: React.HTMLAttributes<HTMLDivElement>) => ( 83 + <div 84 + className={cn( 85 + "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", 86 + className 87 + )} 88 + {...props} 89 + /> 90 + ); 91 + DialogFooter.displayName = "DialogFooter"; 92 + 93 + const DialogTitle = React.forwardRef< 94 + React.ElementRef<typeof DialogPrimitive.Title>, 95 + React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title> 96 + >(({ className, ...props }, ref) => ( 97 + <DialogPrimitive.Title 98 + ref={ref} 99 + className={cn( 100 + "text-lg font-semibold leading-none tracking-tight", 101 + className 102 + )} 103 + {...props} 104 + /> 105 + )); 106 + DialogTitle.displayName = DialogPrimitive.Title.displayName; 107 + 108 + const DialogDescription = React.forwardRef< 109 + React.ElementRef<typeof DialogPrimitive.Description>, 110 + React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description> 111 + >(({ className, ...props }, ref) => ( 112 + <DialogPrimitive.Description 113 + ref={ref} 114 + className={cn("text-sm text-muted-foreground", className)} 115 + {...props} 116 + /> 117 + )); 118 + DialogDescription.displayName = DialogPrimitive.Description.displayName; 119 + 120 + export { 121 + Dialog, 122 + DialogTrigger, 123 + DialogContent, 124 + DialogHeader, 125 + DialogFooter, 126 + DialogTitle, 127 + DialogDescription, 128 + };
+117
apps/web/src/components/ui/table.tsx
··· 1 + import * as React from "react"; 2 + 3 + import { cn } from "@/lib/utils"; 4 + 5 + const Table = React.forwardRef< 6 + HTMLTableElement, 7 + React.HTMLAttributes<HTMLTableElement> 8 + >(({ className, ...props }, ref) => ( 9 + <div className="w-full overflow-auto"> 10 + <table 11 + ref={ref} 12 + className={cn("w-full caption-bottom text-sm", className)} 13 + {...props} 14 + /> 15 + </div> 16 + )); 17 + Table.displayName = "Table"; 18 + 19 + const TableHeader = React.forwardRef< 20 + HTMLTableSectionElement, 21 + React.HTMLAttributes<HTMLTableSectionElement> 22 + >(({ className, ...props }, ref) => ( 23 + <thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} /> 24 + )); 25 + TableHeader.displayName = "TableHeader"; 26 + 27 + const TableBody = React.forwardRef< 28 + HTMLTableSectionElement, 29 + React.HTMLAttributes<HTMLTableSectionElement> 30 + >(({ className, ...props }, ref) => ( 31 + <tbody 32 + ref={ref} 33 + className={cn("[&_tr:last-child]:border-0", className)} 34 + {...props} 35 + /> 36 + )); 37 + TableBody.displayName = "TableBody"; 38 + 39 + const TableFooter = React.forwardRef< 40 + HTMLTableSectionElement, 41 + React.HTMLAttributes<HTMLTableSectionElement> 42 + >(({ className, ...props }, ref) => ( 43 + <tfoot 44 + ref={ref} 45 + className={cn("bg-primary font-medium text-primary-foreground", className)} 46 + {...props} 47 + /> 48 + )); 49 + TableFooter.displayName = "TableFooter"; 50 + 51 + const TableRow = React.forwardRef< 52 + HTMLTableRowElement, 53 + React.HTMLAttributes<HTMLTableRowElement> 54 + >(({ className, ...props }, ref) => ( 55 + <tr 56 + ref={ref} 57 + className={cn( 58 + "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", 59 + className 60 + )} 61 + {...props} 62 + /> 63 + )); 64 + TableRow.displayName = "TableRow"; 65 + 66 + const TableHead = React.forwardRef< 67 + HTMLTableCellElement, 68 + React.ThHTMLAttributes<HTMLTableCellElement> 69 + >(({ className, ...props }, ref) => ( 70 + <th 71 + ref={ref} 72 + className={cn( 73 + "h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", 74 + className 75 + )} 76 + {...props} 77 + /> 78 + )); 79 + TableHead.displayName = "TableHead"; 80 + 81 + const TableCell = React.forwardRef< 82 + HTMLTableCellElement, 83 + React.TdHTMLAttributes<HTMLTableCellElement> 84 + >(({ className, ...props }, ref) => ( 85 + <td 86 + ref={ref} 87 + className={cn( 88 + "p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", 89 + className 90 + )} 91 + {...props} 92 + /> 93 + )); 94 + TableCell.displayName = "TableCell"; 95 + 96 + const TableCaption = React.forwardRef< 97 + HTMLTableCaptionElement, 98 + React.HTMLAttributes<HTMLTableCaptionElement> 99 + >(({ className, ...props }, ref) => ( 100 + <caption 101 + ref={ref} 102 + className={cn("mt-4 text-sm text-muted-foreground", className)} 103 + {...props} 104 + /> 105 + )); 106 + TableCaption.displayName = "TableCaption"; 107 + 108 + export { 109 + Table, 110 + TableHeader, 111 + TableBody, 112 + TableFooter, 113 + TableHead, 114 + TableRow, 115 + TableCell, 116 + TableCaption, 117 + };
+1 -1
packages/tinybird/src/client.ts
··· 26 26 siteId: z.string().default("openstatus"), // REMINDER: remove default once alpha 27 27 start: z.number().int().default(0), // always start from a date 28 28 end: z.number().int().optional(), 29 - limit: z.number().int().optional().default(50), // used for pagination 29 + limit: z.number().int().optional().default(100), // used for pagination 30 30 }), 31 31 data: z.object({ 32 32 id: z.string(),
+1 -1
packages/tsconfig/nextjs.json
··· 14 14 "noEmit": true, 15 15 "resolveJsonModule": true, 16 16 "strict": false, 17 - "target": "es5" 17 + "target": "es2015" 18 18 }, 19 19 "include": ["src", "next-env.d.ts"], 20 20 "exclude": ["node_modules"]
+419
pnpm-lock.yaml
··· 75 75 '@openstatus/upstash': 76 76 specifier: workspace:* 77 77 version: link:../../packages/upstash 78 + '@radix-ui/react-dialog': 79 + specifier: ^1.0.4 80 + version: 1.0.4(@types/react-dom@18.2.5)(@types/react@18.2.12)(react-dom@18.2.0)(react@18.2.0) 78 81 '@radix-ui/react-slot': 79 82 specifier: ^1.0.2 80 83 version: 1.0.2(@types/react@18.2.12)(react@18.2.0) ··· 93 96 clsx: 94 97 specifier: ^1.2.1 95 98 version: 1.2.1 99 + cmdk: 100 + specifier: ^0.2.0 101 + version: 0.2.0(@types/react@18.2.12)(react-dom@18.2.0)(react@18.2.0) 96 102 date-fns: 97 103 specifier: ^2.30.0 98 104 version: 2.30.0 ··· 1253 1259 engines: {node: '>=16'} 1254 1260 dev: false 1255 1261 1262 + /@radix-ui/primitive@1.0.0: 1263 + resolution: {integrity: sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==} 1264 + dependencies: 1265 + '@babel/runtime': 7.22.5 1266 + dev: false 1267 + 1256 1268 /@radix-ui/primitive@1.0.1: 1257 1269 resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} 1258 1270 dependencies: ··· 1283 1295 react-dom: 18.2.0(react@18.2.0) 1284 1296 dev: false 1285 1297 1298 + /@radix-ui/react-compose-refs@1.0.0(react@18.2.0): 1299 + resolution: {integrity: sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==} 1300 + peerDependencies: 1301 + react: ^16.8 || ^17.0 || ^18.0 1302 + dependencies: 1303 + '@babel/runtime': 7.22.5 1304 + react: 18.2.0 1305 + dev: false 1306 + 1286 1307 /@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.12)(react@18.2.0): 1287 1308 resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} 1288 1309 peerDependencies: ··· 1297 1318 react: 18.2.0 1298 1319 dev: false 1299 1320 1321 + /@radix-ui/react-context@1.0.0(react@18.2.0): 1322 + resolution: {integrity: sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==} 1323 + peerDependencies: 1324 + react: ^16.8 || ^17.0 || ^18.0 1325 + dependencies: 1326 + '@babel/runtime': 7.22.5 1327 + react: 18.2.0 1328 + dev: false 1329 + 1300 1330 /@radix-ui/react-context@1.0.1(@types/react@18.2.12)(react@18.2.0): 1301 1331 resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} 1302 1332 peerDependencies: ··· 1311 1341 react: 18.2.0 1312 1342 dev: false 1313 1343 1344 + /@radix-ui/react-dialog@1.0.0(@types/react@18.2.12)(react-dom@18.2.0)(react@18.2.0): 1345 + resolution: {integrity: sha512-Yn9YU+QlHYLWwV1XfKiqnGVpWYWk6MeBVM6x/bcoyPvxgjQGoeT35482viLPctTMWoMw0PoHgqfSox7Ig+957Q==} 1346 + peerDependencies: 1347 + react: ^16.8 || ^17.0 || ^18.0 1348 + react-dom: ^16.8 || ^17.0 || ^18.0 1349 + dependencies: 1350 + '@babel/runtime': 7.22.5 1351 + '@radix-ui/primitive': 1.0.0 1352 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) 1353 + '@radix-ui/react-context': 1.0.0(react@18.2.0) 1354 + '@radix-ui/react-dismissable-layer': 1.0.0(react-dom@18.2.0)(react@18.2.0) 1355 + '@radix-ui/react-focus-guards': 1.0.0(react@18.2.0) 1356 + '@radix-ui/react-focus-scope': 1.0.0(react-dom@18.2.0)(react@18.2.0) 1357 + '@radix-ui/react-id': 1.0.0(react@18.2.0) 1358 + '@radix-ui/react-portal': 1.0.0(react-dom@18.2.0)(react@18.2.0) 1359 + '@radix-ui/react-presence': 1.0.0(react-dom@18.2.0)(react@18.2.0) 1360 + '@radix-ui/react-primitive': 1.0.0(react-dom@18.2.0)(react@18.2.0) 1361 + '@radix-ui/react-slot': 1.0.0(react@18.2.0) 1362 + '@radix-ui/react-use-controllable-state': 1.0.0(react@18.2.0) 1363 + aria-hidden: 1.2.3 1364 + react: 18.2.0 1365 + react-dom: 18.2.0(react@18.2.0) 1366 + react-remove-scroll: 2.5.4(@types/react@18.2.12)(react@18.2.0) 1367 + transitivePeerDependencies: 1368 + - '@types/react' 1369 + dev: false 1370 + 1371 + /@radix-ui/react-dialog@1.0.4(@types/react-dom@18.2.5)(@types/react@18.2.12)(react-dom@18.2.0)(react@18.2.0): 1372 + resolution: {integrity: sha512-hJtRy/jPULGQZceSAP2Re6/4NpKo8im6V8P2hUqZsdFiSL8l35kYsw3qbRI6Ay5mQd2+wlLqje770eq+RJ3yZg==} 1373 + peerDependencies: 1374 + '@types/react': '*' 1375 + '@types/react-dom': '*' 1376 + react: ^16.8 || ^17.0 || ^18.0 1377 + react-dom: ^16.8 || ^17.0 || ^18.0 1378 + peerDependenciesMeta: 1379 + '@types/react': 1380 + optional: true 1381 + '@types/react-dom': 1382 + optional: true 1383 + dependencies: 1384 + '@babel/runtime': 7.22.5 1385 + '@radix-ui/primitive': 1.0.1 1386 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.12)(react@18.2.0) 1387 + '@radix-ui/react-context': 1.0.1(@types/react@18.2.12)(react@18.2.0) 1388 + '@radix-ui/react-dismissable-layer': 1.0.4(@types/react-dom@18.2.5)(@types/react@18.2.12)(react-dom@18.2.0)(react@18.2.0) 1389 + '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.12)(react@18.2.0) 1390 + '@radix-ui/react-focus-scope': 1.0.3(@types/react-dom@18.2.5)(@types/react@18.2.12)(react-dom@18.2.0)(react@18.2.0) 1391 + '@radix-ui/react-id': 1.0.1(@types/react@18.2.12)(react@18.2.0) 1392 + '@radix-ui/react-portal': 1.0.3(@types/react-dom@18.2.5)(@types/react@18.2.12)(react-dom@18.2.0)(react@18.2.0) 1393 + '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.5)(@types/react@18.2.12)(react-dom@18.2.0)(react@18.2.0) 1394 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.5)(@types/react@18.2.12)(react-dom@18.2.0)(react@18.2.0) 1395 + '@radix-ui/react-slot': 1.0.2(@types/react@18.2.12)(react@18.2.0) 1396 + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.12)(react@18.2.0) 1397 + '@types/react': 18.2.12 1398 + '@types/react-dom': 18.2.5 1399 + aria-hidden: 1.2.3 1400 + react: 18.2.0 1401 + react-dom: 18.2.0(react@18.2.0) 1402 + react-remove-scroll: 2.5.5(@types/react@18.2.12)(react@18.2.0) 1403 + dev: false 1404 + 1405 + /@radix-ui/react-dismissable-layer@1.0.0(react-dom@18.2.0)(react@18.2.0): 1406 + resolution: {integrity: sha512-n7kDRfx+LB1zLueRDvZ1Pd0bxdJWDUZNQ/GWoxDn2prnuJKRdxsjulejX/ePkOsLi2tTm6P24mDqlMSgQpsT6g==} 1407 + peerDependencies: 1408 + react: ^16.8 || ^17.0 || ^18.0 1409 + react-dom: ^16.8 || ^17.0 || ^18.0 1410 + dependencies: 1411 + '@babel/runtime': 7.22.5 1412 + '@radix-ui/primitive': 1.0.0 1413 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) 1414 + '@radix-ui/react-primitive': 1.0.0(react-dom@18.2.0)(react@18.2.0) 1415 + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) 1416 + '@radix-ui/react-use-escape-keydown': 1.0.0(react@18.2.0) 1417 + react: 18.2.0 1418 + react-dom: 18.2.0(react@18.2.0) 1419 + dev: false 1420 + 1314 1421 /@radix-ui/react-dismissable-layer@1.0.4(@types/react-dom@18.2.5)(@types/react@18.2.12)(react-dom@18.2.0)(react@18.2.0): 1315 1422 resolution: {integrity: sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==} 1316 1423 peerDependencies: ··· 1336 1443 react-dom: 18.2.0(react@18.2.0) 1337 1444 dev: false 1338 1445 1446 + /@radix-ui/react-focus-guards@1.0.0(react@18.2.0): 1447 + resolution: {integrity: sha512-UagjDk4ijOAnGu4WMUPj9ahi7/zJJqNZ9ZAiGPp7waUWJO0O1aWXi/udPphI0IUjvrhBsZJGSN66dR2dsueLWQ==} 1448 + peerDependencies: 1449 + react: ^16.8 || ^17.0 || ^18.0 1450 + dependencies: 1451 + '@babel/runtime': 7.22.5 1452 + react: 18.2.0 1453 + dev: false 1454 + 1455 + /@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.12)(react@18.2.0): 1456 + resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==} 1457 + peerDependencies: 1458 + '@types/react': '*' 1459 + react: ^16.8 || ^17.0 || ^18.0 1460 + peerDependenciesMeta: 1461 + '@types/react': 1462 + optional: true 1463 + dependencies: 1464 + '@babel/runtime': 7.22.5 1465 + '@types/react': 18.2.12 1466 + react: 18.2.0 1467 + dev: false 1468 + 1469 + /@radix-ui/react-focus-scope@1.0.0(react-dom@18.2.0)(react@18.2.0): 1470 + resolution: {integrity: sha512-C4SWtsULLGf/2L4oGeIHlvWQx7Rf+7cX/vKOAD2dXW0A1b5QXwi3wWeaEgW+wn+SEVrraMUk05vLU9fZZz5HbQ==} 1471 + peerDependencies: 1472 + react: ^16.8 || ^17.0 || ^18.0 1473 + react-dom: ^16.8 || ^17.0 || ^18.0 1474 + dependencies: 1475 + '@babel/runtime': 7.22.5 1476 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) 1477 + '@radix-ui/react-primitive': 1.0.0(react-dom@18.2.0)(react@18.2.0) 1478 + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) 1479 + react: 18.2.0 1480 + react-dom: 18.2.0(react@18.2.0) 1481 + dev: false 1482 + 1483 + /@radix-ui/react-focus-scope@1.0.3(@types/react-dom@18.2.5)(@types/react@18.2.12)(react-dom@18.2.0)(react@18.2.0): 1484 + resolution: {integrity: sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ==} 1485 + peerDependencies: 1486 + '@types/react': '*' 1487 + '@types/react-dom': '*' 1488 + react: ^16.8 || ^17.0 || ^18.0 1489 + react-dom: ^16.8 || ^17.0 || ^18.0 1490 + peerDependenciesMeta: 1491 + '@types/react': 1492 + optional: true 1493 + '@types/react-dom': 1494 + optional: true 1495 + dependencies: 1496 + '@babel/runtime': 7.22.5 1497 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.12)(react@18.2.0) 1498 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.5)(@types/react@18.2.12)(react-dom@18.2.0)(react@18.2.0) 1499 + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.12)(react@18.2.0) 1500 + '@types/react': 18.2.12 1501 + '@types/react-dom': 18.2.5 1502 + react: 18.2.0 1503 + react-dom: 18.2.0(react@18.2.0) 1504 + dev: false 1505 + 1506 + /@radix-ui/react-id@1.0.0(react@18.2.0): 1507 + resolution: {integrity: sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==} 1508 + peerDependencies: 1509 + react: ^16.8 || ^17.0 || ^18.0 1510 + dependencies: 1511 + '@babel/runtime': 7.22.5 1512 + '@radix-ui/react-use-layout-effect': 1.0.0(react@18.2.0) 1513 + react: 18.2.0 1514 + dev: false 1515 + 1516 + /@radix-ui/react-id@1.0.1(@types/react@18.2.12)(react@18.2.0): 1517 + resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} 1518 + peerDependencies: 1519 + '@types/react': '*' 1520 + react: ^16.8 || ^17.0 || ^18.0 1521 + peerDependenciesMeta: 1522 + '@types/react': 1523 + optional: true 1524 + dependencies: 1525 + '@babel/runtime': 7.22.5 1526 + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.12)(react@18.2.0) 1527 + '@types/react': 18.2.12 1528 + react: 18.2.0 1529 + dev: false 1530 + 1531 + /@radix-ui/react-portal@1.0.0(react-dom@18.2.0)(react@18.2.0): 1532 + resolution: {integrity: sha512-a8qyFO/Xb99d8wQdu4o7qnigNjTPG123uADNecz0eX4usnQEj7o+cG4ZX4zkqq98NYekT7UoEQIjxBNWIFuqTA==} 1533 + peerDependencies: 1534 + react: ^16.8 || ^17.0 || ^18.0 1535 + react-dom: ^16.8 || ^17.0 || ^18.0 1536 + dependencies: 1537 + '@babel/runtime': 7.22.5 1538 + '@radix-ui/react-primitive': 1.0.0(react-dom@18.2.0)(react@18.2.0) 1539 + react: 18.2.0 1540 + react-dom: 18.2.0(react@18.2.0) 1541 + dev: false 1542 + 1339 1543 /@radix-ui/react-portal@1.0.3(@types/react-dom@18.2.5)(@types/react@18.2.12)(react-dom@18.2.0)(react@18.2.0): 1340 1544 resolution: {integrity: sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==} 1341 1545 peerDependencies: ··· 1357 1561 react-dom: 18.2.0(react@18.2.0) 1358 1562 dev: false 1359 1563 1564 + /@radix-ui/react-presence@1.0.0(react-dom@18.2.0)(react@18.2.0): 1565 + resolution: {integrity: sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==} 1566 + peerDependencies: 1567 + react: ^16.8 || ^17.0 || ^18.0 1568 + react-dom: ^16.8 || ^17.0 || ^18.0 1569 + dependencies: 1570 + '@babel/runtime': 7.22.5 1571 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) 1572 + '@radix-ui/react-use-layout-effect': 1.0.0(react@18.2.0) 1573 + react: 18.2.0 1574 + react-dom: 18.2.0(react@18.2.0) 1575 + dev: false 1576 + 1360 1577 /@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.5)(@types/react@18.2.12)(react-dom@18.2.0)(react@18.2.0): 1361 1578 resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==} 1362 1579 peerDependencies: ··· 1379 1596 react-dom: 18.2.0(react@18.2.0) 1380 1597 dev: false 1381 1598 1599 + /@radix-ui/react-primitive@1.0.0(react-dom@18.2.0)(react@18.2.0): 1600 + resolution: {integrity: sha512-EyXe6mnRlHZ8b6f4ilTDrXmkLShICIuOTTj0GX4w1rp+wSxf3+TD05u1UOITC8VsJ2a9nwHvdXtOXEOl0Cw/zQ==} 1601 + peerDependencies: 1602 + react: ^16.8 || ^17.0 || ^18.0 1603 + react-dom: ^16.8 || ^17.0 || ^18.0 1604 + dependencies: 1605 + '@babel/runtime': 7.22.5 1606 + '@radix-ui/react-slot': 1.0.0(react@18.2.0) 1607 + react: 18.2.0 1608 + react-dom: 18.2.0(react@18.2.0) 1609 + dev: false 1610 + 1382 1611 /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.5)(@types/react@18.2.12)(react-dom@18.2.0)(react@18.2.0): 1383 1612 resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==} 1384 1613 peerDependencies: ··· 1400 1629 react-dom: 18.2.0(react@18.2.0) 1401 1630 dev: false 1402 1631 1632 + /@radix-ui/react-slot@1.0.0(react@18.2.0): 1633 + resolution: {integrity: sha512-3mrKauI/tWXo1Ll+gN5dHcxDPdm/Df1ufcDLCecn+pnCIVcdWE7CujXo8QaXOWRJyZyQWWbpB8eFwHzWXlv5mQ==} 1634 + peerDependencies: 1635 + react: ^16.8 || ^17.0 || ^18.0 1636 + dependencies: 1637 + '@babel/runtime': 7.22.5 1638 + '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) 1639 + react: 18.2.0 1640 + dev: false 1641 + 1403 1642 /@radix-ui/react-slot@1.0.2(@types/react@18.2.12)(react@18.2.0): 1404 1643 resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} 1405 1644 peerDependencies: ··· 1447 1686 react-dom: 18.2.0(react@18.2.0) 1448 1687 dev: false 1449 1688 1689 + /@radix-ui/react-use-callback-ref@1.0.0(react@18.2.0): 1690 + resolution: {integrity: sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==} 1691 + peerDependencies: 1692 + react: ^16.8 || ^17.0 || ^18.0 1693 + dependencies: 1694 + '@babel/runtime': 7.22.5 1695 + react: 18.2.0 1696 + dev: false 1697 + 1450 1698 /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.12)(react@18.2.0): 1451 1699 resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==} 1452 1700 peerDependencies: ··· 1461 1709 react: 18.2.0 1462 1710 dev: false 1463 1711 1712 + /@radix-ui/react-use-controllable-state@1.0.0(react@18.2.0): 1713 + resolution: {integrity: sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg==} 1714 + peerDependencies: 1715 + react: ^16.8 || ^17.0 || ^18.0 1716 + dependencies: 1717 + '@babel/runtime': 7.22.5 1718 + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) 1719 + react: 18.2.0 1720 + dev: false 1721 + 1464 1722 /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.2.12)(react@18.2.0): 1465 1723 resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==} 1466 1724 peerDependencies: ··· 1476 1734 react: 18.2.0 1477 1735 dev: false 1478 1736 1737 + /@radix-ui/react-use-escape-keydown@1.0.0(react@18.2.0): 1738 + resolution: {integrity: sha512-JwfBCUIfhXRxKExgIqGa4CQsiMemo1Xt0W/B4ei3fpzpvPENKpMKQ8mZSB6Acj3ebrAEgi2xiQvcI1PAAodvyg==} 1739 + peerDependencies: 1740 + react: ^16.8 || ^17.0 || ^18.0 1741 + dependencies: 1742 + '@babel/runtime': 7.22.5 1743 + '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0) 1744 + react: 18.2.0 1745 + dev: false 1746 + 1479 1747 /@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.2.12)(react@18.2.0): 1480 1748 resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==} 1481 1749 peerDependencies: ··· 1488 1756 '@babel/runtime': 7.22.5 1489 1757 '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.12)(react@18.2.0) 1490 1758 '@types/react': 18.2.12 1759 + react: 18.2.0 1760 + dev: false 1761 + 1762 + /@radix-ui/react-use-layout-effect@1.0.0(react@18.2.0): 1763 + resolution: {integrity: sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==} 1764 + peerDependencies: 1765 + react: ^16.8 || ^17.0 || ^18.0 1766 + dependencies: 1767 + '@babel/runtime': 7.22.5 1491 1768 react: 18.2.0 1492 1769 dev: false 1493 1770 ··· 2015 2292 /argparse@2.0.1: 2016 2293 resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 2017 2294 2295 + /aria-hidden@1.2.3: 2296 + resolution: {integrity: sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==} 2297 + engines: {node: '>=10'} 2298 + dependencies: 2299 + tslib: 2.5.3 2300 + dev: false 2301 + 2018 2302 /aria-query@5.2.1: 2019 2303 resolution: {integrity: sha512-7uFg4b+lETFgdaJyETnILsXgnnzVnkHcgRbwbPwevm5x/LmUlt3MjczMRe1zg824iBgXZNRPTBftNYyRSKLp2g==} 2020 2304 dependencies: ··· 2402 2686 engines: {node: '>=6'} 2403 2687 dev: false 2404 2688 2689 + /cmdk@0.2.0(@types/react@18.2.12)(react-dom@18.2.0)(react@18.2.0): 2690 + resolution: {integrity: sha512-JQpKvEOb86SnvMZbYaFKYhvzFntWBeSZdyii0rZPhKJj9uwJBxu4DaVYDrRN7r3mPop56oPhRw+JYWTKs66TYw==} 2691 + peerDependencies: 2692 + react: ^18.0.0 2693 + react-dom: ^18.0.0 2694 + dependencies: 2695 + '@radix-ui/react-dialog': 1.0.0(@types/react@18.2.12)(react-dom@18.2.0)(react@18.2.0) 2696 + command-score: 0.1.2 2697 + react: 18.2.0 2698 + react-dom: 18.2.0(react@18.2.0) 2699 + transitivePeerDependencies: 2700 + - '@types/react' 2701 + dev: false 2702 + 2405 2703 /color-convert@1.9.3: 2406 2704 resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} 2407 2705 dependencies: ··· 2426 2724 engines: {node: '>= 0.8'} 2427 2725 dependencies: 2428 2726 delayed-stream: 1.0.0 2727 + dev: false 2728 + 2729 + /command-score@0.1.2: 2730 + resolution: {integrity: sha512-VtDvQpIJBvBatnONUsPzXYFVKQQAhuf3XTNOAsdBxCNO/QCtUUd8LSgjn0GVarBkCad6aJCZfXgrjYbl/KRr7w==} 2429 2731 dev: false 2430 2732 2431 2733 /commander@10.0.1: ··· 2618 2920 /dequal@2.0.3: 2619 2921 resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} 2620 2922 engines: {node: '>=6'} 2923 + dev: false 2924 + 2925 + /detect-node-es@1.1.0: 2926 + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} 2621 2927 dev: false 2622 2928 2623 2929 /didyoumean@1.2.2: ··· 3546 3852 has-symbols: 1.0.3 3547 3853 dev: false 3548 3854 3855 + /get-nonce@1.0.1: 3856 + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} 3857 + engines: {node: '>=6'} 3858 + dev: false 3859 + 3549 3860 /get-stream@6.0.1: 3550 3861 resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} 3551 3862 engines: {node: '>=10'} ··· 3879 4190 side-channel: 1.0.4 3880 4191 dev: false 3881 4192 4193 + /invariant@2.2.4: 4194 + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} 4195 + dependencies: 4196 + loose-envify: 1.4.0 4197 + dev: false 4198 + 3882 4199 /is-array-buffer@3.0.2: 3883 4200 resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} 3884 4201 dependencies: ··· 4936 5253 resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} 4937 5254 dev: false 4938 5255 5256 + /react-remove-scroll-bar@2.3.4(@types/react@18.2.12)(react@18.2.0): 5257 + resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==} 5258 + engines: {node: '>=10'} 5259 + peerDependencies: 5260 + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 5261 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 5262 + peerDependenciesMeta: 5263 + '@types/react': 5264 + optional: true 5265 + dependencies: 5266 + '@types/react': 18.2.12 5267 + react: 18.2.0 5268 + react-style-singleton: 2.2.1(@types/react@18.2.12)(react@18.2.0) 5269 + tslib: 2.5.3 5270 + dev: false 5271 + 5272 + /react-remove-scroll@2.5.4(@types/react@18.2.12)(react@18.2.0): 5273 + resolution: {integrity: sha512-xGVKJJr0SJGQVirVFAUZ2k1QLyO6m+2fy0l8Qawbp5Jgrv3DeLalrfMNBFSlmz5kriGGzsVBtGVnf4pTKIhhWA==} 5274 + engines: {node: '>=10'} 5275 + peerDependencies: 5276 + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 5277 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 5278 + peerDependenciesMeta: 5279 + '@types/react': 5280 + optional: true 5281 + dependencies: 5282 + '@types/react': 18.2.12 5283 + react: 18.2.0 5284 + react-remove-scroll-bar: 2.3.4(@types/react@18.2.12)(react@18.2.0) 5285 + react-style-singleton: 2.2.1(@types/react@18.2.12)(react@18.2.0) 5286 + tslib: 2.5.3 5287 + use-callback-ref: 1.3.0(@types/react@18.2.12)(react@18.2.0) 5288 + use-sidecar: 1.1.2(@types/react@18.2.12)(react@18.2.0) 5289 + dev: false 5290 + 5291 + /react-remove-scroll@2.5.5(@types/react@18.2.12)(react@18.2.0): 5292 + resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==} 5293 + engines: {node: '>=10'} 5294 + peerDependencies: 5295 + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 5296 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 5297 + peerDependenciesMeta: 5298 + '@types/react': 5299 + optional: true 5300 + dependencies: 5301 + '@types/react': 18.2.12 5302 + react: 18.2.0 5303 + react-remove-scroll-bar: 2.3.4(@types/react@18.2.12)(react@18.2.0) 5304 + react-style-singleton: 2.2.1(@types/react@18.2.12)(react@18.2.0) 5305 + tslib: 2.5.3 5306 + use-callback-ref: 1.3.0(@types/react@18.2.12)(react@18.2.0) 5307 + use-sidecar: 1.1.2(@types/react@18.2.12)(react@18.2.0) 5308 + dev: false 5309 + 5310 + /react-style-singleton@2.2.1(@types/react@18.2.12)(react@18.2.0): 5311 + resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} 5312 + engines: {node: '>=10'} 5313 + peerDependencies: 5314 + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 5315 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 5316 + peerDependenciesMeta: 5317 + '@types/react': 5318 + optional: true 5319 + dependencies: 5320 + '@types/react': 18.2.12 5321 + get-nonce: 1.0.1 5322 + invariant: 2.2.4 5323 + react: 18.2.0 5324 + tslib: 2.5.3 5325 + dev: false 5326 + 4939 5327 /react@18.2.0: 4940 5328 resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} 4941 5329 engines: {node: '>=0.10.0'} ··· 5771 6159 resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 5772 6160 dependencies: 5773 6161 punycode: 2.3.0 6162 + 6163 + /use-callback-ref@1.3.0(@types/react@18.2.12)(react@18.2.0): 6164 + resolution: {integrity: sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==} 6165 + engines: {node: '>=10'} 6166 + peerDependencies: 6167 + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 6168 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 6169 + peerDependenciesMeta: 6170 + '@types/react': 6171 + optional: true 6172 + dependencies: 6173 + '@types/react': 18.2.12 6174 + react: 18.2.0 6175 + tslib: 2.5.3 6176 + dev: false 6177 + 6178 + /use-sidecar@1.1.2(@types/react@18.2.12)(react@18.2.0): 6179 + resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} 6180 + engines: {node: '>=10'} 6181 + peerDependencies: 6182 + '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0 6183 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 6184 + peerDependenciesMeta: 6185 + '@types/react': 6186 + optional: true 6187 + dependencies: 6188 + '@types/react': 18.2.12 6189 + detect-node-es: 1.1.0 6190 + react: 18.2.0 6191 + tslib: 2.5.3 6192 + dev: false 5774 6193 5775 6194 /util-deprecate@1.0.2: 5776 6195 resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}