
A live admin activity feed that streams new events, with session stats and a pause control.
@esuiss
components/blocks/log-live.tsx
"use client"
import * as React from "react"
import { IconPlaceholder } from "@/app/(create)/components/icon-placeholder"
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarInset,
SidebarMenu,
SidebarMenuBadge,
SidebarMenuButton,
SidebarMenuItem,
SidebarProvider,
SidebarRail,
SidebarTrigger,
useSidebar,
} from "@/components/ui/sidebar"
type NavItem = {
value: string
label: string
icon: React.ReactNode
badge?: React.ReactNode
group?: string
}
function NavSidebar({
items,
value: valueProp,
defaultValue,
onValueChange,
header,
footer,
}: {
items: NavItem[]
value?: string
defaultValue?: string
onValueChange?: (value: string) => void
header?: React.ReactNode
footer?: React.ReactNode
}) {
const { isMobile, setOpenMobile } = useSidebar()
const [uncontrolledValue, setUncontrolledValue] = React.useState(
defaultValue ?? items[0]?.value
)
const value = valueProp ?? uncontrolledValue
const groups = [...new Set(items.map((item) => item.group ?? ""))]
return (
<Sidebar collapsible="icon">
{header && (
<SidebarHeader className="group-data-[collapsible=icon]:hidden">
{header}
</SidebarHeader>
)}
<SidebarContent>
{groups.map((group) => (
<SidebarGroup key={group}>
{group && <SidebarGroupLabel>{group}</SidebarGroupLabel>}
<SidebarGroupContent>
<SidebarMenu>
{items
.filter((item) => (item.group ?? "") === group)
.map((item) => (
<SidebarMenuItem key={item.value}>
<SidebarMenuButton
isActive={item.value === value}
tooltip={item.label}
aria-current={item.value === value ? "page" : undefined}
onClick={() => {
if (valueProp === undefined) {
setUncontrolledValue(item.value)
}
onValueChange?.(item.value)
if (isMobile) setOpenMobile(false)
}}
>
{item.icon}
<span>{item.label}</span>
</SidebarMenuButton>
{item.badge !== undefined && (
<SidebarMenuBadge>{item.badge}</SidebarMenuBadge>
)}
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
))}
</SidebarContent>
{footer && (
<SidebarFooter className="group-data-[collapsible=icon]:hidden">
{footer}
</SidebarFooter>
)}
<SidebarRail />
</Sidebar>
)
}
const nav: NavItem[] = [
{
value: "users",
label: "Users",
icon: (
<IconPlaceholder
lucide="UsersIcon"
tabler="IconUsers"
hugeicons="UserGroupIcon"
phosphor="UsersIcon"
remixicon="RiGroupLine"
/>
),
},
{
value: "roles",
label: "Roles",
icon: (
<IconPlaceholder
lucide="ShieldIcon"
tabler="IconShield"
hugeicons="Shield01Icon"
phosphor="ShieldIcon"
remixicon="RiShieldLine"
/>
),
},
{
value: "log",
label: "Activity",
icon: (
<IconPlaceholder
lucide="ChartColumnIcon"
tabler="IconChartBar"
hugeicons="ChartColumnIcon"
phosphor="ChartBarIcon"
remixicon="RiBarChartLine"
/>
),
},
{
value: "settings",
label: "Settings",
icon: (
<IconPlaceholder
lucide="SettingsIcon"
tabler="IconSettings"
hugeicons="Settings01Icon"
phosphor="GearIcon"
remixicon="RiSettingsLine"
/>
),
},
]
const feed = [
{
actor: "Jamie Rivera",
action: "signed in",
detail: "Book Pro · San Francisco",
hue: 250,
},
{
actor: "System",
action: "finished a backup",
detail: "12.4 GB · 3 min",
icon: (
<IconPlaceholder
lucide="CloudIcon"
tabler="IconCloud"
hugeicons="CloudIcon"
phosphor="CloudIcon"
remixicon="RiCloudLine"
className="size-4"
/>
),
},
{
actor: "Morgan Lee",
action: "updated a product",
detail: "Phone Air · 240 in stock",
hue: 30,
icon: (
<IconPlaceholder
lucide="TagIcon"
tabler="IconTag"
hugeicons="Tag01Icon"
phosphor="TagIcon"
remixicon="RiPriceTag3Line"
className="size-4"
/>
),
},
{
actor: "Riley Chen",
action: "answered a ticket",
detail: "#4821 Screen flickers",
hue: 150,
icon: (
<IconPlaceholder
lucide="MessageCircleIcon"
tabler="IconMessageCircle"
hugeicons="BubbleChatIcon"
phosphor="ChatCircleIcon"
remixicon="RiChat1Line"
className="size-4"
/>
),
},
{
actor: "System",
action: "raised a warning",
detail: "API latency 820 ms",
icon: (
<IconPlaceholder
lucide="TriangleAlertIcon"
tabler="IconAlertTriangle"
hugeicons="Alert02Icon"
phosphor="WarningIcon"
remixicon="RiAlertLine"
className="size-4"
/>
),
warning: true,
},
{
actor: "Jordan Park",
action: "approved an order",
detail: "W1052 · $399",
hue: 100,
icon: (
<IconPlaceholder
lucide="HandbagIcon"
tabler="IconShoppingBag"
hugeicons="ShoppingBag01Icon"
phosphor="HandbagIcon"
remixicon="RiHandbagLine"
className="size-4"
/>
),
},
]
type Entry = { id: number; time: string; event: (typeof feed)[number] }
export function LogLive() {
const [entries, setEntries] = React.useState<Entry[]>([])
const [running, setRunning] = React.useState(true)
const [count, setCount] = React.useState(0)
const next = React.useRef(0)
React.useEffect(() => {
if (!running) return
const push = () => {
const id = next.current++
const event = feed[id % feed.length]
if (!event) return
const time = new Date().toLocaleTimeString("en-US", {
hour: "numeric",
minute: "2-digit",
})
setCount((current) => current + 1)
setEntries((current) => [{ id, time, event }, ...current].slice(0, 7))
}
if (next.current === 0) push()
const timer = window.setInterval(push, 2200)
return () => window.clearInterval(timer)
}, [running])
const stats = [
{
label: "This session",
value: `${count} ${count === 1 ? "event" : "events"}`,
},
{ label: "Active users", value: "14" },
{ label: "Avg. response", value: "182 ms" },
]
return (
<SidebarProvider>
<NavSidebar
items={nav}
defaultValue="log"
header={
<p className="flex items-center gap-2 px-2 pt-1 text-sm font-semibold">
<span className="font-bold">suiss</span> Admin
</p>
}
/>
<SidebarInset>
<div className="flex flex-col gap-5 px-5 py-5.5 md:px-7">
<div className="flex items-start gap-3">
<SidebarTrigger className="-ms-2 mt-0.5" />
<div className="flex flex-1 flex-col">
<h1 className="text-3xl font-semibold tracking-tight">
Live activity
</h1>
<p className="text-sm text-muted-foreground">
{running ? "Events appear as they happen" : "Paused"}
</p>
</div>
<Button
size="sm"
variant="secondary"
onClick={() => setRunning(!running)}
className="gap-1.5"
>
{running ? (
<IconPlaceholder
lucide="PauseIcon"
tabler="IconPlayerPause"
hugeicons="PauseIcon"
phosphor="PauseIcon"
remixicon="RiPauseLine"
className="size-3 fill-current"
/>
) : (
<IconPlaceholder
lucide="PlayIcon"
tabler="IconPlayerPlay"
hugeicons="PlayIcon"
phosphor="PlayIcon"
remixicon="RiPlayLine"
className="size-3 fill-current"
/>
)}
{running ? "Pause" : "Resume"}
</Button>
</div>
<dl className="grid grid-cols-3 overflow-hidden rounded-xl bg-muted">
{stats.map((stat) => (
<div
key={stat.label}
className="flex min-w-0 flex-col px-3 py-3 not-first:border-s not-first:border-border md:px-4"
>
<dt className="truncate text-xs text-muted-foreground">
{stat.label}
</dt>
<dd className="text-xl font-semibold tabular-nums md:text-2xl">
{stat.value}
</dd>
</div>
))}
</dl>
<div className="-mb-2.5 flex items-center gap-2 px-1">
<h2 className="flex-1 text-sm font-semibold">Recent events</h2>
{running && (
<span className="flex items-center gap-1.5 text-xs text-muted-foreground">
<span className="size-1.75 animate-pulse rounded-full bg-green-500" />
Live
</span>
)}
</div>
<ul
aria-live="polite"
aria-relevant="additions"
className="overflow-hidden rounded-2xl bg-muted"
>
{entries.map(({ id, time, event }) => (
<li
key={id}
className="relative flex min-h-11 items-center gap-3 px-4 py-2 transition-[opacity,translate] duration-700 ease-[cubic-bezier(0.32,0.72,0,1)] not-first:before:absolute not-first:before:inset-e-0 not-first:before:inset-s-14 not-first:before:top-0 not-first:before:h-px not-first:before:bg-border starting:-translate-y-2.5 starting:opacity-0 motion-reduce:transition-none"
>
{event.hue ? (
<Avatar className="size-7">
<AvatarFallback
style={{
background: `linear-gradient(160deg, oklch(0.78 0.1 ${event.hue}), oklch(0.6 0.14 ${event.hue + 30}))`,
}}
className="text-xs text-white"
>
{event.actor
.split(" ")
.map((part) => part[0])
.join("")}
</AvatarFallback>
</Avatar>
) : (
<span
data-warning={event.warning ? "" : undefined}
className="flex size-7 shrink-0 items-center justify-center rounded-[7px] bg-zinc-500 text-white data-warning:bg-orange-500"
>
{event.icon}
</span>
)}
<span className="flex min-w-0 flex-1 flex-col">
<span className="truncate text-sm">
<strong className="font-semibold">{event.actor}</strong>{" "}
{event.action}
</span>
<span className="truncate text-xs text-muted-foreground">
{event.detail}
</span>
</span>
<span className="text-sm text-muted-foreground tabular-nums">
{time}
</span>
</li>
))}
</ul>
</div>
</SidebarInset>
</SidebarProvider>
)
}

























