···
export const PlcLogView = (props: { did: string }) => {
const [activePlcEvent, setActivePlcEvent] = createSignal<PlcEvent | undefined>();
+
const shouldShowDiff = (diff: DiffEntry) =>
+
!activePlcEvent() || diff.type.startsWith(activePlcEvent()!);
+
const shouldShowEntry = (diffs: DiffEntry[]) =>
+
!activePlcEvent() || diffs.some((d) => d.type.startsWith(activePlcEvent()!));
const fetchPlcLogs = async () => {
`${localStorage.plcDirectory ?? "https://plc.directory"}/${props.did}/log/audit`,
···
createResource<[IndexedEntry<CompatibleOperationOrTombstone>, DiffEntry[]][]>(fetchPlcLogs);
+
const FilterButton = (props: { icon: string; event: PlcEvent; label: string }) => {
+
const isActive = () => activePlcEvent() === props.event;
+
const toggleFilter = () => setActivePlcEvent(isActive() ? undefined : props.event);
+
"flex items-center gap-1 sm:gap-1.5 rounded-lg px-2 py-1.5 text-xs sm:text-sm transition-colors": true,
+
"bg-neutral-700 text-white dark:bg-neutral-200 dark:text-neutral-900": isActive(),
+
"bg-neutral-200 text-neutral-700 hover:bg-neutral-300 dark:bg-neutral-700 dark:text-neutral-300 dark:hover:bg-neutral-600":
+
<span class={props.icon}></span>
+
<span class="font-medium">{props.label}</span>
const DiffItem = (props: { diff: DiffEntry }) => {
+
const getDiffConfig = () => {
+
case "identity_created":
+
return { icon: "lucide--bell", title: "Identity created" };
+
case "identity_tombstoned":
+
return { icon: "lucide--skull", title: "Identity tombstoned" };
+
icon: "lucide--at-sign",
+
icon: "lucide--at-sign",
+
title: "Alias removed",
+
icon: "lucide--at-sign",
+
title: "Alias updated",
+
oldValue: diff.prev_handle,
+
newValue: diff.next_handle,
+
case "rotation_key_added":
+
icon: "lucide--key-round",
+
title: "Rotation key added",
+
value: diff.rotation_key,
+
case "rotation_key_removed":
+
icon: "lucide--key-round",
+
title: "Rotation key removed",
+
value: diff.rotation_key,
+
icon: "lucide--hard-drive",
+
title: "Service added",
+
badge: diff.service_id,
+
value: diff.service_endpoint,
+
case "service_removed":
+
icon: "lucide--hard-drive",
+
title: "Service removed",
+
badge: diff.service_id,
+
value: diff.service_endpoint,
+
case "service_changed":
+
icon: "lucide--hard-drive",
+
title: "Service updated",
+
badge: diff.service_id,
+
oldValue: diff.prev_service_endpoint,
+
newValue: diff.next_service_endpoint,
+
case "verification_method_added":
+
icon: "lucide--shield-check",
+
title: "Verification method added",
+
value: diff.method_key,
+
case "verification_method_removed":
+
icon: "lucide--shield-check",
+
title: "Verification method removed",
+
value: diff.method_key,
+
case "verification_method_changed":
+
icon: "lucide--shield-check",
+
title: "Verification method updated",
+
oldValue: diff.prev_method_key,
+
newValue: diff.next_method_key,
+
return { icon: "lucide--circle-help", title: "Unknown log entry" };
+
const config = getDiffConfig();
+
"grid grid-cols-[auto_1fr] gap-x-2 gap-y-1": true,
+
"opacity-60": diff.orig.nullified,
+
<div class={`${icon} iconify shrink-0 self-center`} />
+
<div class="flex min-w-0 items-center gap-1.5">
+
"font-semibold text-sm": true,
+
"line-through": diff.orig.nullified,
+
<span class="shrink-0 rounded bg-neutral-200 px-1.5 py-0.5 text-xs font-medium dark:bg-neutral-700">
+
<Show when={diff.orig.nullified}>
+
<span class="ml-auto rounded bg-neutral-200 px-2 py-0.5 text-xs font-medium dark:bg-neutral-700">
+
"text-sm break-all flex items-start gap-2 min-w-0": true,
+
"text-green-600 dark:text-green-400": isAddition,
+
"text-red-600 dark:text-red-400": isRemoval,
+
"text-neutral-600 dark:text-neutral-400": !isAddition && !isRemoval,
+
<Show when={isAddition}>
+
<span class="shrink-0">+</span>
+
<Show when={isRemoval}>
+
<span class="shrink-0">−</span>
+
<span class="break-all">{value}</span>
+
<Show when={oldValue && newValue}>
+
<div class="flex min-w-0 flex-col gap-1 text-sm">
+
<div class="flex items-start gap-2 text-red-600 dark:text-red-400">
+
<span class="shrink-0">−</span>
+
<span class="break-all">{oldValue}</span>
+
<div class="flex items-start gap-2 text-green-600 dark:text-green-400">
+
<span class="shrink-0">+</span>
+
<span class="break-all">{newValue}</span>
+
<div class="flex w-full flex-col gap-4 wrap-anywhere">
+
<div class="flex flex-col gap-2">
+
<div class="flex items-center gap-1.5 text-sm">
+
<div class="iconify lucide--filter" />
+
<p class="font-semibold">Filter by type</p>
+
<div class="flex flex-wrap gap-1 sm:gap-2">
+
<FilterButton icon="iconify lucide--at-sign" event="handle" label="Alias" />
+
icon="iconify lucide--key-round"
+
<FilterButton icon="iconify lucide--hard-drive" event="service" label="Service" />
+
icon="iconify lucide--shield-check"
+
event="verification_method"
+
<div class="flex flex-col gap-3">
+
<Show when={shouldShowEntry(diffs)}>
+
<div class="flex flex-col gap-2">
+
<div class="flex items-center gap-2 text-sm">
+
<div class="iconify lucide--clock text-neutral-600 dark:text-neutral-400" />
+
<span class="font-medium text-neutral-700 dark:text-neutral-300">
+
{localDateFromTimestamp(new Date(entry.createdAt).getTime())}
+
<div class="flex flex-col gap-2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-3 text-sm dark:border-neutral-700 dark:bg-neutral-800">
+
<For each={diffs.filter(shouldShowDiff)}>
+
{(diff) => <DiffItem diff={diff} />}