Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
ad4429f
Set up mock analytics data
jshwrnr Nov 16, 2025
d004d2e
Revert "Set up mock analytics data"
jshwrnr Nov 16, 2025
86c980a
Update the charts at the top of stats to a combined card and bar char…
jshwrnr Nov 18, 2025
9668f26
Remove comments
jshwrnr Nov 18, 2025
7e3290d
Add better colors to charts
jshwrnr Nov 18, 2025
2734e98
Add color dot to chart tooltip
jshwrnr Nov 18, 2025
400c18d
Rename component
jshwrnr Nov 18, 2025
2176326
Remove detailed stats from stats page
jshwrnr Nov 18, 2025
14d4988
Fix button
jshwrnr Nov 18, 2025
292b936
Remove explainer text
jshwrnr Nov 18, 2025
a5bed51
Combine email analytics cards
jshwrnr Nov 18, 2025
c472aeb
Move actions outside of page header
jshwrnr Nov 18, 2025
53e923c
Clean up ActionBar
jshwrnr Nov 18, 2025
d009f17
Refactoring
jshwrnr Nov 18, 2025
60d5edc
Add chevron to filters
jshwrnr Nov 18, 2025
61a036b
Combine day options into date picker
jshwrnr Nov 18, 2025
910a907
Update icon in Group by filter
jshwrnr Nov 18, 2025
df364b2
Redo the bar list cards
jshwrnr Nov 19, 2025
1d69e3f
Fade out bar lists in bar list card
jshwrnr Nov 19, 2025
f6db11e
Remove comment
jshwrnr Nov 19, 2025
392cd31
Add dialogs for bar list card
jshwrnr Nov 19, 2025
2583ed0
Remove unused stuff fix dialog
jshwrnr Nov 19, 2025
0f9a9c2
Add links to horizontal bar charts
jshwrnr Nov 19, 2025
ffb29fa
Test
jshwrnr Nov 19, 2025
c33fb83
Add icons for email items
jshwrnr Nov 19, 2025
f602b4a
Add fallback icons from email list
jshwrnr Nov 19, 2025
63a507d
Fallback icon fix
jshwrnr Nov 19, 2025
a164145
Add in temp mock data
jshwrnr Nov 20, 2025
865d53e
Fix for email icon
jshwrnr Nov 20, 2025
f52a490
Refactor email icon
jshwrnr Nov 20, 2025
93f5d6d
Add 90 day label to calendar dropdown
jshwrnr Nov 20, 2025
2cf993a
Reduce horizontal padding on date picker with range trigger
jshwrnr Nov 20, 2025
d300f1b
Remove StatsCards
jshwrnr Nov 20, 2025
df053b1
Add some loading state changes
jshwrnr Nov 20, 2025
c8c64c6
Refactor main chart
jshwrnr Nov 20, 2025
2d38d5a
Replace old bar charts with the new one
jshwrnr Nov 20, 2025
21be771
Remove most of tremor
jshwrnr Nov 20, 2025
862491b
Remove tremor from progress panel
jshwrnr Nov 20, 2025
9ab8bf4
Remove tremor
jshwrnr Nov 20, 2025
237c614
Make progress a bit shorter
jshwrnr Nov 20, 2025
95b09ed
Update chart color
jshwrnr Nov 20, 2025
fd8d922
Allow main chart to show multiple bars at once
jshwrnr Nov 20, 2025
f4cf9e7
Turn rules select from tabs into a dropdown menu
jshwrnr Nov 20, 2025
603ac43
Merge branch 'main' into feat/charts-redesign
elie222 Nov 20, 2025
fe21b3e
Remove unused import
jshwrnr Nov 20, 2025
44b3140
Merge branch 'main' into feat/charts-redesign
elie222 Nov 22, 2025
4845958
Refactor ActionBar
jshwrnr Nov 23, 2025
4c3efbe
Refactor
jshwrnr Nov 23, 2025
5ba6934
Remove mock data
jshwrnr Nov 23, 2025
c95dd03
Undo xs button changes
jshwrnr Nov 23, 2025
1f94c7b
Remove mock data
jshwrnr Nov 23, 2025
ca69cac
Keep progress as h-4
jshwrnr Nov 23, 2025
9967de3
Rename "color" props to "innerClassName"
jshwrnr Nov 23, 2025
181853f
Bring back stats card for usage page
jshwrnr Nov 23, 2025
e70c3ee
Switch all uses of cx with cn
jshwrnr Nov 23, 2025
fe29e8c
Update padding in tab select
jshwrnr Nov 23, 2025
57ac76f
Move Banner and List from /components/common to /components
jshwrnr Nov 23, 2025
58dd606
Use a function for relative date label
jshwrnr Nov 23, 2025
33ac0c7
Refactor calendar
jshwrnr Nov 23, 2025
f177ca1
Add xs-2 size variant to button
jshwrnr Nov 23, 2025
b317bf1
Refactor MainStatChart
jshwrnr Nov 23, 2025
2cadf67
Use COLORS in EmailActionsAnalytics
jshwrnr Nov 23, 2025
078fe9d
Organize colors into colors.ts
jshwrnr Nov 23, 2025
1b5ff68
Move badge colors to tailwind config
jshwrnr Nov 23, 2025
63893d4
Move header link colors to tailwind
jshwrnr Nov 23, 2025
429d49a
Move colors into tailwind
jshwrnr Nov 23, 2025
7f0ab7c
Only show "view more" button in BarListCard if there is more to view
jshwrnr Nov 23, 2025
e25542d
Fix typo
jshwrnr Nov 23, 2025
150e479
Swap action bar item positions
jshwrnr Nov 24, 2025
10952df
Use group by in stats summary
jshwrnr Nov 24, 2025
2986b6b
Fix group by and bar chart labelling
jshwrnr Nov 24, 2025
8f2e7ca
Fix issue with duplicate startOfPeriods in weekly group by bar charts
jshwrnr Nov 24, 2025
480449f
Add support for 'Common Topics' chart
jshwrnr Nov 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 51 additions & 10 deletions apps/web/app/(app)/[emailAccountId]/assistant/RulesSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,69 @@
import { useRules } from "@/hooks/useRules";
import { Skeleton } from "@/components/ui/skeleton";
import { LoadingContent } from "@/components/LoadingContent";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useSearchParams, useRouter } from "next/navigation";
import { ChevronDown, Tag } from "lucide-react";

export function RulesSelect() {
const { data, isLoading, error } = useRules();
const searchParams = useSearchParams();
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mark the component as a client component before using useSearchParams/useRouter; otherwise Next.js will crash because these hooks are client-only.

Prompt for AI agents
Address the following comment on apps/web/app/(app)/[emailAccountId]/assistant/RulesSelect.tsx at line 16:

<comment>Mark the component as a client component before using `useSearchParams`/`useRouter`; otherwise Next.js will crash because these hooks are client-only.</comment>

<file context>
@@ -1,28 +1,69 @@
 
 export function RulesSelect() {
   const { data, isLoading, error } = useRules();
+  const searchParams = useSearchParams();
+  const router = useRouter();
+  const currentValue = searchParams.get(&quot;ruleId&quot;) || &quot;all&quot;;
</file context>
Fix with Cubic

const router = useRouter();
const currentValue = searchParams.get("ruleId") || "all";

const handleValueChange = (value: string) => {
const params = new URLSearchParams(searchParams.toString());
params.set("ruleId", value);
router.push(`?${params.toString()}`);
};

const getCurrentLabel = () => {
if (currentValue === "all") return "All rules";
if (currentValue === "skipped") return "No match";
return data?.find((rule) => rule.id === currentValue)?.name || "All rules";
};

return (
<LoadingContent
loading={isLoading}
error={error}
loadingComponent={<Skeleton className="h-10 w-full" />}
loadingComponent={<Skeleton className="h-10 w-[200px]" />}
>
<Tabs defaultValue="all" searchParam="ruleId" className="overflow-x-auto">
<TabsList>
<TabsTrigger value="all">All rules</TabsTrigger>
<TabsTrigger value="skipped">No match</TabsTrigger>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="sm"
className="h-10 whitespace-nowrap"
>
<Tag className="mr-2 h-4 w-4" />
{getCurrentLabel()}
<ChevronDown className="ml-2 h-4 w-4 text-gray-400" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => handleValueChange("all")}>
All rules
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleValueChange("skipped")}>
No match
</DropdownMenuItem>
{data?.map((rule) => (
<TabsTrigger key={rule.id} value={rule.id}>
<DropdownMenuItem
key={rule.id}
onClick={() => handleValueChange(rule.id)}
>
{rule.name}
</TabsTrigger>
</DropdownMenuItem>
))}
</TabsList>
</Tabs>
</DropdownMenuContent>
</DropdownMenu>
</LoadingContent>
);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"use client";

import type React from "react";
import { ProgressBar } from "@tremor/react";
import {
Table,
TableBody,
Expand All @@ -16,6 +15,12 @@ import {
} from "@/app/(app)/[emailAccountId]/bulk-unsubscribe/common";
import type { RowProps } from "@/app/(app)/[emailAccountId]/bulk-unsubscribe/types";
import { Checkbox } from "@/components/Checkbox";
import { Progress } from "@/components/ui/progress";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";

export function BulkUnsubscribeDesktop({
tableRows,
Expand Down Expand Up @@ -117,27 +122,36 @@ export function BulkUnsubscribeRowDesktop({
<TableCell>{item.value}</TableCell>
<TableCell>
<div className="hidden xl:block">
<ProgressBar
label={`${Math.round(readPercentage)}%`}
value={readPercentage}
tooltip={`${item.readEmails} read. ${
item.value - item.readEmails
} unread.`}
color="blue"
className="w-[150px]"
/>
<div className="flex items-center gap-4">
<Tooltip>
<TooltipTrigger asChild>
<Progress value={readPercentage} className="h-2 w-[150px]" />
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new <Progress> bars never announce their percentage because the Progress component discards the value prop, so Radix treats them as indeterminate; forward the value (and max) to ProgressPrimitive.Root before using it here to avoid the accessibility regression.

Prompt for AI agents
Address the following comment on apps/web/app/(app)/[emailAccountId]/bulk-unsubscribe/BulkUnsubscribeDesktop.tsx at line 128:

<comment>The new `&lt;Progress&gt;` bars never announce their percentage because the Progress component discards the `value` prop, so Radix treats them as indeterminate; forward the `value` (and max) to `ProgressPrimitive.Root` before using it here to avoid the accessibility regression.</comment>

<file context>
@@ -117,27 +122,36 @@ export function BulkUnsubscribeRowDesktop({
+          &lt;div className=&quot;flex items-center gap-4&quot;&gt;
+            &lt;Tooltip&gt;
+              &lt;TooltipTrigger asChild&gt;
+                &lt;Progress value={readPercentage} className=&quot;h-2 w-[150px]&quot; /&gt;
+              &lt;/TooltipTrigger&gt;
+              &lt;TooltipContent&gt;
</file context>
Fix with Cubic

</TooltipTrigger>
<TooltipContent>
{item.readEmails} read. {item.value - item.readEmails} unread.
</TooltipContent>
</Tooltip>
<span className="text-sm">{Math.round(readPercentage)}%</span>
</div>
</div>
<div className="xl:hidden">{Math.round(readPercentage)}%</div>
</TableCell>
<TableCell>
<div className="hidden 2xl:block">
<ProgressBar
label={`${Math.round(archivedPercentage)}%`}
value={archivedPercentage}
tooltip={`${archivedEmails} archived. ${item.inboxEmails} unarchived.`}
color="blue"
className="w-[150px]"
/>
<div className="flex items-center gap-4">
<Tooltip>
<TooltipTrigger asChild>
<Progress
value={archivedPercentage}
className="h-2 w-[150px]"
/>
</TooltipTrigger>
<TooltipContent>
{archivedEmails} archived. {item.inboxEmails} unarchived.
</TooltipContent>
</Tooltip>
<span className="text-sm">{Math.round(archivedPercentage)}%</span>
</div>
</div>
<div className="2xl:hidden">{Math.round(archivedPercentage)}%</div>
</TableCell>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,13 @@ import { ClientOnly } from "@/components/ClientOnly";
import { Toggle } from "@/components/Toggle";
import { useAccount } from "@/providers/EmailAccountProvider";
import { useWindowSize } from "usehooks-ts";
import { ActionBar } from "@/app/(app)/[emailAccountId]/stats/ActionBar";
import { LoadStatsButton } from "@/app/(app)/[emailAccountId]/stats/LoadStatsButton";
import { PageWrapper } from "@/components/PageWrapper";
import { PageHeader } from "@/components/PageHeader";
import { TextLink } from "@/components/Typography";
import { DismissibleVideoCard } from "@/components/VideoCard";
import { ActionBar } from "@/app/(app)/[emailAccountId]/stats/ActionBar";
import { DatePickerWithRange } from "@/components/DatePickerWithRange";

const selectOptions = [
{ label: "Last week", value: "7" },
Expand Down Expand Up @@ -246,34 +247,34 @@ export function BulkUnsubscribe() {
/>

<div className="items-center justify-between flex mt-4 flex-wrap">
<div className="flex items-center justify-end gap-1">
<div className="">
<Toggle
name="show-unhandled"
label="Only unhandled"
enabled={onlyUnhandled}
onChange={() =>
setFilters(
onlyUnhandled
? {
unhandled: true,
autoArchived: true,
unsubscribed: true,
approved: true,
}
: {
unhandled: true,
autoArchived: false,
unsubscribed: false,
approved: false,
},
)
}
/>
<ActionBar rightContent={<LoadStatsButton />}>
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrapping all of the bulk-unsubscribe toolbar controls inside ActionBar removes the flex-wrap that previously let them stack, so on mobile/tablet widths the toggle, search bar, filter, date range picker, and Load Stats button overflow and become unusable.

Prompt for AI agents
Address the following comment on apps/web/app/(app)/[emailAccountId]/bulk-unsubscribe/BulkUnsubscribeSection.tsx at line 250:

<comment>Wrapping all of the bulk-unsubscribe toolbar controls inside ActionBar removes the flex-wrap that previously let them stack, so on mobile/tablet widths the toggle, search bar, filter, date range picker, and Load Stats button overflow and become unusable.</comment>

<file context>
@@ -246,34 +247,34 @@ export function BulkUnsubscribe() {
-                )
-              }
-            /&gt;
+        &lt;ActionBar rightContent={&lt;LoadStatsButton /&gt;}&gt;
+          &lt;div className=&quot;flex items-center justify-end gap-1&quot;&gt;
+            &lt;div className=&quot;&quot;&gt;
</file context>
Fix with Cubic

<div className="flex items-center justify-end gap-1">
<div className="">
<Toggle
name="show-unhandled"
label="Only unhandled"
enabled={onlyUnhandled}
onChange={() =>
setFilters(
onlyUnhandled
? {
unhandled: true,
autoArchived: true,
unsubscribed: true,
approved: true,
}
: {
unhandled: true,
autoArchived: false,
unsubscribed: false,
approved: false,
},
)
}
/>
</div>
</div>

<SearchBar onSearch={setSearch} />

<DetailedStatsFilter
label="Filter"
icon={<FilterIcon className="mr-2 h-4 w-4" />}
Expand Down Expand Up @@ -330,19 +331,14 @@ export function BulkUnsubscribe() {
},
]}
/>
</div>

<div className="flex flex-wrap gap-1">
<ActionBar
<DatePickerWithRange
dateRange={dateRange}
onSetDateRange={setDateRange}
selectOptions={selectOptions}
dateDropdown={dateDropdown}
setDateDropdown={onSetDateDropdown}
dateRange={dateRange}
setDateRange={setDateRange}
isMobile={isMobile}
onSetDateDropdown={onSetDateDropdown}
/>
<LoadStatsButton />
</div>
</ActionBar>
</div>

<ClientOnly>
Expand Down

This file was deleted.

Loading