diff --git a/frontend/src/pages/Planner/Dashboard.tsx b/frontend/src/pages/Planner/Dashboard.tsx
index a6b10ac3..0a025737 100644
--- a/frontend/src/pages/Planner/Dashboard.tsx
+++ b/frontend/src/pages/Planner/Dashboard.tsx
@@ -1,10 +1,11 @@
-import { useEffect, useState, useMemo } from "react";
+import { useEffect, useState, useMemo, lazy, Suspense } from "react";
import { Link, useNavigate } from "react-router";
import PageMeta from "../../components/common/PageMeta";
import ComponentCard from "../../components/common/ComponentCard";
import { ProgressBar } from "../../components/ui/progress";
-import Chart from "react-apexcharts";
import { ApexOptions } from "apexcharts";
+
+const Chart = lazy(() => import("react-apexcharts").then((mod) => ({ default: mod.default })));
import {
ListIcon,
GroupIcon,
@@ -445,18 +446,18 @@ export default function PlannerDashboard() {
{/* Top Status Cards */}
-
-
-
-
-
Keywords Ready
+ >
+
+
+
+
Keywords Ready
{stats.keywords.total.toLocaleString()}
-
+
{trends.keywords !== 0 && (
0 ? 'text-success-500' : 'text-error-500'}`}>
{trends.keywords > 0 ?
:
}
@@ -464,28 +465,28 @@ export default function PlannerDashboard() {
)}
-
+
{stats.keywords.mapped} mapped • {stats.keywords.unmapped} unmapped
-
-
-
-
-
+
-
+
+
+
+
+
-
-
-
-
-
Clusters Built
+ >
+
+
+
+
Clusters Built
{stats.clusters.total.toLocaleString()}
-
+
{trends.clusters !== 0 && (
0 ? 'text-success-500' : 'text-error-500'}`}>
{trends.clusters > 0 ?
:
}
@@ -493,28 +494,28 @@ export default function PlannerDashboard() {
)}
-
+
{stats.clusters.totalVolume.toLocaleString()} total volume • {stats.clusters.avgKeywords} avg keywords
-
-
-
-
-
+
-
+
+
+
+
+
-
-
-
-
-
Ideas Generated
+ >
+
+
+
+
Ideas Generated
{stats.ideas.total.toLocaleString()}
-
+
{trends.ideas !== 0 && (
0 ? 'text-success-500' : 'text-error-500'}`}>
{trends.ideas > 0 ?
:
}
@@ -522,48 +523,48 @@ export default function PlannerDashboard() {
)}
-
+
{stats.ideas.queued} queued • {stats.ideas.notQueued} pending
-
-
-
-
-
+
-
+
+
+
+
+
-
-
-
-
+ >
+
+
+
Mapping Progress
-
+
{keywordMappingPct}%
-
-
+
+
{stats.keywords.mapped} of {stats.keywords.total} keywords mapped
-
-
-
+
-
-
+
+
+
+
- {/* Planner Workflow Steps */}
-
-
- {workflowSteps.map((step) => (
-
+
+ {workflowSteps.map((step) => (
+
-
+ >
+
{step.status === "completed" ? : step.number}
-
-
{step.title}
+
{step.title}
+
-
- {step.status === "completed" ? (
- <>
-
- Completed
- >
- ) : (
- <>
-
- Pending
- >
- )}
-
+
+ {step.status === "completed" ? (
+ <>
+
+ Completed
+ >
+ ) : (
+ <>
+
+ Pending
+ >
+ )}
- {step.count !== null && (
+
+ {step.count !== null && (
{step.count} {step.title.includes("Keywords") ? "keywords" : step.title.includes("Clusters") ? "clusters" : step.title.includes("Ideas") ? "ideas" : "items"}
-
- )}
- {step.status === "pending" && (
-
+ )}
+
+ ))}
+
+
+
+
+ {/* Progress Summary */}
+
+
+
+
+ Keyword Mapping
+ {keywordMappingPct}%
+
+
+
+ {stats.keywords.mapped} of {stats.keywords.total} keywords mapped
+
+
+
+
+
+ Clusters With Ideas
+ {clustersIdeasPct}%
+
+
+
+ {stats.clusters.withIdeas} of {stats.clusters.total} clusters have ideas
+
+
+
+
+
+ Ideas Queued to Writer
+ {ideasQueuedPct}%
+
+
+
+ {stats.ideas.queued} of {stats.ideas.total} ideas queued
+
+
-
- {/* Progress Summary */}
-
-
-
-
- Keyword Mapping
- {keywordMappingPct}%
-
-
-
- {stats.keywords.mapped} of {stats.keywords.total} keywords mapped
-
-
-
-
-
- Clusters With Ideas
- {clustersIdeasPct}%
-
-
-
- {stats.clusters.withIdeas} of {stats.clusters.total} clusters have ideas
-
-
-
-
-
- Ideas Queued to Writer
- {ideasQueuedPct}%
-
-
-
- {stats.ideas.queued} of {stats.ideas.total} ideas queued
-
-
-
-
-
{/* Top 5 Clusters */}
{topClustersChart ? (
-
+ }>
+
+
) : (
No clusters data available
)}
-
+
{/* Keywords by Status */}
{keywordsStatusChart && (
-
+ }>
+
+
)}
{/* Ideas by Status */}
{ideasStatusChart && (
-
+ }>
+
+
)}
-
+
- {/* Next Actions */}
+ {/* Next Actions */}
{nextActions.length > 0 && (
-
-
- {nextActions.map((action, index) => (
-
+
+ {nextActions.map((action, index) => (
+
+ >
{action.text}
-
- {action.action}
-
-
-
- ))}
+ >
+ {action.action}
+
+
-
+ ))}
+
+
)}
>
diff --git a/frontend/src/pages/Writer/Dashboard.tsx b/frontend/src/pages/Writer/Dashboard.tsx
index 1dbdce03..14c6801f 100644
--- a/frontend/src/pages/Writer/Dashboard.tsx
+++ b/frontend/src/pages/Writer/Dashboard.tsx
@@ -1,10 +1,11 @@
-import { useEffect, useState, useMemo } from "react";
+import { useEffect, useState, useMemo, lazy, Suspense } from "react";
import { Link, useNavigate } from "react-router";
import PageMeta from "../../components/common/PageMeta";
import ComponentCard from "../../components/common/ComponentCard";
import { ProgressBar } from "../../components/ui/progress";
-import Chart from "react-apexcharts";
import { ApexOptions } from "apexcharts";
+
+const Chart = lazy(() => import("react-apexcharts").then((mod) => ({ default: mod.default })));
import {
FileTextIcon,
BoxIcon,
@@ -748,12 +749,14 @@ export default function WriterDashboard() {
{/* Content Status Chart */}
{contentStatusChart && (
-
+ }>
+
+
)}
@@ -762,24 +765,28 @@ export default function WriterDashboard() {
{/* Tasks by Status */}
{tasksStatusChart && (
-
+ }>
+
+
)}
{/* Images by Type */}
{imagesTypeChart ? (
-
+ }>
+
+
) : (
@@ -804,12 +811,14 @@ export default function WriterDashboard() {
{/* Productivity Chart */}
{productivityChart && (
-
+ }>
+
+
)}
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index 6a34b26b..910ace1c 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -25,6 +25,9 @@ export default defineConfig(({ mode }) => {
'clsx',
'tailwind-merge',
'zustand',
+ // Include apexcharts for proper module resolution
+ 'apexcharts',
+ 'react-apexcharts',
],
// Exclude heavy dependencies that are only used in specific pages
// They will be lazy-loaded when needed
@@ -35,8 +38,6 @@ export default defineConfig(({ mode }) => {
'@fullcalendar/list',
'@fullcalendar/react',
'@fullcalendar/timegrid',
- 'apexcharts',
- 'react-apexcharts',
'@react-jvectormap/core',
'@react-jvectormap/world',
'react-dnd',