From 71847995e7a5e603874df3787248cc1f9a1a9a46 Mon Sep 17 00:00:00 2001 From: KM Koushik Date: Sun, 15 Mar 2026 18:02:19 +1100 Subject: [PATCH 1/4] dashbaord ui stuff --- .../dashboard/dashboard-filters.tsx | 63 ++++++++++--------- .../app/(dashboard)/dashboard/email-chart.tsx | 5 +- .../src/app/(dashboard)/dashboard/page.tsx | 8 +-- .../src/server/service/dashboard-service.ts | 25 ++++---- packages/ui/styles/globals.css | 16 ++--- 5 files changed, 62 insertions(+), 55 deletions(-) diff --git a/apps/web/src/app/(dashboard)/dashboard/dashboard-filters.tsx b/apps/web/src/app/(dashboard)/dashboard/dashboard-filters.tsx index a7e8e507..ddc4eab2 100644 --- a/apps/web/src/app/(dashboard)/dashboard/dashboard-filters.tsx +++ b/apps/web/src/app/(dashboard)/dashboard/dashboard-filters.tsx @@ -29,33 +29,38 @@ export default function DashboardFilters({ }; return ( -
- - setDays(value)}> - - - 7 Days - - - 30 Days - - - -
- ); +
+ + setDays(value)}> + + + 7 Days + + + 30 Days + + + +
+ ); } diff --git a/apps/web/src/app/(dashboard)/dashboard/email-chart.tsx b/apps/web/src/app/(dashboard)/dashboard/email-chart.tsx index 8c02e208..5abe0bef 100644 --- a/apps/web/src/app/(dashboard)/dashboard/email-chart.tsx +++ b/apps/web/src/app/(dashboard)/dashboard/email-chart.tsx @@ -135,6 +135,9 @@ export default function EmailChart({ days, domain }: EmailChartProps) { fontSize={12} className="font-mono" stroke={currentColors.xaxis} + tick={{ fill: currentColors.xaxis, fillOpacity: 0.65 }} + axisLine={false} + tickLine={false} /> {/* */} {/* */}

Analytics

- + - +
diff --git a/apps/web/src/server/service/dashboard-service.ts b/apps/web/src/server/service/dashboard-service.ts index 90228111..117c100f 100644 --- a/apps/web/src/server/service/dashboard-service.ts +++ b/apps/web/src/server/service/dashboard-service.ts @@ -3,15 +3,15 @@ import { format, subDays } from "date-fns"; import { Prisma, Team } from "@prisma/client"; type EmailTimeSeries = { - days?: number; - domain?: number - team: Team + days?: number; + domain?: number; + team: Team; }; export async function emailTimeSeries(input: EmailTimeSeries) { - const days = input.days !== 7 ? 30 : 7; - const { domain, team } = input - const startDate = new Date(); + const days = input.days !== 7 ? 30 : 7; + const { domain, team } = input; + const startDate = new Date(); startDate.setDate(startDate.getDate() - days); const isoStartDate = startDate.toISOString().split("T")[0]; @@ -87,22 +87,21 @@ export async function emailTimeSeries(input: EmailTimeSeries) { clicked: 0, bounced: 0, complained: 0, - } + }, ); return { result: filledResult, totalCounts }; } - type ReputationMetricsData = { - domain?: number - team: Team + domain?: number; + team: Team; }; export async function reputationMetricsData(input: ReputationMetricsData) { - const { domain, team } = input + const { domain, team } = input; - const reputations = await db.cumulatedMetrics.findMany({ + const reputations = await db.cumulatedMetrics.findMany({ where: { teamId: team.id, ...(domain ? { domainId: domain } : {}), @@ -116,7 +115,7 @@ export async function reputationMetricsData(input: ReputationMetricsData) { acc.complained += Number(curr.complained); return acc; }, - { delivered: 0, hardBounced: 0, complained: 0 } + { delivered: 0, hardBounced: 0, complained: 0 }, ); const resultWithRates = { diff --git a/packages/ui/styles/globals.css b/packages/ui/styles/globals.css index 24f13825..7904793f 100644 --- a/packages/ui/styles/globals.css +++ b/packages/ui/styles/globals.css @@ -114,7 +114,7 @@ --input: 214.3 31.8% 91.4%; --ring: 222.2 84% 4.9%; - --radius: 0.5rem; + --radius: 0.25rem; --chart-1: 12 76% 61%; --chart-2: 173 58% 39%; @@ -122,10 +122,10 @@ --chart-4: 43 74% 66%; --chart-5: 27 87% 67%; - --sidebar-background: 225 3% 94%; - --sidebar-foreground: 240 5.3% 26.1%; - --sidebar-primary: 240 5.9% 10%; - --sidebar-primary-foreground: 0 0% 98%; + --sidebar-background: 220 2% 96%; + --sidebar-foreground: 234 16% 35%; + --sidebar-primary: 167 34% 20%; + --sidebar-primary-foreground: 210 40% 98%; --sidebar-accent: 240 11% 88%; --sidebar-accent-foreground: 240 5.9% 10%; --sidebar-border: 240 11% 88%; @@ -182,10 +182,10 @@ --chart-4: 280 65% 60%; --chart-5: 340 75% 55%; - --sidebar-background: 240 23% 9%; + --sidebar-background: 240 21% 12%; --sidebar-foreground: 226 64% 88%; - --sidebar-primary: 224.3 76.3% 48%; - --sidebar-primary-foreground: 0 0% 100%; + --sidebar-primary: 167 64% 94%; + --sidebar-primary-foreground: 240 23% 9%; --sidebar-accent: 237 17% 20%; --sidebar-accent-foreground: 240 4.8% 95.9%; --sidebar-border: 240 21% 15%; From bc627d76b07f669193848d4acee6e3af8502263b Mon Sep 17 00:00:00 2001 From: KM Koushik Date: Sun, 15 Mar 2026 18:14:57 +1100 Subject: [PATCH 2/4] graph stuff --- .../app/(dashboard)/dashboard/email-chart.tsx | 219 +++++++++--------- 1 file changed, 114 insertions(+), 105 deletions(-) diff --git a/apps/web/src/app/(dashboard)/dashboard/email-chart.tsx b/apps/web/src/app/(dashboard)/dashboard/email-chart.tsx index 5abe0bef..8821e045 100644 --- a/apps/web/src/app/(dashboard)/dashboard/email-chart.tsx +++ b/apps/web/src/app/(dashboard)/dashboard/email-chart.tsx @@ -32,16 +32,18 @@ const STACK_ORDER: string[] = [ ] as const; type StackKey = (typeof STACK_ORDER)[number]; - -function createRoundedTopShape(currentKey: StackKey) { - const currentIndex = STACK_ORDER.indexOf(currentKey); +function createRoundedTopShape( + currentKey: StackKey, + visibleStackOrder: StackKey[], +) { + const currentIndex = visibleStackOrder.indexOf(currentKey); return (props: any) => { const payload = props.payload as | Partial> | undefined; let hasAbove = false; - for (let i = currentIndex + 1; i < STACK_ORDER.length; i++) { - const key = STACK_ORDER[i]; + for (let i = currentIndex + 1; i < visibleStackOrder.length; i++) { + const key = visibleStackOrder[i]; const val = key ? (payload?.[key] ?? 0) : 0; if (val > 0) { hasAbove = true; @@ -55,6 +57,7 @@ function createRoundedTopShape(currentKey: StackKey) { } export default function EmailChart({ days, domain }: EmailChartProps) { + const [selectedMetrics, setSelectedMetrics] = React.useState([]); const domainId = domain ? Number(domain) : undefined; const statusQuery = api.dashboard.emailTimeSeries.useQuery({ days: days, @@ -63,6 +66,32 @@ export default function EmailChart({ days, domain }: EmailChartProps) { const currentColors = useColors(); + const metricMeta: Record = { + delivered: { label: "Delivered", color: currentColors.delivered }, + bounced: { label: "Bounced", color: currentColors.bounced }, + complained: { label: "Complained", color: currentColors.complained }, + opened: { label: "Opened", color: currentColors.opened }, + clicked: { label: "Clicked", color: currentColors.clicked }, + }; + + const visibleMetrics = + selectedMetrics.length === 0 + ? STACK_ORDER + : STACK_ORDER.filter((key) => selectedMetrics.includes(key)); + + const toggleMetric = (metric: StackKey) => { + setSelectedMetrics((prev) => { + const exists = prev.includes(metric); + + if (exists) { + return prev.filter((key) => key !== metric); + } + + const nextSet = new Set([...prev, metric]); + return STACK_ORDER.filter((key) => nextSet.has(key)); + }); + }; + return (
{!statusQuery.isLoading && statusQuery.data ? ( @@ -75,6 +104,8 @@ export default function EmailChart({ days, domain }: EmailChartProps) { status={"total"} count={statusQuery.data.totalCounts.sent} percentage={100} + isActive={selectedMetrics.length === 0} + isClickable={false} /> toggleMetric("delivered")} /> toggleMetric("bounced")} /> toggleMetric("complained")} /> toggleMetric("clicked")} /> toggleMetric("opened")} />
@@ -157,11 +213,10 @@ export default function EmailChart({ days, domain }: EmailChartProps) { if (!data) return null; const hasAnyData = - (data.delivered || 0) > 0 || - (data.bounced || 0) > 0 || - (data.complained || 0) > 0 || - (data.opened || 0) > 0 || - (data.clicked || 0) > 0; + visibleMetrics.reduce( + (sum, key) => sum + (data[key] || 0), + 0, + ) > 0; if (!hasAnyData) return null; @@ -170,105 +225,43 @@ export default function EmailChart({ days, domain }: EmailChartProps) {

{data.date}

- {data.delivered ? ( -
-
-

- Delivered -

-

{data.delivered}

-
- ) : null} - {data.bounced ? ( -
-
-

- Bounced -

-

{data.bounced}

-
- ) : null} - {data.complained ? ( -
-
-

- Complained -

-

{data.complained}

-
- ) : null} - {data.opened ? ( -
-
-

- Opened -

-

{data.opened}

-
- ) : null} - {data.clicked ? ( -
+ {visibleMetrics.map((metricKey) => { + const metricValue = data[metricKey] || 0; + if (!metricValue) return null; + + return (
-

- Clicked -

-

{data.clicked}

-
- ) : null} + key={metricKey} + className="flex gap-2 items-center" + > +
+

+ {metricMeta[metricKey].label} +

+

{metricValue}

+ + ); + })} ); }} cursor={false} /> - {/* */} - - - - - + {visibleMetrics.map((metricKey) => ( + + ))} @@ -283,6 +276,9 @@ type DashboardItemCardProps = { status: EmailStatus | "total"; count: number; percentage: number; + onClick?: () => void; + isActive?: boolean; + isClickable?: boolean; }; const DashboardItemCard: React.FC = ({ @@ -314,6 +310,9 @@ const EmailChartItem: React.FC = ({ status, count, percentage, + onClick, + isActive = false, + isClickable = true, }) => { const currentColors = useColors(); @@ -336,7 +335,17 @@ const EmailChartItem: React.FC = ({ }; return ( -
+ ); }; From 26554592080b96e0dd82d7e6bb18b42682004bd5 Mon Sep 17 00:00:00 2001 From: KM Koushik Date: Sun, 15 Mar 2026 18:27:40 +1100 Subject: [PATCH 3/4] stuff --- packages/ui/styles/globals.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/styles/globals.css b/packages/ui/styles/globals.css index 7904793f..43a0cc0b 100644 --- a/packages/ui/styles/globals.css +++ b/packages/ui/styles/globals.css @@ -114,7 +114,7 @@ --input: 214.3 31.8% 91.4%; --ring: 222.2 84% 4.9%; - --radius: 0.25rem; + --radius: 0.5rem; --chart-1: 12 76% 61%; --chart-2: 173 58% 39%; From 9dd0eadb00e6865fb392f96facc74f1f4b402f9a Mon Sep 17 00:00:00 2001 From: KM Koushik Date: Mon, 16 Mar 2026 00:03:30 +1100 Subject: [PATCH 4/4] fix: type dashboard chart metric keys --- apps/web/src/app/(dashboard)/dashboard/email-chart.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/web/src/app/(dashboard)/dashboard/email-chart.tsx b/apps/web/src/app/(dashboard)/dashboard/email-chart.tsx index 8821e045..402dd7d4 100644 --- a/apps/web/src/app/(dashboard)/dashboard/email-chart.tsx +++ b/apps/web/src/app/(dashboard)/dashboard/email-chart.tsx @@ -23,7 +23,7 @@ interface EmailChartProps { domain: string | null; } -const STACK_ORDER: string[] = [ +const STACK_ORDER = [ "delivered", "bounced", "complained", @@ -74,9 +74,9 @@ export default function EmailChart({ days, domain }: EmailChartProps) { clicked: { label: "Clicked", color: currentColors.clicked }, }; - const visibleMetrics = + const visibleMetrics: StackKey[] = selectedMetrics.length === 0 - ? STACK_ORDER + ? [...STACK_ORDER] : STACK_ORDER.filter((key) => selectedMetrics.includes(key)); const toggleMetric = (metric: StackKey) => {