diff --git a/client/src/pages/Dashboard.jsx b/client/src/pages/Dashboard.jsx
index 03b320d..4c7fb91 100644
--- a/client/src/pages/Dashboard.jsx
+++ b/client/src/pages/Dashboard.jsx
@@ -3,6 +3,7 @@ import { Link, useNavigate } from 'react-router-dom'
import { format, isAfter, isBefore, addDays } from 'date-fns'
import { FileText, Megaphone, AlertTriangle, ArrowRight, Clock, Wallet, TrendingUp, TrendingDown, DollarSign, Landmark, CheckSquare, FolderKanban } from 'lucide-react'
import { AppContext } from '../App'
+import { useAuth } from '../contexts/AuthContext'
import { useLanguage } from '../i18n/LanguageContext'
import { api, PRIORITY_CONFIG } from '../utils/api'
import StatCard from '../components/StatCard'
@@ -264,6 +265,7 @@ export default function Dashboard() {
const { t, currencySymbol } = useLanguage()
const navigate = useNavigate()
const { currentUser, teamMembers } = useContext(AppContext)
+ const { hasModule } = useAuth()
const [posts, setPosts] = useState([])
const [campaigns, setCampaigns] = useState([])
const [tasks, setTasks] = useState([])
@@ -282,18 +284,30 @@ export default function Dashboard() {
const loadData = async () => {
try {
- const [postsRes, campaignsRes, tasksRes, financeRes, projectsRes] = await Promise.allSettled([
- api.get('/posts?limit=50&sort=-createdAt'),
- api.get('/campaigns'),
- api.get('/tasks'),
- api.get('/finance/summary'),
- api.get('/projects'),
- ])
- setPosts(postsRes.status === 'fulfilled' ? (postsRes.value.data || postsRes.value || []) : [])
- setCampaigns(campaignsRes.status === 'fulfilled' ? (campaignsRes.value.data || campaignsRes.value || []) : [])
- setTasks(tasksRes.status === 'fulfilled' ? (tasksRes.value.data || tasksRes.value || []) : [])
- setFinance(financeRes.status === 'fulfilled' ? (financeRes.value.data || financeRes.value || null) : null)
- setProjects(projectsRes.status === 'fulfilled' ? (projectsRes.value.data || projectsRes.value || []) : [])
+ const fetches = []
+ // Only fetch data for modules the user has access to
+ if (hasModule('marketing')) {
+ fetches.push(api.get('/posts?limit=50&sort=-createdAt').then(r => ({ key: 'posts', data: r.data || r || [] })))
+ fetches.push(api.get('/campaigns').then(r => ({ key: 'campaigns', data: r.data || r || [] })))
+ }
+ if (hasModule('projects')) {
+ fetches.push(api.get('/tasks').then(r => ({ key: 'tasks', data: r.data || r || [] })))
+ fetches.push(api.get('/projects').then(r => ({ key: 'projects', data: r.data || r || [] })))
+ }
+ if (hasModule('finance')) {
+ fetches.push(api.get('/finance/summary').then(r => ({ key: 'finance', data: r.data || r || null })))
+ }
+
+ const results = await Promise.allSettled(fetches)
+ results.forEach(r => {
+ if (r.status !== 'fulfilled') return
+ const { key, data } = r.value
+ if (key === 'posts') setPosts(data)
+ else if (key === 'campaigns') setCampaigns(data)
+ else if (key === 'tasks') setTasks(data)
+ else if (key === 'projects') setProjects(data)
+ else if (key === 'finance') setFinance(data)
+ })
} catch (err) {
console.error('Dashboard load error:', err)
} finally {
@@ -339,6 +353,42 @@ export default function Dashboard() {
.sort((a, b) => new Date(a.dueDate) - new Date(b.dueDate))
.slice(0, 8)
+ const statCards = []
+ if (hasModule('marketing')) {
+ statCards.push({
+ icon: FileText,
+ label: t('dashboard.totalPosts'),
+ value: filteredPosts.length || 0,
+ subtitle: `${filteredPosts.filter(p => p.status === 'published').length} ${t('dashboard.published')}`,
+ color: 'brand-primary',
+ })
+ statCards.push({
+ icon: Megaphone,
+ label: t('dashboard.activeCampaigns'),
+ value: activeCampaigns,
+ subtitle: `${campaigns.length} ${t('dashboard.total')}`,
+ color: 'brand-secondary',
+ })
+ }
+ if (hasModule('finance')) {
+ statCards.push({
+ icon: Landmark,
+ label: t('dashboard.budgetRemaining'),
+ value: `${(finance?.remaining ?? 0).toLocaleString()}`,
+ subtitle: finance?.totalReceived ? `${(finance.spent || 0).toLocaleString()} ${t('dashboard.spent')} ${t('dashboard.of')} ${finance.totalReceived.toLocaleString()} ${currencySymbol}` : t('dashboard.noBudget'),
+ color: 'brand-tertiary',
+ })
+ }
+ if (hasModule('projects')) {
+ statCards.push({
+ icon: AlertTriangle,
+ label: t('dashboard.overdueTasks'),
+ value: overdueTasks,
+ subtitle: overdueTasks > 0 ? t('dashboard.needsAttention') : t('dashboard.allOnTrack'),
+ color: 'brand-quaternary',
+ })
+ }
+
if (loading) {
return
{post.title}
-{post.title}
+{task.title}
-{task.title}
+