This commit is contained in:
Luke Vella 2024-09-28 22:53:39 +01:00
parent 8e778a40ec
commit dc9940b390
No known key found for this signature in database
GPG key ID: 469CAD687F0D784C
5 changed files with 77 additions and 39 deletions

View file

@ -278,7 +278,8 @@
"1month": "1 month", "1month": "1 month",
"subscribe": "Subscribe", "subscribe": "Subscribe",
"cancelAnytime": "Cancel anytime from your <a>billing page</a>.", "cancelAnytime": "Cancel anytime from your <a>billing page</a>.",
"copiedToClipboard": "Copied to clipboard",
"viewAll": "View All", "viewAll": "View All",
"pending": "Pending" "pending": "Pending",
"copyLink": "Copy link",
"viewResults": "View results"
} }

View file

@ -1,7 +1,6 @@
"use client"; "use client";
import { PollStatus } from "@rallly/database"; import { PollStatus } from "@rallly/database";
import { RadioCards, RadioCardsItem } from "@rallly/ui/radio-pills"; import { RadioCards, RadioCardsItem } from "@rallly/ui/radio-pills";
import { getCoreRowModel, useReactTable } from "@tanstack/react-table";
import { CalendarPlusIcon } from "lucide-react"; import { CalendarPlusIcon } from "lucide-react";
import { useSearchParams } from "next/navigation"; import { useSearchParams } from "next/navigation";
import { z } from "zod"; import { z } from "zod";
@ -29,6 +28,7 @@ function FilteredPolls({ status }: { status: PollStatus }) {
limit: 30, limit: 30,
}, },
{ {
suspense: true,
getNextPageParam: (lastPage) => lastPage.nextCursor, getNextPageParam: (lastPage) => lastPage.nextCursor,
keepPreviousData: true, keepPreviousData: true,
}, },
@ -59,7 +59,7 @@ function FilteredPolls({ status }: { status: PollStatus }) {
<ol className="space-y-4"> <ol className="space-y-4">
{data.pages.map((page, i) => ( {data.pages.map((page, i) => (
<li key={i}> <li key={i}>
<div className="grid gap-3 sm:gap-4 md:grid-cols-3"> <div className="grid gap-4 md:grid-cols-3">
{page.polls.map((poll) => ( {page.polls.map((poll) => (
<GroupPollCard <GroupPollCard
key={poll.id} key={poll.id}
@ -93,7 +93,9 @@ function PollStatusMenu({
onStatusChange?: (status: PollStatus) => void; onStatusChange?: (status: PollStatus) => void;
}) { }) {
const { data: countByStatus, isFetching } = const { data: countByStatus, isFetching } =
trpc.polls.getCountByStatus.useQuery(); trpc.polls.getCountByStatus.useQuery(undefined, {
suspense: true,
});
if (!countByStatus) { if (!countByStatus) {
return null; return null;

View file

@ -1,5 +1,5 @@
export function GridCardFooter({ children }: React.PropsWithChildren) { export function GridCardFooter({ children }: React.PropsWithChildren) {
return <div className="relative z-10 mt-6 flex gap-1">{children}</div>; return <div className="relative z-10 mt-6">{children}</div>;
} }
export function GridCardHeader({ children }: React.PropsWithChildren) { export function GridCardHeader({ children }: React.PropsWithChildren) {
@ -8,6 +8,6 @@ export function GridCardHeader({ children }: React.PropsWithChildren) {
export const GridCard = ({ children }: { children: React.ReactNode }) => { export const GridCard = ({ children }: { children: React.ReactNode }) => {
return ( return (
<div className="relative rounded-lg border bg-white p-4">{children}</div> <div className="relative rounded-xl border bg-white p-4">{children}</div>
); );
}; };

View file

@ -2,16 +2,17 @@
import { PollStatus } from "@rallly/database"; import { PollStatus } from "@rallly/database";
import { Button } from "@rallly/ui/button"; import { Button } from "@rallly/ui/button";
import { useToast } from "@rallly/ui/hooks/use-toast";
import { Icon } from "@rallly/ui/icon"; import { Icon } from "@rallly/ui/icon";
import { Tooltip, TooltipContent, TooltipTrigger } from "@rallly/ui/tooltip";
import { import {
BarChart2Icon, BarChart2Icon,
CalendarIcon, CalendarIcon,
CheckIcon,
Link2Icon, Link2Icon,
User2Icon, User2Icon,
} from "lucide-react"; } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import { useTranslation } from "react-i18next"; import React from "react";
import { useCopyToClipboard } from "react-use"; import { useCopyToClipboard } from "react-use";
import { import {
@ -26,6 +27,45 @@ import { Trans } from "@/components/trans";
import { useLocalizeTime } from "@/utils/dayjs"; import { useLocalizeTime } from "@/utils/dayjs";
import { getRange } from "@/utils/get-range"; import { getRange } from "@/utils/get-range";
function CopyLinkButton({ link, ...forwardProps }: { link: string }) {
const [, copy] = useCopyToClipboard();
const [isCopied, setIsCopied] = React.useState(false);
return (
<Tooltip open={isCopied ? true : undefined}>
<TooltipTrigger asChild>
<Button
{...forwardProps}
variant="ghost"
onClick={() => {
copy(link);
React.startTransition(() => {
setIsCopied(true);
});
setTimeout(() => {
setIsCopied(false);
}, 2000);
}}
>
<Icon>
<Link2Icon />
</Icon>
</Button>
</TooltipTrigger>
<TooltipContent>
{isCopied ? (
<p>
<CheckIcon className="mr-2 inline-block size-4 text-green-500" />
<Trans i18nKey="copied" defaults="Copied" />
</p>
) : (
<Trans i18nKey="copyLink" defaults="Copy link" />
)}
</TooltipContent>
</Tooltip>
);
}
export function GroupPollCard({ export function GroupPollCard({
status, status,
pollId, pollId,
@ -45,10 +85,6 @@ export function GroupPollCard({
}) { }) {
const localizeTime = useLocalizeTime(); const localizeTime = useLocalizeTime();
const { t } = useTranslation("app");
const [, copy] = useCopyToClipboard();
const { toast } = useToast();
return ( return (
<GridCard key={pollId}> <GridCard key={pollId}>
<GridCardHeader> <GridCardHeader>
@ -56,7 +92,10 @@ export function GroupPollCard({
<GroupPollIcon size="xs" /> <GroupPollIcon size="xs" />
</div> </div>
<h3 className="truncate font-medium"> <h3 className="truncate font-medium">
<Link className="focus:underline" href={`/poll/${pollId}`}> <Link
className="hover:underline focus:text-gray-900"
href={`/poll/${pollId}`}
>
{title} {title}
</Link> </Link>
</h3> </h3>
@ -82,29 +121,23 @@ export function GroupPollCard({
</Pill> </Pill>
</PillList> </PillList>
<GridCardFooter> <GridCardFooter>
<Button <div className="flex justify-end gap-1">
variant="ghost" <CopyLinkButton link={inviteLink} />
onClick={() => { <Tooltip>
copy(inviteLink); <TooltipTrigger asChild>
toast({ <Button variant="ghost" asChild>
title: t("copiedToClipboard", { <Link href={`/poll/${pollId}`}>
ns: "app", <Icon>
defaultValue: "Copied to clipboard", <BarChart2Icon />
}), </Icon>
}); </Link>
}} </Button>
> </TooltipTrigger>
<Icon> <TooltipContent>
<Link2Icon /> <Trans i18nKey="viewResults" defaults="View results" />
</Icon> </TooltipContent>
</Button> </Tooltip>
<Button variant="ghost" asChild> </div>
<Link href={`/poll/${pollId}`}>
<Icon>
<BarChart2Icon />
</Icon>
</Link>
</Button>
</GridCardFooter> </GridCardFooter>
</GridCard> </GridCard>
); );

View file

@ -9,7 +9,7 @@ export function Heading({
return ( return (
<h1 <h1
className={cn( className={cn(
"text-xl font-semibold text-gray-900", "text-2xl font-semibold text-gray-900",
heading.className, heading.className,
className, className,
)} )}
@ -24,6 +24,8 @@ export function Subheading({
className, className,
}: React.PropsWithChildren<{ className?: string }>) { }: React.PropsWithChildren<{ className?: string }>) {
return ( return (
<h2 className={cn("text-base font-semibold", className)}>{children}</h2> <h2 className={cn("text-lg font-semibold text-gray-900", className)}>
{children}
</h2>
); );
} }