PolicyEditorPanelHeader.tsx128 lines · main
| 1 | import type { PGPolicy } from '@supabase/pg-meta' |
| 2 | import { ChevronDown, PanelLeftClose, PanelRightClose, X } from 'lucide-react' |
| 3 | import { useState } from 'react' |
| 4 | import { |
| 5 | cn, |
| 6 | Collapsible, |
| 7 | CollapsibleContent, |
| 8 | CollapsibleTrigger, |
| 9 | SheetClose, |
| 10 | SheetHeader, |
| 11 | SheetTitle, |
| 12 | Tooltip, |
| 13 | TooltipContent, |
| 14 | TooltipTrigger, |
| 15 | } from 'ui' |
| 16 | |
| 17 | export const PolicyEditorPanelHeader = ({ |
| 18 | selectedPolicy, |
| 19 | showTools, |
| 20 | setShowTools, |
| 21 | }: { |
| 22 | selectedPolicy?: PGPolicy |
| 23 | showTools: boolean |
| 24 | setShowTools: (v: boolean) => void |
| 25 | }) => { |
| 26 | const [showDetails, setShowDetails] = useState(false) |
| 27 | |
| 28 | return ( |
| 29 | <SheetHeader className={cn('py-3 flex flex-row justify-between items-start border-b')}> |
| 30 | <div className="flex flex-row gap-3 max-w-[75%] items-start"> |
| 31 | <SheetClose |
| 32 | className={cn( |
| 33 | 'text-muted hover:text ring-offset-background transition-opacity hover:opacity-100', |
| 34 | 'focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2', |
| 35 | 'transition disabled:pointer-events-none data-[state=open]:bg-secondary', |
| 36 | 'mt-1.5' |
| 37 | )} |
| 38 | > |
| 39 | <X className="h-3 w-3" /> |
| 40 | <span className="sr-only">Close</span> |
| 41 | </SheetClose> |
| 42 | <div className="h-[24px] w-px bg-border-overlay" /> |
| 43 | <div> |
| 44 | <SheetTitle className="truncate"> |
| 45 | {selectedPolicy !== undefined |
| 46 | ? `Update policy: ${selectedPolicy.name}` |
| 47 | : 'Create a new Row Level Security policy'} |
| 48 | </SheetTitle> |
| 49 | {selectedPolicy !== undefined && ( |
| 50 | <Collapsible |
| 51 | className="-mt-1.5 pb-1.5" |
| 52 | open={showDetails} |
| 53 | onOpenChange={setShowDetails} |
| 54 | > |
| 55 | <CollapsibleTrigger className="group font-normal p-0 [&[data-state=open]>div>svg]:-rotate-180!"> |
| 56 | <div className="flex items-center gap-x-2 w-full"> |
| 57 | <p className="text-xs text-foreground-light group-hover:text-foreground transition"> |
| 58 | View policy details |
| 59 | </p> |
| 60 | <ChevronDown |
| 61 | className="transition-transform duration-200" |
| 62 | strokeWidth={1.5} |
| 63 | size={14} |
| 64 | /> |
| 65 | </div> |
| 66 | </CollapsibleTrigger> |
| 67 | <CollapsibleContent className="grid gap-1.5"> |
| 68 | <div className="flex my-2"> |
| 69 | <div> |
| 70 | <div className="text-xs flex items-start space-x-2 border-b py-1.5"> |
| 71 | <p className="w-[110px] text-foreground-light">Name:</p> |
| 72 | <p className="pr-4">{selectedPolicy?.name}</p> |
| 73 | </div> |
| 74 | <div className="text-xs flex items-start space-x-2 border-b py-1.5"> |
| 75 | <p className="w-[110px] text-foreground-light">Action:</p> |
| 76 | <p className="font-mono pr-4">{selectedPolicy?.action}</p> |
| 77 | </div> |
| 78 | <div className="text-xs flex items-start space-x-2 border-b py-1.5"> |
| 79 | <p className="w-[110px] text-foreground-light">Command:</p> |
| 80 | <p className="font-mono pr-4">{selectedPolicy?.command}</p> |
| 81 | </div> |
| 82 | <div className="text-xs flex items-start space-x-2 border-b py-1.5"> |
| 83 | <p className="w-[110px] text-foreground-light">Target roles:</p> |
| 84 | <p className="font-mono pr-4">{selectedPolicy?.roles.join(', ')}</p> |
| 85 | </div> |
| 86 | <div className="text-xs flex items-start space-x-2 border-b py-1.5"> |
| 87 | <p className="w-[110px] text-foreground-light">USING expression:</p> |
| 88 | <p className="font-mono pr-4">{selectedPolicy?.definition}</p> |
| 89 | </div> |
| 90 | <div className="text-xs flex items-start space-x-2 pt-1.5"> |
| 91 | <p className="w-[110px] text-foreground-light">CHECK expression:</p> |
| 92 | <p |
| 93 | className={`${selectedPolicy?.check ? '' : 'text-foreground-light'} font-mono pr-4`} |
| 94 | > |
| 95 | {selectedPolicy?.check ?? 'None'} |
| 96 | </p> |
| 97 | </div> |
| 98 | </div> |
| 99 | </div> |
| 100 | </CollapsibleContent> |
| 101 | </Collapsible> |
| 102 | )} |
| 103 | </div> |
| 104 | </div> |
| 105 | <Tooltip> |
| 106 | <TooltipTrigger asChild> |
| 107 | <button |
| 108 | aria-expanded={showTools} |
| 109 | aria-controls="ai-chat-assistant" |
| 110 | className={cn( |
| 111 | !showTools ? 'text-foreground-lighter' : 'text-light', |
| 112 | 'mt-1 hover:text-foreground transition' |
| 113 | )} |
| 114 | onClick={() => setShowTools(!showTools)} |
| 115 | > |
| 116 | {!showTools ? ( |
| 117 | <PanelLeftClose size={19} strokeWidth={1} /> |
| 118 | ) : ( |
| 119 | <PanelRightClose size={19} strokeWidth={1} /> |
| 120 | )} |
| 121 | <span className="sr-only">{showTools ? 'Hide' : 'Show'} tools</span> |
| 122 | </button> |
| 123 | </TooltipTrigger> |
| 124 | <TooltipContent side="left">{showTools ? 'Hide' : 'Show'} tools</TooltipContent> |
| 125 | </Tooltip> |
| 126 | </SheetHeader> |
| 127 | ) |
| 128 | } |