-
{company.name}
-
- Analytics Dashboard
-
+ {/* Apple-Style Unified Header */}
+
+
+
+ {/* Top row: Company info and actions */}
+
+
+
+
{company.name}
+
+ Analytics Dashboard
+
+
+
+ Last updated{" "}
+
+ {new Date(metrics.lastUpdated || Date.now()).toLocaleString()}
+
+
-
- Last updated{" "}
-
- {new Date(metrics.lastUpdated || Date.now()).toLocaleString()}
-
-
-
-
-
-
-
- {refreshing ? "Refreshing..." : "Refresh"}
-
-
-
-
-
-
-
-
- signOut({ callbackUrl: "/login" })}>
-
- Sign out
-
-
-
+
+
+
+ {refreshing ? "Refreshing..." : "Refresh"}
+
+
+
+
+
+
+
+
+
+ signOut({ callbackUrl: "/login" })}>
+
+ Sign out
+
+
+
+
+
+ {/* Date Range Controls */}
+ {dateRange && (
+
+
+
+
+ Date Range:
+
+
+
+ From:
+ handleDateRangeChange(e.target.value, selectedEndDate)}
+ className="px-3 py-1.5 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
+ />
+
+
+ To:
+ handleDateRangeChange(selectedStartDate, e.target.value)}
+ className="px-3 py-1.5 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
+ />
+
+
+ {
+ const endDate = new Date().toISOString().split('T')[0];
+ const startDate = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
+ handleDateRangeChange(startDate, endDate);
+ }}
+ className="text-xs border-gray-200 hover:bg-gray-50"
+ >
+ Last 7 days
+
+ {
+ const endDate = new Date().toISOString().split('T')[0];
+ const startDate = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
+ handleDateRangeChange(startDate, endDate);
+ }}
+ className="text-xs border-gray-200 hover:bg-gray-50"
+ >
+ Last 30 days
+
+ handleDateRangeChange(dateRange.minDate, dateRange.maxDate)}
+ className="text-xs border-gray-200 hover:bg-gray-50"
+ >
+ All time
+
+
+
+
+
+ Available data: {new Date(dateRange.minDate).toLocaleDateString()} - {new Date(dateRange.maxDate).toLocaleDateString()}
+
+
+ )}
- {/* Date Range Picker */}
- {dateRange && (
-
- )}
-
{/* Modern Metrics Grid */}
= 0,
}}
+ variant="primary"
/>
}
description="Average per day"
+ variant="warning"
/>
}
description="Busiest hour"
+ variant="primary"
/>
}
description="Languages detected"
+ variant="success"
/>
diff --git a/app/globals.css b/app/globals.css
index f4c1e9b..fd91c14 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -43,71 +43,71 @@
:root {
--radius: 0.625rem;
- --background: oklch(1 0 0);
- --foreground: oklch(0.145 0 0);
- --card: oklch(1 0 0);
- --card-foreground: oklch(0.145 0 0);
- --popover: oklch(1 0 0);
- --popover-foreground: oklch(0.145 0 0);
- --primary: oklch(0.205 0 0);
- --primary-foreground: oklch(0.985 0 0);
- --secondary: oklch(0.97 0 0);
- --secondary-foreground: oklch(0.205 0 0);
- --muted: oklch(0.97 0 0);
- --muted-foreground: oklch(0.556 0 0);
- --accent: oklch(0.97 0 0);
- --accent-foreground: oklch(0.205 0 0);
- --destructive: oklch(0.577 0.245 27.325);
- --border: oklch(0.922 0 0);
- --input: oklch(0.922 0 0);
- --ring: oklch(0.708 0 0);
- --chart-1: oklch(0.646 0.222 41.116);
- --chart-2: oklch(0.6 0.118 184.704);
- --chart-3: oklch(0.398 0.07 227.392);
- --chart-4: oklch(0.828 0.189 84.429);
- --chart-5: oklch(0.769 0.188 70.08);
- --sidebar: oklch(0.985 0 0);
- --sidebar-foreground: oklch(0.145 0 0);
- --sidebar-primary: oklch(0.205 0 0);
- --sidebar-primary-foreground: oklch(0.985 0 0);
- --sidebar-accent: oklch(0.97 0 0);
- --sidebar-accent-foreground: oklch(0.205 0 0);
- --sidebar-border: oklch(0.922 0 0);
- --sidebar-ring: oklch(0.708 0 0);
+ --background: 255 255 255;
+ --foreground: 15 23 42;
+ --card: 255 255 255;
+ --card-foreground: 15 23 42;
+ --popover: 255 255 255;
+ --popover-foreground: 15 23 42;
+ --primary: 0 123 255;
+ --primary-foreground: 255 255 255;
+ --secondary: 245 245 245;
+ --secondary-foreground: 51 51 51;
+ --muted: 248 250 252;
+ --muted-foreground: 100 116 139;
+ --accent: 245 245 245;
+ --accent-foreground: 51 51 51;
+ --destructive: 239 68 68;
+ --border: 229 231 235;
+ --input: 229 231 235;
+ --ring: 0 123 255;
+ --chart-1: 0 123 255;
+ --chart-2: 255 20 147;
+ --chart-3: 50 205 50;
+ --chart-4: 138 43 226;
+ --chart-5: 255 215 0;
+ --sidebar: 248 250 252;
+ --sidebar-foreground: 15 23 42;
+ --sidebar-primary: 0 123 255;
+ --sidebar-primary-foreground: 255 255 255;
+ --sidebar-accent: 245 245 245;
+ --sidebar-accent-foreground: 51 51 51;
+ --sidebar-border: 229 231 235;
+ --sidebar-ring: 0 123 255;
}
.dark {
- --background: oklch(0.145 0 0);
- --foreground: oklch(0.985 0 0);
- --card: oklch(0.205 0 0);
- --card-foreground: oklch(0.985 0 0);
- --popover: oklch(0.205 0 0);
- --popover-foreground: oklch(0.985 0 0);
- --primary: oklch(0.922 0 0);
- --primary-foreground: oklch(0.205 0 0);
- --secondary: oklch(0.269 0 0);
- --secondary-foreground: oklch(0.985 0 0);
- --muted: oklch(0.269 0 0);
- --muted-foreground: oklch(0.708 0 0);
- --accent: oklch(0.269 0 0);
- --accent-foreground: oklch(0.985 0 0);
- --destructive: oklch(0.704 0.191 22.216);
- --border: oklch(1 0 0 / 10%);
- --input: oklch(1 0 0 / 15%);
- --ring: oklch(0.556 0 0);
- --chart-1: oklch(0.488 0.243 264.376);
- --chart-2: oklch(0.696 0.17 162.48);
- --chart-3: oklch(0.769 0.188 70.08);
- --chart-4: oklch(0.627 0.265 303.9);
- --chart-5: oklch(0.645 0.246 16.439);
- --sidebar: oklch(0.205 0 0);
- --sidebar-foreground: oklch(0.985 0 0);
- --sidebar-primary: oklch(0.488 0.243 264.376);
- --sidebar-primary-foreground: oklch(0.985 0 0);
- --sidebar-accent: oklch(0.269 0 0);
- --sidebar-accent-foreground: oklch(0.985 0 0);
- --sidebar-border: oklch(1 0 0 / 10%);
- --sidebar-ring: oklch(0.556 0 0);
+ --background: 15 23 42;
+ --foreground: 248 250 252;
+ --card: 30 41 59;
+ --card-foreground: 248 250 252;
+ --popover: 30 41 59;
+ --popover-foreground: 248 250 252;
+ --primary: 59 130 246;
+ --primary-foreground: 15 23 42;
+ --secondary: 51 65 85;
+ --secondary-foreground: 248 250 252;
+ --muted: 51 65 85;
+ --muted-foreground: 148 163 184;
+ --accent: 51 65 85;
+ --accent-foreground: 248 250 252;
+ --destructive: 248 113 113;
+ --border: 51 65 85;
+ --input: 51 65 85;
+ --ring: 59 130 246;
+ --chart-1: 59 130 246;
+ --chart-2: 236 72 153;
+ --chart-3: 34 197 94;
+ --chart-4: 147 51 234;
+ --chart-5: 251 191 36;
+ --sidebar: 30 41 59;
+ --sidebar-foreground: 248 250 252;
+ --sidebar-primary: 59 130 246;
+ --sidebar-primary-foreground: 248 250 252;
+ --sidebar-accent: 51 65 85;
+ --sidebar-accent-foreground: 248 250 252;
+ --sidebar-border: 51 65 85;
+ --sidebar-ring: 59 130 246;
}
@layer base {
@@ -115,6 +115,25 @@
@apply border-border outline-ring/50;
}
body {
- @apply bg-background text-foreground;
+ @apply bg-gray-50 text-gray-900;
+ }
+
+ /* Apple-style scrollbars */
+ ::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+ }
+
+ ::-webkit-scrollbar-track {
+ background: transparent;
+ }
+
+ ::-webkit-scrollbar-thumb {
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 4px;
+ }
+
+ ::-webkit-scrollbar-thumb:hover {
+ background: rgba(0, 0, 0, 0.3);
}
}
diff --git a/components/Charts.tsx b/components/Charts.tsx
index fd959b3..f283823 100644
--- a/components/Charts.tsx
+++ b/components/Charts.tsx
@@ -128,9 +128,9 @@ export function SentimentChart({ sentimentData }: SentimentChartProps) {
sentimentData.negative,
],
backgroundColor: [
- "rgba(34, 197, 94, 0.8)", // green
- "rgba(249, 115, 22, 0.8)", // orange
- "rgba(239, 68, 68, 0.8)", // red
+ "rgba(37, 99, 235, 0.8)", // blue (primary)
+ "rgba(107, 114, 128, 0.8)", // gray
+ "rgba(236, 72, 153, 0.8)", // pink
],
borderWidth: 1,
},
@@ -196,12 +196,12 @@ export function LanguagePieChart({ languages }: LanguagePieChartProps) {
{
data,
backgroundColor: [
- "rgba(59, 130, 246, 0.8)",
- "rgba(16, 185, 129, 0.8)",
- "rgba(249, 115, 22, 0.8)",
- "rgba(236, 72, 153, 0.8)",
- "rgba(139, 92, 246, 0.8)",
- "rgba(107, 114, 128, 0.8)",
+ "rgba(37, 99, 235, 0.8)", // blue (primary)
+ "rgba(107, 114, 128, 0.8)", // gray
+ "rgba(236, 72, 153, 0.8)", // pink
+ "rgba(34, 197, 94, 0.8)", // lime green
+ "rgba(168, 85, 247, 0.8)", // purple
+ "rgba(251, 191, 36, 0.8)", // yellow
],
borderWidth: 1,
},
diff --git a/components/ResponseTimeDistribution.tsx b/components/ResponseTimeDistribution.tsx
index dc97668..ef1b604 100644
--- a/components/ResponseTimeDistribution.tsx
+++ b/components/ResponseTimeDistribution.tsx
@@ -20,10 +20,10 @@ interface ResponseTimeDistributionProps {
const CustomTooltip = ({ active, payload, label }: any) => {
if (active && payload && payload.length) {
return (
-
-
{label}
-
-
+
+
{label}
+
+
{payload[0].value}
{" "}
responses
@@ -66,11 +66,11 @@ export default function ResponseTimeDistribution({
label = `${i}-${i + 1} sec`;
}
- // Determine color based on response time
+ // Determine color based on response time using cohesive palette
let color;
- if (i <= 2) color = "hsl(var(--chart-1))"; // Green for fast
- else if (i <= 5) color = "hsl(var(--chart-4))"; // Yellow for medium
- else color = "hsl(var(--chart-3))"; // Red for slow
+ if (i <= 2) color = "rgb(37, 99, 235)"; // Blue for fast (primary color)
+ else if (i <= 5) color = "rgb(107, 114, 128)"; // Gray for medium
+ else color = "rgb(236, 72, 153)"; // Pink for slow
return {
name: label,
@@ -85,18 +85,18 @@ export default function ResponseTimeDistribution({
} />
@@ -122,14 +122,14 @@ export default function ResponseTimeDistribution({
{/* Average line */}
- {title}
-
+
+
{title}
+
No questions data available
@@ -24,36 +24,38 @@ export default function TopQuestionsChart({ data, title = "Top 5 Asked Questions
const maxCount = Math.max(...data.map(q => q.count));
return (
-
-
{title}
+
+
{title}
-
+
{data.map((question, index) => {
const percentage = maxCount > 0 ? (question.count / maxCount) * 100 : 0;
return (
-
- {/* Question text */}
-
-
- {question.question}
-
-
- {question.count}
-
-
-
- {/* Progress bar */}
-
-
- {/* Rank indicator */}
-
- {index + 1}
+
+ {/* Rank and Question */}
+
+
+ {index + 1}
+
+
+
+ {question.question}
+
+
+
+
+ {question.count} times
+
+
+
);
@@ -61,10 +63,10 @@ export default function TopQuestionsChart({ data, title = "Top 5 Asked Questions
{/* Summary */}
-
-
-
Total questions analyzed
-
+
+
+ Total questions analyzed
+
{data.reduce((sum, q) => sum + q.count, 0)}
diff --git a/components/charts/bar-chart.tsx b/components/charts/bar-chart.tsx
index 9c1b754..f20e6a2 100644
--- a/components/charts/bar-chart.tsx
+++ b/components/charts/bar-chart.tsx
@@ -43,11 +43,11 @@ export default function ModernBarChart({
title,
dataKey = "value",
colors = [
- "hsl(var(--chart-1))",
- "hsl(var(--chart-2))",
- "hsl(var(--chart-3))",
- "hsl(var(--chart-4))",
- "hsl(var(--chart-5))",
+ "rgb(37, 99, 235)", // Blue (primary)
+ "rgb(107, 114, 128)", // Gray
+ "rgb(236, 72, 153)", // Pink
+ "rgb(34, 197, 94)", // Lime green
+ "rgb(168, 85, 247)", // Purple
],
height = 300,
className,
@@ -64,12 +64,12 @@ export default function ModernBarChart({
))}
diff --git a/components/charts/line-chart.tsx b/components/charts/line-chart.tsx
index c3a0f10..e900125 100644
--- a/components/charts/line-chart.tsx
+++ b/components/charts/line-chart.tsx
@@ -44,7 +44,7 @@ export default function ModernLineChart({
data,
title,
dataKey = "value",
- color = "hsl(var(--primary))",
+ color = "rgb(37, 99, 235)",
gradient = true,
height = 300,
className,
@@ -71,18 +71,18 @@ export default function ModernLineChart({
) {
{
switch (variant) {
case "primary":
- return "border-primary/20 bg-linear-to-br from-primary/5 to-primary/10";
+ return "border border-blue-100 bg-white shadow-sm hover:shadow-md";
case "success":
- return "border-green-200 bg-linear-to-br from-green-50 to-green-100 dark:border-green-800 dark:from-green-950 dark:to-green-900";
+ return "border border-green-100 bg-white shadow-sm hover:shadow-md";
case "warning":
- return "border-amber-200 bg-linear-to-br from-amber-50 to-amber-100 dark:border-amber-800 dark:from-amber-950 dark:to-amber-900";
+ return "border border-pink-100 bg-white shadow-sm hover:shadow-md";
case "danger":
- return "border-red-200 bg-linear-to-br from-red-50 to-red-100 dark:border-red-800 dark:from-red-950 dark:to-red-900";
+ return "border border-red-100 bg-white shadow-sm hover:shadow-md";
default:
- return "border-border bg-linear-to-br from-card to-muted/20";
+ return "border border-gray-100 bg-white shadow-sm hover:shadow-md";
}
};
const getIconClasses = () => {
- switch (variant) {
- case "primary":
- return "bg-primary/10 text-primary border-primary/20";
- case "success":
- return "bg-green-100 text-green-600 border-green-200 dark:bg-green-900 dark:text-green-400 dark:border-green-800";
- case "warning":
- return "bg-amber-100 text-amber-600 border-amber-200 dark:bg-amber-900 dark:text-amber-400 dark:border-amber-800";
- case "danger":
- return "bg-red-100 text-red-600 border-red-200 dark:bg-red-900 dark:text-red-400 dark:border-red-800";
- default:
- return "bg-muted text-muted-foreground border-border";
- }
+ return "bg-gray-50 text-gray-900 border-gray-100";
};
const getTrendIcon = () => {
@@ -105,13 +94,11 @@ export default function MetricCard({
className
)}
>
- {/* Subtle gradient overlay */}
-
-
+
{title}
{description && (
@@ -137,7 +124,7 @@ export default function MetricCard({