diff --git a/frontend/src/app/[locale]/(authenticated)/page.tsx b/frontend/src/app/[locale]/(authenticated)/page.tsx
new file mode 100644
index 0000000..43e0755
--- /dev/null
+++ b/frontend/src/app/[locale]/(authenticated)/page.tsx
@@ -0,0 +1,23 @@
+/**
+ * Dashboard Page
+ *
+ * Main authenticated homepage showing:
+ * - Quick stats overview
+ * - Recent projects
+ * - Pending approvals
+ * - Real-time activity feed
+ *
+ * @see Issue #53
+ */
+
+import { Metadata } from 'next';
+import { Dashboard } from '@/components/dashboard';
+
+export const metadata: Metadata = {
+ title: 'Dashboard',
+ description: 'Overview of your projects, agents, and activity',
+};
+
+export default function DashboardPage() {
+ return ;
+}
diff --git a/frontend/src/components/dashboard/Dashboard.tsx b/frontend/src/components/dashboard/Dashboard.tsx
new file mode 100644
index 0000000..5c130ab
--- /dev/null
+++ b/frontend/src/components/dashboard/Dashboard.tsx
@@ -0,0 +1,131 @@
+/**
+ * Dashboard Component
+ *
+ * Main dashboard layout orchestrator.
+ * Combines all dashboard sub-components into a cohesive layout.
+ *
+ * Layout:
+ * +------------------------------------------+------------------+
+ * | Welcome Header | ACTIVITY |
+ * +------------------------------------------+ FEED |
+ * | Quick Stats (4 cards) | SIDEBAR |
+ * +------------------------------------------+ |
+ * | Recent Projects (3-6 cards) | |
+ * +------------------------------------------+ |
+ * | Pending Approvals (if any) | |
+ * +------------------------------------------+------------------+
+ *
+ * @see Issue #53
+ */
+
+'use client';
+
+import { useCallback } from 'react';
+import { toast } from 'sonner';
+import { Card } from '@/components/ui/card';
+import { ActivityFeed } from '@/components/activity/ActivityFeed';
+import { WelcomeHeader } from './WelcomeHeader';
+import { DashboardQuickStats } from './DashboardQuickStats';
+import { RecentProjects } from './RecentProjects';
+import { PendingApprovals } from './PendingApprovals';
+import { EmptyState } from './EmptyState';
+import { useDashboard, type PendingApproval } from '@/lib/api/hooks/useDashboard';
+import { useAuth } from '@/lib/auth/AuthContext';
+import { useProjectEvents } from '@/lib/hooks/useProjectEvents';
+import { useProjectEventsFromStore } from '@/lib/stores/eventStore';
+
+export interface DashboardProps {
+ /** Additional CSS classes */
+ className?: string;
+}
+
+export function Dashboard({ className }: DashboardProps) {
+ const { user } = useAuth();
+ const { data, isLoading, error } = useDashboard();
+
+ // Real-time events - using a generic project ID for dashboard-wide events
+ // In production, this would be a dedicated dashboard events endpoint
+ const { connectionState } = useProjectEvents('dashboard', {
+ autoConnect: true,
+ });
+ const events = useProjectEventsFromStore('dashboard');
+
+ // Get user's first name for empty state
+ const firstName = user?.first_name || user?.email?.split('@')[0] || 'there';
+
+ // Handle approval actions
+ const handleApprove = useCallback((approval: PendingApproval) => {
+ // TODO: Implement actual approval API call
+ toast.success(`Approved: ${approval.title}`, {
+ description: `${approval.projectName}`,
+ });
+ }, []);
+
+ const handleReject = useCallback((approval: PendingApproval) => {
+ // TODO: Implement actual rejection API call
+ toast.info(`Rejected: ${approval.title}`, {
+ description: `${approval.projectName}`,
+ });
+ }, []);
+
+ // Show error state
+ if (error) {
+ toast.error('Failed to load dashboard data', {
+ description: 'Please try refreshing the page',
+ });
+ }
+
+ // Check if user has no projects (empty state)
+ const hasNoProjects = !isLoading && (!data?.recentProjects || data.recentProjects.length === 0);
+
+ return (
+
+
+ {/* Welcome Header - always shown */}
+
+
+ {hasNoProjects ? (
+ // Empty state for new users
+
+ ) : (
+ // Main dashboard layout
+
+ {/* Main Content */}
+
+ {/* Quick Stats */}
+
+
+ {/* Recent Projects */}
+
+
+ {/* Pending Approvals */}
+
+
+
+ {/* Activity Feed Sidebar */}
+
+
+ )}
+
+
+ );
+}
diff --git a/frontend/src/components/dashboard/DashboardQuickStats.tsx b/frontend/src/components/dashboard/DashboardQuickStats.tsx
new file mode 100644
index 0000000..20ea65e
--- /dev/null
+++ b/frontend/src/components/dashboard/DashboardQuickStats.tsx
@@ -0,0 +1,63 @@
+/**
+ * DashboardQuickStats Component
+ *
+ * Displays quick stats cards for the dashboard:
+ * - Active Projects
+ * - Running Agents
+ * - Open Issues
+ * - Pending Approvals
+ *
+ * @see Issue #53
+ */
+
+'use client';
+
+import { Folder, Bot, CircleDot, AlertCircle } from 'lucide-react';
+import { StatCard } from '@/components/admin/StatCard';
+import type { DashboardStats } from '@/lib/api/hooks/useDashboard';
+
+export interface DashboardQuickStatsProps {
+ /** Stats data */
+ stats?: DashboardStats;
+ /** Whether data is loading */
+ isLoading?: boolean;
+ /** Additional CSS classes */
+ className?: string;
+}
+
+export function DashboardQuickStats({ stats, isLoading = false, className }: DashboardQuickStatsProps) {
+ return (
+
+ );
+}
diff --git a/frontend/src/components/dashboard/EmptyState.tsx b/frontend/src/components/dashboard/EmptyState.tsx
new file mode 100644
index 0000000..58e7cb5
--- /dev/null
+++ b/frontend/src/components/dashboard/EmptyState.tsx
@@ -0,0 +1,61 @@
+/**
+ * EmptyState Component
+ *
+ * Displays a welcome message for new users with no projects.
+ * Provides call-to-action to create first project.
+ *
+ * @see Issue #53
+ */
+
+'use client';
+
+import { Rocket, Bot, Settings } from 'lucide-react';
+import { Card, CardContent } from '@/components/ui/card';
+import { Button } from '@/components/ui/button';
+import { Link } from '@/lib/i18n/routing';
+
+export interface EmptyStateProps {
+ /** User's first name for personalization */
+ userName?: string;
+ /** Additional CSS classes */
+ className?: string;
+}
+
+export function EmptyState({ userName = 'there', className }: EmptyStateProps) {
+ return (
+
+
+
+
+
+
+ Welcome to Syndarix, {userName}!
+
+ Get started by creating your first project. Our AI agents will help you
+ turn your ideas into reality.
+
+
+
+
+
+
+
+ Set up AI agent types
+
+
+
+ Configure your account
+
+
+
+
+ );
+}
diff --git a/frontend/src/components/dashboard/PendingApprovals.tsx b/frontend/src/components/dashboard/PendingApprovals.tsx
new file mode 100644
index 0000000..91fbc23
--- /dev/null
+++ b/frontend/src/components/dashboard/PendingApprovals.tsx
@@ -0,0 +1,195 @@
+/**
+ * PendingApprovals Component
+ *
+ * Displays pending approval requests that need user attention.
+ * Only renders when there are approvals to show.
+ *
+ * @see Issue #53
+ */
+
+'use client';
+
+import { formatDistanceToNow } from 'date-fns';
+import {
+ AlertCircle,
+ CheckCircle2,
+ XCircle,
+ GitBranch,
+ Code2,
+ Building2,
+ Rocket,
+} from 'lucide-react';
+import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
+import { Button } from '@/components/ui/button';
+import { Badge } from '@/components/ui/badge';
+import { Skeleton } from '@/components/ui/skeleton';
+import { Link } from '@/lib/i18n/routing';
+import { cn } from '@/lib/utils';
+import type { PendingApproval } from '@/lib/api/hooks/useDashboard';
+
+export interface PendingApprovalsProps {
+ /** Pending approvals to display */
+ approvals?: PendingApproval[];
+ /** Whether data is loading */
+ isLoading?: boolean;
+ /** Callback when approval is approved */
+ onApprove?: (approval: PendingApproval) => void;
+ /** Callback when approval is rejected */
+ onReject?: (approval: PendingApproval) => void;
+ /** Additional CSS classes */
+ className?: string;
+}
+
+const typeConfig: Record<
+ PendingApproval['type'],
+ { icon: typeof GitBranch; label: string; color: string }
+> = {
+ sprint_boundary: {
+ icon: GitBranch,
+ label: 'Sprint Boundary',
+ color: 'text-blue-500',
+ },
+ code_review: {
+ icon: Code2,
+ label: 'Code Review',
+ color: 'text-purple-500',
+ },
+ architecture_decision: {
+ icon: Building2,
+ label: 'Architecture',
+ color: 'text-orange-500',
+ },
+ deployment: {
+ icon: Rocket,
+ label: 'Deployment',
+ color: 'text-green-500',
+ },
+};
+
+const priorityConfig: Record = {
+ low: { label: 'Low', variant: 'outline' },
+ medium: { label: 'Medium', variant: 'secondary' },
+ high: { label: 'High', variant: 'default' },
+ critical: { label: 'Critical', variant: 'destructive' },
+};
+
+function ApprovalSkeleton() {
+ return (
+
+ );
+}
+
+interface ApprovalItemProps {
+ approval: PendingApproval;
+ onApprove?: () => void;
+ onReject?: () => void;
+}
+
+function ApprovalItem({ approval, onApprove, onReject }: ApprovalItemProps) {
+ const config = typeConfig[approval.type];
+ const Icon = config.icon;
+ const priority = priorityConfig[approval.priority];
+
+ const timeAgo = formatDistanceToNow(new Date(approval.requestedAt), { addSuffix: true });
+
+ return (
+
+
+
+
+
+
+
+
{approval.title}
+
+ {priority.label}
+
+
+
{approval.description}
+
+
+ {approval.projectName}
+
+ -
+ Requested by {approval.requestedBy}
+ -
+ {timeAgo}
+
+
+
+
+
+
+
+
+ );
+}
+
+export function PendingApprovals({
+ approvals,
+ isLoading = false,
+ onApprove,
+ onReject,
+ className,
+}: PendingApprovalsProps) {
+ // Don't render if no approvals and not loading
+ if (!isLoading && (!approvals || approvals.length === 0)) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
Pending Approvals
+ {approvals && approvals.length > 0 && (
+
{approvals.length}
+ )}
+
+
+
+ {isLoading ? (
+ <>
+
+
+ >
+ ) : (
+ approvals?.map((approval) => (
+ onApprove?.(approval)}
+ onReject={() => onReject?.(approval)}
+ />
+ ))
+ )}
+
+
+ );
+}
diff --git a/frontend/src/components/dashboard/RecentProjects.tsx b/frontend/src/components/dashboard/RecentProjects.tsx
new file mode 100644
index 0000000..0185b98
--- /dev/null
+++ b/frontend/src/components/dashboard/RecentProjects.tsx
@@ -0,0 +1,152 @@
+/**
+ * RecentProjects Component
+ *
+ * Displays recent projects in a responsive grid with a "View all" link.
+ * Shows 3 projects on mobile, 6 on desktop.
+ *
+ * @see Issue #53
+ */
+
+'use client';
+
+import { ArrowRight, Bot, CircleDot, Clock } from 'lucide-react';
+import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
+import { Button } from '@/components/ui/button';
+import { Badge } from '@/components/ui/badge';
+import { Skeleton } from '@/components/ui/skeleton';
+import { Link } from '@/lib/i18n/routing';
+import { cn } from '@/lib/utils';
+import { ProjectStatusBadge } from '@/components/projects/StatusBadge';
+import { ProgressBar } from '@/components/projects/ProgressBar';
+import type { DashboardProject } from '@/lib/api/hooks/useDashboard';
+
+export interface RecentProjectsProps {
+ /** Projects to display */
+ projects?: DashboardProject[];
+ /** Whether data is loading */
+ isLoading?: boolean;
+ /** Additional CSS classes */
+ className?: string;
+}
+
+function ProjectCardSkeleton() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+interface ProjectCardProps {
+ project: DashboardProject;
+}
+
+function ProjectCard({ project }: ProjectCardProps) {
+ return (
+
+
+
+
+
+ {project.currentSprint && (
+
+ {project.currentSprint}
+
+ )}
+
+ {project.name}
+
+
+ {project.description && (
+ {project.description}
+ )}
+
+
+
+
+
+
+
+ {project.activeAgents} agents
+
+
+
+ {project.openIssues} issues
+
+
+
+
+ {project.lastActivity}
+
+
+
+
+
+ );
+}
+
+export function RecentProjects({ projects, isLoading = false, className }: RecentProjectsProps) {
+ // Show first 3 on mobile (hidden beyond), 6 on desktop
+ const displayProjects = projects?.slice(0, 6) ?? [];
+
+ return (
+
+
+
Recent Projects
+
+
+
+ {isLoading ? (
+
+ {[1, 2, 3, 4, 5, 6].map((i) => (
+
3 && 'hidden lg:block')}
+ >
+
+
+ ))}
+
+ ) : displayProjects.length === 0 ? (
+
+
+ No projects yet
+
+
+
+ ) : (
+
+ {displayProjects.map((project, index) => (
+
= 3 && 'hidden lg:block')}
+ >
+
+
+ ))}
+
+ )}
+
+ );
+}
diff --git a/frontend/src/components/dashboard/WelcomeHeader.tsx b/frontend/src/components/dashboard/WelcomeHeader.tsx
new file mode 100644
index 0000000..420df36
--- /dev/null
+++ b/frontend/src/components/dashboard/WelcomeHeader.tsx
@@ -0,0 +1,55 @@
+/**
+ * WelcomeHeader Component
+ *
+ * Displays a personalized welcome message for the dashboard.
+ *
+ * @see Issue #53
+ */
+
+'use client';
+
+import { Plus } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import { Link } from '@/lib/i18n/routing';
+import { useAuth } from '@/lib/auth/AuthContext';
+
+export interface WelcomeHeaderProps {
+ /** Additional CSS classes */
+ className?: string;
+}
+
+export function WelcomeHeader({ className }: WelcomeHeaderProps) {
+ const { user } = useAuth();
+
+ // Get first name for greeting
+ const firstName = user?.first_name || user?.email?.split('@')[0] || 'there';
+
+ // Get time-based greeting
+ const getGreeting = () => {
+ const hour = new Date().getHours();
+ if (hour < 12) return 'Good morning';
+ if (hour < 18) return 'Good afternoon';
+ return 'Good evening';
+ };
+
+ return (
+
+
+
+
+ {getGreeting()}, {firstName}
+
+
+ Here's what's happening with your projects today.
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/components/dashboard/index.ts b/frontend/src/components/dashboard/index.ts
new file mode 100644
index 0000000..4a7b9e6
--- /dev/null
+++ b/frontend/src/components/dashboard/index.ts
@@ -0,0 +1,23 @@
+/**
+ * Dashboard Components
+ *
+ * Exports all dashboard-related components.
+ *
+ * @module components/dashboard
+ * @see Issue #53
+ */
+
+export { Dashboard } from './Dashboard';
+export { WelcomeHeader } from './WelcomeHeader';
+export { DashboardQuickStats } from './DashboardQuickStats';
+export { RecentProjects } from './RecentProjects';
+export { PendingApprovals } from './PendingApprovals';
+export { EmptyState } from './EmptyState';
+
+// Re-export types
+export type { DashboardProps } from './Dashboard';
+export type { WelcomeHeaderProps } from './WelcomeHeader';
+export type { DashboardQuickStatsProps } from './DashboardQuickStats';
+export type { RecentProjectsProps } from './RecentProjects';
+export type { PendingApprovalsProps } from './PendingApprovals';
+export type { EmptyStateProps } from './EmptyState';
diff --git a/frontend/src/lib/api/hooks/useDashboard.ts b/frontend/src/lib/api/hooks/useDashboard.ts
new file mode 100644
index 0000000..79a55b7
--- /dev/null
+++ b/frontend/src/lib/api/hooks/useDashboard.ts
@@ -0,0 +1,257 @@
+/**
+ * Dashboard Data Hook
+ *
+ * Provides data for the main dashboard including:
+ * - Quick stats (projects, agents, issues, approvals)
+ * - Recent projects
+ * - Pending approvals
+ *
+ * Uses mock data until backend endpoints are available.
+ *
+ * @see Issue #53
+ */
+
+import { useQuery } from '@tanstack/react-query';
+import type { Project, ProjectStatus } from '@/components/projects/types';
+
+// ============================================================================
+// Types
+// ============================================================================
+
+export interface DashboardStats {
+ activeProjects: number;
+ runningAgents: number;
+ openIssues: number;
+ pendingApprovals: number;
+}
+
+export interface DashboardProject extends Project {
+ progress: number;
+ openIssues: number;
+ activeAgents: number;
+ currentSprint?: string;
+ lastActivity: string;
+}
+
+export interface PendingApproval {
+ id: string;
+ type: 'sprint_boundary' | 'code_review' | 'architecture_decision' | 'deployment';
+ title: string;
+ description: string;
+ projectId: string;
+ projectName: string;
+ requestedBy: string;
+ requestedAt: string;
+ priority: 'low' | 'medium' | 'high' | 'critical';
+}
+
+export interface DashboardData {
+ stats: DashboardStats;
+ recentProjects: DashboardProject[];
+ pendingApprovals: PendingApproval[];
+}
+
+// ============================================================================
+// Mock Data
+// ============================================================================
+
+const mockStats: DashboardStats = {
+ activeProjects: 3,
+ runningAgents: 8,
+ openIssues: 24,
+ pendingApprovals: 2,
+};
+
+const mockProjects: DashboardProject[] = [
+ {
+ id: 'proj-001',
+ name: 'E-Commerce Platform Redesign',
+ description: 'Complete redesign of the e-commerce platform with modern UI/UX',
+ status: 'active' as ProjectStatus,
+ autonomy_level: 'milestone',
+ created_at: '2025-11-15T10:00:00Z',
+ updated_at: '2025-12-30T14:30:00Z',
+ owner_id: 'user-001',
+ progress: 67,
+ openIssues: 12,
+ activeAgents: 4,
+ currentSprint: 'Sprint 3',
+ lastActivity: '2 minutes ago',
+ },
+ {
+ id: 'proj-002',
+ name: 'Mobile Banking App',
+ description: 'Native mobile app for banking services with biometric authentication',
+ status: 'active' as ProjectStatus,
+ autonomy_level: 'autonomous',
+ created_at: '2025-11-20T09:00:00Z',
+ updated_at: '2025-12-30T12:00:00Z',
+ owner_id: 'user-001',
+ progress: 45,
+ openIssues: 8,
+ activeAgents: 5,
+ currentSprint: 'Sprint 2',
+ lastActivity: '15 minutes ago',
+ },
+ {
+ id: 'proj-003',
+ name: 'Internal HR Portal',
+ description: 'Employee self-service portal for HR operations',
+ status: 'paused' as ProjectStatus,
+ autonomy_level: 'full_control',
+ created_at: '2025-10-01T08:00:00Z',
+ updated_at: '2025-12-28T16:00:00Z',
+ owner_id: 'user-001',
+ progress: 23,
+ openIssues: 5,
+ activeAgents: 0,
+ currentSprint: 'Sprint 1',
+ lastActivity: '2 days ago',
+ },
+ {
+ id: 'proj-004',
+ name: 'API Gateway Modernization',
+ description: 'Migrate legacy API gateway to cloud-native architecture',
+ status: 'active' as ProjectStatus,
+ autonomy_level: 'milestone',
+ created_at: '2025-12-01T11:00:00Z',
+ updated_at: '2025-12-30T10:00:00Z',
+ owner_id: 'user-001',
+ progress: 82,
+ openIssues: 3,
+ activeAgents: 2,
+ currentSprint: 'Sprint 4',
+ lastActivity: '1 hour ago',
+ },
+ {
+ id: 'proj-005',
+ name: 'Customer Analytics Dashboard',
+ description: 'Real-time analytics dashboard for customer behavior insights',
+ status: 'completed' as ProjectStatus,
+ autonomy_level: 'autonomous',
+ created_at: '2025-09-01T10:00:00Z',
+ updated_at: '2025-12-15T17:00:00Z',
+ owner_id: 'user-001',
+ progress: 100,
+ openIssues: 0,
+ activeAgents: 0,
+ lastActivity: '2 weeks ago',
+ },
+ {
+ id: 'proj-006',
+ name: 'DevOps Pipeline Automation',
+ description: 'Automate CI/CD pipelines with AI-assisted deployments',
+ status: 'active' as ProjectStatus,
+ autonomy_level: 'milestone',
+ created_at: '2025-12-10T14:00:00Z',
+ updated_at: '2025-12-30T09:00:00Z',
+ owner_id: 'user-001',
+ progress: 35,
+ openIssues: 6,
+ activeAgents: 3,
+ currentSprint: 'Sprint 1',
+ lastActivity: '30 minutes ago',
+ },
+];
+
+const mockApprovals: PendingApproval[] = [
+ {
+ id: 'approval-001',
+ type: 'sprint_boundary',
+ title: 'Sprint 3 Completion Review',
+ description: 'Review sprint deliverables and approve transition to Sprint 4',
+ projectId: 'proj-001',
+ projectName: 'E-Commerce Platform Redesign',
+ requestedBy: 'Product Owner Agent',
+ requestedAt: '2025-12-30T14:00:00Z',
+ priority: 'high',
+ },
+ {
+ id: 'approval-002',
+ type: 'architecture_decision',
+ title: 'Database Migration Strategy',
+ description: 'Approve PostgreSQL to CockroachDB migration plan',
+ projectId: 'proj-004',
+ projectName: 'API Gateway Modernization',
+ requestedBy: 'Architect Agent',
+ requestedAt: '2025-12-30T10:30:00Z',
+ priority: 'medium',
+ },
+];
+
+// ============================================================================
+// Hook
+// ============================================================================
+
+/**
+ * Fetches dashboard data (stats, recent projects, pending approvals)
+ *
+ * @returns Query result with dashboard data
+ */
+export function useDashboard() {
+ return useQuery({
+ queryKey: ['dashboard'],
+ queryFn: async () => {
+ // Simulate network delay
+ await new Promise((resolve) => setTimeout(resolve, 500));
+
+ // Return mock data
+ // TODO: Replace with actual API call when backend is ready
+ // const response = await apiClient.get('/api/v1/dashboard');
+ // return response.data;
+
+ return {
+ stats: mockStats,
+ recentProjects: mockProjects,
+ pendingApprovals: mockApprovals,
+ };
+ },
+ staleTime: 30000, // 30 seconds
+ refetchInterval: 60000, // Refetch every minute
+ });
+}
+
+/**
+ * Fetches only dashboard stats
+ */
+export function useDashboardStats() {
+ return useQuery({
+ queryKey: ['dashboard', 'stats'],
+ queryFn: async () => {
+ await new Promise((resolve) => setTimeout(resolve, 300));
+ return mockStats;
+ },
+ staleTime: 30000,
+ refetchInterval: 60000,
+ });
+}
+
+/**
+ * Fetches recent projects for dashboard
+ *
+ * @param limit - Maximum number of projects to return
+ */
+export function useRecentProjects(limit: number = 6) {
+ return useQuery({
+ queryKey: ['dashboard', 'recentProjects', limit],
+ queryFn: async () => {
+ await new Promise((resolve) => setTimeout(resolve, 400));
+ return mockProjects.slice(0, limit);
+ },
+ staleTime: 30000,
+ });
+}
+
+/**
+ * Fetches pending approvals
+ */
+export function usePendingApprovals() {
+ return useQuery({
+ queryKey: ['dashboard', 'pendingApprovals'],
+ queryFn: async () => {
+ await new Promise((resolve) => setTimeout(resolve, 300));
+ return mockApprovals;
+ },
+ staleTime: 30000,
+ });
+}