feat(bb-ui2): Social and Swarm views with detail panels integrated
This commit is contained in:
parent
976fd0c361
commit
8dd2d01686
11 changed files with 622 additions and 64 deletions
116
src/components/social/social-detail.tsx
Normal file
116
src/components/social/social-detail.tsx
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
'use client';
|
||||
|
||||
import type { SocialCard as SocialCardData, AgentStatus } from '../../lib/social-cards';
|
||||
import { StatusBadge } from '../shared/status-badge';
|
||||
import { AgentAvatar } from '../shared/agent-avatar';
|
||||
import { Plus } from 'lucide-react';
|
||||
|
||||
interface SocialDetailProps {
|
||||
data: SocialCardData;
|
||||
}
|
||||
|
||||
function formatRelativeTime(date: Date): string {
|
||||
const now = new Date();
|
||||
const diffMs = now.getTime() - date.getTime();
|
||||
const diffSecs = Math.floor(diffMs / 1000);
|
||||
const diffMins = Math.floor(diffSecs / 60);
|
||||
const diffHours = Math.floor(diffMins / 60);
|
||||
const diffDays = Math.floor(diffHours / 24);
|
||||
|
||||
if (diffSecs < 60) return 'just now';
|
||||
if (diffMins < 60) return `${diffMins}m ago`;
|
||||
if (diffHours < 24) return `${diffHours}h ago`;
|
||||
if (diffDays < 7) return `${diffDays}d ago`;
|
||||
return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
|
||||
}
|
||||
|
||||
export function SocialDetail({ data }: SocialDetailProps) {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<span className="text-teal-400 font-mono text-sm font-medium">
|
||||
{data.id}
|
||||
</span>
|
||||
<h2 className="text-text-primary font-semibold text-base leading-tight">
|
||||
{data.title}
|
||||
</h2>
|
||||
<StatusBadge status={data.status} size="sm" />
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<h3 className="text-text-muted text-xs font-semibold uppercase tracking-wider">
|
||||
Thread
|
||||
</h3>
|
||||
<p className="text-text-muted text-sm italic">
|
||||
Thread placeholder (bb-ui2.13)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{data.blocks.length > 0 && (
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-amber-400 text-xs font-semibold uppercase tracking-wider">
|
||||
Blocks
|
||||
</h3>
|
||||
<ul className="space-y-1">
|
||||
{data.blocks.map((id) => (
|
||||
<li key={id} className="text-text-secondary text-sm font-mono">
|
||||
{id}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{data.unlocks.length > 0 && (
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-emerald-400 text-xs font-semibold uppercase tracking-wider">
|
||||
Unlocks
|
||||
</h3>
|
||||
<ul className="space-y-1">
|
||||
{data.unlocks.map((id) => (
|
||||
<li key={id} className="text-text-secondary text-sm font-mono">
|
||||
{id}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-text-muted text-xs font-semibold uppercase tracking-wider">
|
||||
Assigned
|
||||
</h3>
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
{data.agents.length > 0 ? (
|
||||
data.agents.map((agent) => (
|
||||
<AgentAvatar
|
||||
key={agent.name}
|
||||
name={agent.name}
|
||||
status={agent.status as AgentStatus}
|
||||
size="sm"
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<span className="text-text-muted text-sm">No agents</span>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className="p-1.5 rounded-md border border-dashed border-white/20 hover:border-white/40 hover:bg-white/5 transition-colors"
|
||||
aria-label="Add agent"
|
||||
>
|
||||
<Plus size={14} className="text-text-muted" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1 pt-2 border-t border-white/10">
|
||||
<h3 className="text-text-muted text-xs font-semibold uppercase tracking-wider">
|
||||
Last Activity
|
||||
</h3>
|
||||
<p className="text-text-secondary text-sm">
|
||||
{formatRelativeTime(data.lastActivity)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
65
src/components/social/social-page.tsx
Normal file
65
src/components/social/social-page.tsx
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
'use client';
|
||||
|
||||
import { useMemo, useState } from 'react';
|
||||
import type { BeadIssue } from '../../lib/types';
|
||||
import { buildSocialCards } from '../../lib/social-cards';
|
||||
import { SocialCard } from './social-card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ChevronDown } from 'lucide-react';
|
||||
|
||||
const INITIAL_LIMIT = 16; // 4x4 grid
|
||||
|
||||
interface SocialPageProps {
|
||||
issues: BeadIssue[];
|
||||
selectedId?: string;
|
||||
onSelect: (id: string) => void;
|
||||
}
|
||||
|
||||
export function SocialPage({ issues, selectedId, onSelect }: SocialPageProps) {
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const cards = useMemo(() => buildSocialCards(issues), [issues]);
|
||||
const visibleCards = expanded ? cards : cards.slice(0, INITIAL_LIMIT);
|
||||
const hasMore = cards.length > INITIAL_LIMIT;
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
<div
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(4, 1fr)',
|
||||
gap: '1rem',
|
||||
maxWidth: '1200px',
|
||||
margin: '0 auto',
|
||||
}}
|
||||
>
|
||||
{visibleCards.map((card) => (
|
||||
<SocialCard
|
||||
key={card.id}
|
||||
data={card}
|
||||
selected={selectedId === card.id}
|
||||
onClick={() => onSelect(card.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{hasMore && (
|
||||
<div className="flex justify-center mt-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setExpanded(true)}
|
||||
className="gap-2 border-white/10 bg-white/5 hover:bg-white/10"
|
||||
>
|
||||
Show {cards.length - INITIAL_LIMIT} more
|
||||
<ChevronDown className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{cards.length === 0 && (
|
||||
<div className="text-center py-12" style={{ color: 'var(--color-text-muted)' }}>
|
||||
No tasks found.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue