-
Notifications
You must be signed in to change notification settings - Fork 1.1k
[WIP] Common Topics #1004
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[WIP] Common Topics #1004
Changes from all commits
ad4429f
d004d2e
86c980a
9668f26
7e3290d
2734e98
400c18d
2176326
14d4988
292b936
a5bed51
c472aeb
53e923c
d009f17
60d5edc
61a036b
910a907
df364b2
1d69e3f
f6db11e
392cd31
2583ed0
0f9a9c2
ffb29fa
c33fb83
f602b4a
63a507d
a164145
865d53e
f52a490
93f5d6d
2cf993a
d300f1b
df053b1
c8c64c6
2d38d5a
21be771
862491b
9ab8bf4
237c614
95b09ed
fd8d922
f4cf9e7
603ac43
fe21b3e
44b3140
4845958
4c3efbe
5ba6934
c95dd03
1f94c7b
ca69cac
9967de3
181853f
e70c3ee
fe29e8c
57ac76f
58dd606
33ac0c7
f177ca1
b317bf1
2cadf67
078fe9d
1b5ff68
63893d4
429d49a
7f0ab7c
e25542d
150e479
10952df
2986b6b
8f2e7ca
480449f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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(); | ||
| 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, | ||
|
|
@@ -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, | ||
|
|
@@ -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]" /> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The new Prompt for AI agents |
||
| </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> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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" }, | ||
|
|
@@ -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 />}> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| <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" />} | ||
|
|
@@ -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> | ||
|
|
||
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
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