feat: add public SharedSessionPage with tree preview
Add the public-facing shared session page at /share/:shareToken that renders shared sessions without authentication. Includes error handling for 401 (redirect to login), 403 (access denied), 404 (not found), and 410 (expired). The page features a minimal header, session metadata, SessionTimeline component, and a new SharedSessionTreePreview component that renders the decision tree structure with the path taken highlighted. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
88
frontend/src/components/session/SharedSessionTreePreview.tsx
Normal file
88
frontend/src/components/session/SharedSessionTreePreview.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
interface SharedSessionTreePreviewProps {
|
||||
treeStructure: Record<string, unknown>
|
||||
pathTaken: string[]
|
||||
}
|
||||
|
||||
const nodeTypeColors: Record<string, string> = {
|
||||
root: 'bg-white',
|
||||
decision: 'bg-blue-400',
|
||||
action: 'bg-yellow-400',
|
||||
solution: 'bg-emerald-400',
|
||||
information: 'bg-white/50',
|
||||
}
|
||||
|
||||
function getNodeTitle(node: Record<string, unknown>): string {
|
||||
return (
|
||||
(node.question as string) ||
|
||||
(node.title as string) ||
|
||||
(node.node_type as string) ||
|
||||
'Untitled'
|
||||
)
|
||||
}
|
||||
|
||||
function TreeNode({
|
||||
node,
|
||||
depth,
|
||||
pathTaken,
|
||||
}: {
|
||||
node: Record<string, unknown>
|
||||
depth: number
|
||||
pathTaken: string[]
|
||||
}) {
|
||||
const nodeId = (node.id as string) || ''
|
||||
const nodeType = (node.node_type as string) || 'decision'
|
||||
const isInPath = pathTaken.includes(nodeId)
|
||||
const children = (node.children as Record<string, unknown>[]) || []
|
||||
const colorClass = nodeTypeColors[nodeType] || 'bg-white/50'
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center gap-2 px-3 py-1.5 text-sm',
|
||||
isInPath
|
||||
? 'rounded-md border-l-2 border-white/40 bg-white/10 font-medium text-white'
|
||||
: 'text-white/30'
|
||||
)}
|
||||
style={{ paddingLeft: `${depth * 16 + 12}px` }}
|
||||
>
|
||||
<span
|
||||
className={cn('h-2 w-2 shrink-0 rounded-full', colorClass)}
|
||||
/>
|
||||
<span className="truncate">{getNodeTitle(node)}</span>
|
||||
</div>
|
||||
{children.map((child, index) => (
|
||||
<TreeNode
|
||||
key={(child.id as string) || index}
|
||||
node={child}
|
||||
depth={depth + 1}
|
||||
pathTaken={pathTaken}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function SharedSessionTreePreview({
|
||||
treeStructure,
|
||||
pathTaken,
|
||||
}: SharedSessionTreePreviewProps) {
|
||||
if (!treeStructure) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="glass-card rounded-2xl">
|
||||
<div className="sticky top-0 z-10 rounded-t-2xl border-b border-white/[0.06] bg-black/80 px-6 py-4 backdrop-blur">
|
||||
<h3 className="text-sm font-semibold text-white">Tree Structure</h3>
|
||||
</div>
|
||||
<div className="max-h-[600px] overflow-y-auto py-2">
|
||||
<TreeNode node={treeStructure} depth={0} pathTaken={pathTaken} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SharedSessionTreePreview
|
||||
Reference in New Issue
Block a user