forked from pdsls.dev/pdsls
this repo has no description

plc logs filters

Changed files
+104 -76
src
components
views
+13 -16
src/components/tooltip.tsx
··· 2 2 3 3 const isTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 1; 4 4 5 - const Tooltip = (props: { text: string; children: JSX.Element }) => { 6 - const width = (props.text.length - 1).toString(); 7 - return ( 8 - <div class="group/tooltip relative flex items-center"> 9 - {props.children} 10 - <Show when={!isTouchDevice}> 11 - <span 12 - style={`transform: translate(-50%, 28px); min-width: ${width}ch`} 13 - class={`left-50% border-0.5 dark:shadow-dark-900 pointer-events-none absolute z-10 hidden select-none whitespace-nowrap rounded border-neutral-300 bg-white p-1 text-center font-sans text-xs text-slate-900 shadow-md group-hover/tooltip:inline dark:border-neutral-600 dark:bg-neutral-800 dark:text-slate-100`} 14 - > 15 - {props.text} 16 - </span> 17 - </Show> 18 - </div> 19 - ); 20 - }; 5 + const Tooltip = (props: { text: string; children: JSX.Element }) => ( 6 + <div class="group/tooltip relative flex items-center"> 7 + {props.children} 8 + <Show when={!isTouchDevice}> 9 + <span 10 + style={`transform: translate(-50%, 28px)`} 11 + class={`left-50% border-0.5 dark:shadow-dark-900 pointer-events-none absolute z-10 hidden min-w-fit select-none whitespace-nowrap rounded border-neutral-300 bg-white p-1 text-center font-sans text-xs text-slate-900 shadow-md group-hover/tooltip:inline dark:border-neutral-600 dark:bg-neutral-800 dark:text-slate-100`} 12 + > 13 + {props.text} 14 + </span> 15 + </Show> 16 + </div> 17 + ); 21 18 22 19 export default Tooltip;
+91 -60
src/views/repo.tsx
··· 21 21 import { localDateFromTimestamp } from "../utils/date.js"; 22 22 23 23 type Tab = "collections" | "backlinks" | "doc" | "blobs"; 24 + type PlcEvent = "handle" | "rotation_key" | "service" | "verification_method"; 24 25 25 - const RepoView = () => { 26 - const params = useParams(); 27 - const [error, setError] = createSignal<string>(); 28 - const [downloading, setDownloading] = createSignal(false); 29 - const [didDoc, setDidDoc] = createSignal<DidDocument>(); 30 - const [backlinks, setBacklinks] = createSignal<{ links: LinkData; target: string }>(); 31 - const [nsids, setNsids] = createSignal<Record<string, { hidden: boolean; nsids: string[] }>>(); 32 - const [tab, setTab] = createSignal<Tab>("collections"); 33 - const [filter, setFilter] = createSignal<string>(); 34 - const [plcOps, setPlcOps] = 35 - createSignal<[IndexedEntry<CompatibleOperationOrTombstone>, DiffEntry[]][]>(); 36 - const [showPlcLogs, setShowPlcLogs] = createSignal(false); 37 - const [loading, setLoading] = createSignal(false); 38 - let rpc: Client; 39 - let pds: string; 40 - const did = params.repo; 26 + const PlcLogView = (props: { 27 + plcOps: [IndexedEntry<CompatibleOperationOrTombstone>, DiffEntry[]][]; 28 + }) => { 29 + const [activePlcEvent, setActivePlcEvent] = createSignal<PlcEvent | undefined>(); 41 30 42 - const RepoTab = (props: { tab: Tab; label: string }) => ( 31 + const FilterButton = (props: { icon: string; event: PlcEvent }) => ( 43 32 <button 44 33 classList={{ 45 - "rounded-lg flex flex-1 py-1 justify-center": true, 46 - "bg-zinc-200/70 dark:bg-dark-200 shadow-sm dark:shadow-dark-900": tab() === props.tab, 47 - "bg-transparent hover:bg-zinc-200/50 dark:hover:bg-dark-300": tab() !== props.tab, 34 + "rounded-full p-1.5": true, 35 + "bg-neutral-700 dark:bg-neutral-200": activePlcEvent() === props.event, 48 36 }} 49 - onclick={() => setTab(props.tab)} 37 + onclick={() => setActivePlcEvent(activePlcEvent() === props.event ? undefined : props.event)} 50 38 > 51 - {props.label} 39 + <div 40 + class={`${props.icon} text-xl ${activePlcEvent() === props.event ? "text-slate-100 dark:text-slate-900" : ""}`} 41 + /> 52 42 </button> 53 43 ); 54 44 ··· 64 54 } else if (diff.type === "identity_tombstoned") { 65 55 icon = "i-lucide-skull"; 66 56 title = `Identity tombstoned`; 67 - } else if (diff.type === "handle_added") { 57 + } else if (diff.type === "handle_added" || diff.type === "handle_removed") { 68 58 icon = "i-lucide-at-sign"; 69 - title = "Alias added"; 59 + title = diff.type === "handle_added" ? "Alias added" : "Alias removed"; 70 60 value = diff.handle; 71 61 } else if (diff.type === "handle_changed") { 72 62 icon = "i-lucide-at-sign"; 73 63 title = "Alias updated"; 74 64 value = `${diff.prev_handle} → ${diff.next_handle}`; 75 - } else if (diff.type === "handle_removed") { 76 - icon = "i-lucide-at-sign"; 77 - title = `Alias removed`; 78 - value = diff.handle; 79 - } else if (diff.type === "rotation_key_added") { 65 + } else if (diff.type === "rotation_key_added" || diff.type === "rotation_key_removed") { 80 66 icon = "i-lucide-key-round"; 81 - title = `Rotation key added`; 82 - value = diff.rotation_key; 83 - } else if (diff.type === "rotation_key_removed") { 84 - icon = "i-lucide-key-round"; 85 - title = `Rotation key removed`; 67 + title = diff.type === "rotation_key_added" ? "Rotation key added" : "Rotation key removed"; 86 68 value = diff.rotation_key; 87 - } else if (diff.type === "service_added") { 69 + } else if (diff.type === "service_added" || diff.type === "service_removed") { 88 70 icon = "i-lucide-server"; 89 - title = `Service ${diff.service_id} added`; 71 + title = `Service ${diff.service_id} ${diff.type === "service_added" ? "added" : "removed"}`; 90 72 value = `${diff.service_endpoint}`; 91 73 } else if (diff.type === "service_changed") { 92 74 icon = "i-lucide-server"; 93 75 title = `Service ${diff.service_id} updated`; 94 76 value = `${diff.prev_service_endpoint} → ${diff.next_service_endpoint}`; 95 - } else if (diff.type === "service_removed") { 96 - icon = "i-lucide-server"; 97 - title = `Service ${diff.service_id} removed`; 98 - value = `${diff.service_endpoint}`; 99 - } else if (diff.type === "verification_method_added") { 77 + } else if ( 78 + diff.type === "verification_method_added" || 79 + diff.type === "verification_method_removed" 80 + ) { 100 81 icon = "i-lucide-shield-check"; 101 - title = `Verification method ${diff.method_id} added`; 82 + title = `Verification method ${diff.method_id} ${diff.type === "verification_method_added" ? "added" : "removed"}`; 102 83 value = `${diff.method_key}`; 103 84 } else if (diff.type === "verification_method_changed") { 104 85 icon = "i-lucide-shield-check"; 105 86 title = `Verification method ${diff.method_id} updated`; 106 87 value = `${diff.prev_method_key} → ${diff.next_method_key}`; 107 - } else if (diff.type === "verification_method_removed") { 108 - icon = "i-lucide-shield-check"; 109 - title = `Verification method ${diff.method_id} removed`; 110 - value = `${diff.method_key}`; 111 88 } 112 89 113 90 return ( ··· 126 103 </div> 127 104 ); 128 105 }; 106 + 107 + return ( 108 + <> 109 + <div class="flex items-center gap-1"> 110 + <Tooltip text="Filter operations"> 111 + <div class="i-lucide-filter text-xl" /> 112 + </Tooltip> 113 + <div class="dark:shadow-dark-900 flex w-fit items-center rounded-full bg-neutral-200 shadow-md dark:bg-neutral-700"> 114 + <FilterButton icon="i-lucide-at-sign" event="handle" /> 115 + <FilterButton icon="i-lucide-key-round" event="rotation_key" /> 116 + <FilterButton icon="i-lucide-server" event="service" /> 117 + <FilterButton icon="i-lucide-shield-check" event="verification_method" /> 118 + </div> 119 + </div> 120 + <div class="flex flex-col gap-1 text-sm"> 121 + <For each={props.plcOps}> 122 + {([entry, diffs]) => ( 123 + <Show 124 + when={!activePlcEvent() || diffs.find((d) => d.type.startsWith(activePlcEvent()!))} 125 + > 126 + <div class="flex flex-col"> 127 + <span class="text-neutral-500 dark:text-neutral-400"> 128 + {localDateFromTimestamp(new Date(entry.createdAt).getTime())} 129 + </span> 130 + {diffs.map((diff) => ( 131 + <Show when={!activePlcEvent() || diff.type.startsWith(activePlcEvent()!)}> 132 + <DiffItem diff={diff} /> 133 + </Show> 134 + ))} 135 + </div> 136 + </Show> 137 + )} 138 + </For> 139 + </div> 140 + </> 141 + ); 142 + }; 143 + 144 + const RepoView = () => { 145 + const params = useParams(); 146 + const [error, setError] = createSignal<string>(); 147 + const [downloading, setDownloading] = createSignal(false); 148 + const [didDoc, setDidDoc] = createSignal<DidDocument>(); 149 + const [backlinks, setBacklinks] = createSignal<{ links: LinkData; target: string }>(); 150 + const [nsids, setNsids] = createSignal<Record<string, { hidden: boolean; nsids: string[] }>>(); 151 + const [tab, setTab] = createSignal<Tab>("collections"); 152 + const [filter, setFilter] = createSignal<string>(); 153 + const [plcOps, setPlcOps] = 154 + createSignal<[IndexedEntry<CompatibleOperationOrTombstone>, DiffEntry[]][]>(); 155 + const [showPlcLogs, setShowPlcLogs] = createSignal(false); 156 + const [loading, setLoading] = createSignal(false); 157 + let rpc: Client; 158 + let pds: string; 159 + const did = params.repo; 160 + 161 + const RepoTab = (props: { tab: Tab; label: string }) => ( 162 + <button 163 + classList={{ 164 + "rounded-lg flex flex-1 py-1 justify-center": true, 165 + "bg-zinc-200/70 dark:bg-dark-200 shadow-sm dark:shadow-dark-900": tab() === props.tab, 166 + "bg-transparent hover:bg-zinc-200/50 dark:hover:bg-dark-300": tab() !== props.tab, 167 + }} 168 + onclick={() => setTab(props.tab)} 169 + > 170 + {props.label} 171 + </button> 172 + ); 129 173 130 174 const fetchRepo = async () => { 131 175 pds = await resolvePDS(did); ··· 421 465 </Show> 422 466 </div> 423 467 <Show when={showPlcLogs()}> 424 - <div class="flex flex-col gap-1 text-sm"> 425 - <For each={plcOps()}> 426 - {([entry, diffs]) => ( 427 - <div class="flex flex-col"> 428 - <span class="text-neutral-500 dark:text-neutral-400"> 429 - {localDateFromTimestamp(new Date(entry.createdAt).getTime())} 430 - </span> 431 - {diffs.map((diff) => ( 432 - <DiffItem diff={diff} /> 433 - ))} 434 - </div> 435 - )} 436 - </For> 437 - </div> 468 + <PlcLogView plcOps={plcOps() ?? []} /> 438 469 </Show> 439 470 </div> 440 471 )}