# TASK-FE-009: Dashboard & Notifications UI
**ID:** TASK-FE-009
**Title:** Dashboard, Notifications & Activity Feed UI
**Category:** Supporting Features
**Priority:** P3 (Low)
**Effort:** 3-4 days
**Dependencies:** TASK-FE-003, TASK-BE-011
**Assigned To:** Frontend Developer
---
## 📋 Overview
Build dashboard homepage with statistics widgets, recent activity, pending approvals, and real-time notifications system.
---
## 🎯 Objectives
1. Create dashboard homepage with widgets
2. Implement statistics cards (documents, pending approvals)
3. Build recent activity feed
4. Create notifications dropdown
5. Add pending tasks section
6. Implement real-time updates (optional)
---
## ✅ Acceptance Criteria
- [ ] Dashboard displays key statistics
- [ ] Recent activity feed working
- [ ] Notifications dropdown functional
- [ ] Pending tasks visible
- [ ] Charts/graphs display data
- [ ] Real-time updates (if WebSocket implemented)
---
## 🔧 Implementation Steps
### Step 1: Dashboard Page
```typescript
// File: src/app/(dashboard)/page.tsx
import { StatsCards } from '@/components/dashboard/stats-cards';
import { RecentActivity } from '@/components/dashboard/recent-activity';
import { PendingTasks } from '@/components/dashboard/pending-tasks';
import { QuickActions } from '@/components/dashboard/quick-actions';
export default async function DashboardPage() {
return (
Dashboard
Welcome back! Here's what's happening.
);
}
```
### Step 2: Statistics Cards
```typescript
// File: src/components/dashboard/stats-cards.tsx
import { Card } from '@/components/ui/card';
import { FileText, Clipboard, CheckCircle, Clock } from 'lucide-react';
export async function StatsCards() {
const stats = await getStats(); // Fetch from API
const cards = [
{
title: 'Total Correspondences',
value: stats.correspondences,
icon: FileText,
color: 'text-blue-600',
bgColor: 'bg-blue-50',
},
{
title: 'Active RFAs',
value: stats.rfas,
icon: Clipboard,
color: 'text-purple-600',
bgColor: 'bg-purple-50',
},
{
title: 'Approved Documents',
value: stats.approved,
icon: CheckCircle,
color: 'text-green-600',
bgColor: 'bg-green-50',
},
{
title: 'Pending Approvals',
value: stats.pending,
icon: Clock,
color: 'text-orange-600',
bgColor: 'bg-orange-50',
},
];
return (
{cards.map((card) => {
const Icon = card.icon;
return (
{card.title}
{card.value}
);
})}
);
}
```
### Step 3: Recent Activity Feed
```typescript
// File: src/components/dashboard/recent-activity.tsx
import { Card } from '@/components/ui/card';
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
import { Badge } from '@/components/ui/badge';
import { formatDistanceToNow } from 'date-fns';
export async function RecentActivity() {
const activities = await getRecentActivities();
return (
Recent Activity
{activities.map((activity) => (
{activity.user.initials}
{activity.user.name}
{activity.action}
{activity.description}
{formatDistanceToNow(new Date(activity.createdAt), {
addSuffix: true,
})}
))}
);
}
```
### Step 4: Notifications Dropdown
```typescript
// File: src/components/layout/notifications-dropdown.tsx
'use client';
import { useState, useEffect } from 'react';
import { Bell } from 'lucide-react';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Badge } from '@/components/ui/badge';
import { notificationApi } from '@/lib/api/notifications';
export function NotificationsDropdown() {
const [notifications, setNotifications] = useState([]);
const [unreadCount, setUnreadCount] = useState(0);
useEffect(() => {
// Fetch notifications
notificationApi.getUnread().then((data) => {
setNotifications(data.items);
setUnreadCount(data.unreadCount);
});
}, []);
const markAsRead = async (id: number) => {
await notificationApi.markAsRead(id);
setNotifications((prev) => prev.filter((n) => n.notification_id !== id));
setUnreadCount((prev) => prev - 1);
};
return (
Notifications
{notifications.length === 0 ? (
No new notifications
) : (
{notifications.map((notification) => (
markAsRead(notification.notification_id)}
>
{notification.title}
{notification.message}
{formatDistanceToNow(new Date(notification.created_at), {
addSuffix: true,
})}
))}
)}
View All
);
}
```
### Step 5: Pending Tasks Widget
```typescript
// File: src/components/dashboard/pending-tasks.tsx
import { Card } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import Link from 'next/link';
export async function PendingTasks() {
const tasks = await getPendingTasks();
return (
Pending Tasks
{tasks.map((task) => (
{task.title}
{task.daysOverdue > 0 ? `${task.daysOverdue}d overdue` : 'Due'}
{task.description}
))}
);
}
```
---
## 📦 Deliverables
- [ ] Dashboard page with widgets
- [ ] Statistics cards
- [ ] Recent activity feed
- [ ] Notifications dropdown
- [ ] Pending tasks section
- [ ] Quick actions buttons
---
## 🔗 Related Documents
- [TASK-BE-011: Notification & Audit](./TASK-BE-011-notification-audit.md)
---
**Created:** 2025-12-01
**Status:** Ready