feat: redesign dashboard layout with calendar, open sessions, and glass-card panels

New layout: greeting → calendar+actions → sessions+stats → activity
Replaces old QuickStats and SessionsPanel with new dashboard components

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-03-03 08:31:27 -05:00
parent 396453c18a
commit f0bfa08df1

View File

@@ -12,8 +12,7 @@ import { usePinnedFlowsStore } from '@/store/pinnedFlowsStore'
import { useUserPreferencesStore } from '@/store/userPreferencesStore'
import { usePaginationParams } from '@/hooks/usePaginationParams'
import { useCachedQuota } from '@/hooks/useCachedQuota'
import { QuickStats } from '@/components/dashboard/QuickStats'
import { SessionsPanel } from '@/components/dashboard/SessionsPanel'
// QuickStats and SessionsPanel replaced by new dashboard panels
import { TreeGridView } from '@/components/library/TreeGridView'
import { TreeListView } from '@/components/library/TreeListView'
import { TreeTableView } from '@/components/library/TreeTableView'
@@ -22,6 +21,10 @@ import { AIFlowBuilderModal } from '@/components/ai-builder/AIFlowBuilderModal'
import { CreateFlowDropdown } from '@/components/common/CreateFlowDropdown'
import { cn } from '@/lib/utils'
import { toast } from '@/lib/toast'
import { WeeklyCalendar } from '@/components/dashboard/WeeklyCalendar'
import { QuickActions } from '@/components/dashboard/QuickActions'
import { OpenSessions } from '@/components/dashboard/OpenSessions'
import { RecentActivity } from '@/components/dashboard/RecentActivity'
function timeAgo(dateStr: string): string {
const now = Date.now()
@@ -215,16 +218,22 @@ export function QuickStartPage() {
const now = new Date()
return d.toDateString() === now.toDateString()
}).length
const completedSessions = allSessions.filter(s => s.completed_at).length
// completedSessions removed — no longer displayed in new layout
const recentSessionItems = allSessions.slice(0, 5).map(s => ({
// Open sessions for the new panel (3 oldest)
const openSessionItems = activeSessions
.sort((a, b) => new Date(a.started_at).getTime() - new Date(b.started_at).getTime())
.slice(0, 3)
.map(s => ({
id: s.id,
treeName: s.tree_snapshot?.name || 'Unknown',
status: (s.completed_at ? 'completed' : 'in_progress') as 'completed' | 'in_progress',
ticketNumber: s.ticket_number || undefined,
treeId: s.tree_id,
treeType: (s.tree_snapshot as unknown as Record<string, unknown>)?.tree_type as string | undefined,
timeAgo: timeAgo(s.started_at),
}))
// recentSessionItems removed — replaced by RecentActivity component
// Favorites display
const MAX_VISIBLE_FAVORITES = 8
const visibleFavorites = showAllFavorites ? pinnedItems : pinnedItems.slice(0, MAX_VISIBLE_FAVORITES)
@@ -270,27 +279,61 @@ export function QuickStartPage() {
return (
<div className="p-6 space-y-6">
{/* Page Header */}
<div className="flex items-start justify-between">
<div>
<h1 className="font-heading text-[1.375rem] font-bold tracking-tight text-foreground">
Dashboard
{/* Greeting */}
<div className="fade-in" style={{ animationDelay: '100ms' }}>
<h1 className="font-heading text-4xl font-extrabold tracking-tight text-foreground">
Good {new Date().getHours() < 12 ? 'morning' : new Date().getHours() < 18 ? 'afternoon' : 'evening'}, {user?.name?.split(' ')[0] || 'there'}
</h1>
<p className="mt-1 text-sm text-muted-foreground">
Welcome back. Here&apos;s what&apos;s happening with your flows.
{new Date().toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' })}
</p>
</div>
{/* Row 1: Calendar + Quick Actions */}
<div className="flex gap-4" style={{ alignItems: 'stretch' }}>
<div className="flex-1 min-w-0">
<WeeklyCalendar />
</div>
<div className="w-72 shrink-0">
<QuickActions />
</div>
</div>
{/* Quick Stats */}
<QuickStats
stats={[
{ label: 'My Flows', value: myFlows.length, gradient: true },
{ label: 'Sessions Today', value: todaySessions, color: '#f59e0b' },
{ label: 'Open Sessions', value: openSessions, meta: `${completedSessions} completed` },
{/* Row 2: Open Sessions + Stats 2x2 */}
<div className="flex gap-4" style={{ alignItems: 'stretch' }}>
<div className="flex-1 min-w-0">
<OpenSessions sessions={openSessionItems} />
</div>
<div className="w-72 shrink-0">
<div className="grid grid-cols-2 gap-3 h-full">
{[
{ label: 'Active Flows', value: myFlows.length, gradient: true, glow: true },
{ label: 'This Week', value: todaySessions },
{ label: 'Open Sessions', value: openSessions },
{ label: 'Favorites', value: pinnedItems.length },
]}
/>
].map((stat, i) => (
<div
key={stat.label}
className={cn('glass-card p-4 flex flex-col justify-between fade-in', stat.glow && 'active-glow')}
style={{ animationDelay: `${500 + i * 70}ms` }}
>
<p className="font-label text-[0.625rem] font-medium uppercase tracking-[0.1em] text-muted-foreground">
{stat.label}
</p>
<p className={cn('font-heading text-2xl font-extrabold tracking-tight', stat.gradient && 'text-gradient-brand')}>
{stat.value}
</p>
</div>
))}
</div>
</div>
</div>
{/* Row 3: Recent Activity */}
<RecentActivity />
{/* ── Existing content below ── */}
<div style={{ borderTop: '1px solid var(--glass-border)' }} className="pt-6 space-y-6">
{/* Search */}
<div ref={searchRef} className="relative">
@@ -332,9 +375,6 @@ export function QuickStartPage() {
)}
</div>
{/* Recent Sessions */}
<SessionsPanel sessions={recentSessionItems} delay={150} />
{/* Favorites Section */}
<div>
<div className="mb-3 flex items-center justify-between">
@@ -562,6 +602,7 @@ export function QuickStartPage() {
</>
)}
</div>
</div>
{/* Fork Modal */}
{forkTarget && (