Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { getBountyRewardDescription } from "@/lib/partners/get-bounty-reward-description";
import { PartnerBountyProps } from "@/lib/types";
import { BountyPerformance } from "@/ui/partners/bounties/bounty-performance";
import { BountyThumbnailImage } from "@/ui/partners/bounties/bounty-thumbnail-image";
Expand All @@ -9,7 +8,7 @@ import {
TimestampTooltip,
TooltipContent,
} from "@dub/ui";
import { Calendar6, Gift } from "@dub/ui/icons";
import { Calendar6 } from "@dub/ui/icons";
import { cn, formatDate, formatDateTimeSmart } from "@dub/utils";
import { useParams } from "next/navigation";

Expand Down Expand Up @@ -45,13 +44,13 @@ export function PartnerBountyCard({ bounty }: { bounty: PartnerBountyProps }) {
onClick={() => setShowClaimBountyModal(true)}
disabled={expiredBounty}
className={cn(
"border-border-subtle group relative flex w-full cursor-pointer flex-col gap-5 overflow-hidden rounded-xl border bg-white p-5 text-left",
"border-border-subtle group relative flex w-full cursor-pointer flex-col gap-2.5 overflow-hidden rounded-xl border bg-white p-3 text-left",
expiredBounty
? "cursor-not-allowed"
: "hover:border-border-default transition-all hover:shadow-lg",
)}
>
<div className="relative flex h-[132px] items-center justify-center rounded-lg bg-neutral-100 py-1.5">
<div className="relative flex h-[124px] items-center justify-center rounded-lg bg-neutral-100 py-3">
<div className="relative size-full">
<BountyThumbnailImage bounty={bounty} />
</div>
Expand All @@ -65,8 +64,8 @@ export function PartnerBountyCard({ bounty }: { bounty: PartnerBountyProps }) {
)}
</div>

<div className="flex min-w-0 flex-col gap-1.5">
<h3 className="text-content-emphasis truncate text-sm font-semibold">
<div className="flex min-w-0 flex-col gap-1.5 px-2 pb-1.5">
<h3 className="text-content-emphasis text-sm font-semibold sm:truncate">
{bounty.name}
</h3>

Expand All @@ -91,16 +90,9 @@ export function PartnerBountyCard({ bounty }: { bounty: PartnerBountyProps }) {
)}
</span>
</div>

<div className="text-content-subtle flex items-center gap-2 text-sm font-medium">
<Gift className="size-3.5 shrink-0" />
<span className="truncate">
{getBountyRewardDescription(bounty)}
</span>
</div>
</div>

<div className="flex grow flex-col justify-end">
<div className="flex grow flex-col justify-end px-2 pb-1">
{renderSubmissionStatus({
bounty,
setShowClaimBountyModal,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,20 @@ import {
SubmissionsCountByStatus,
useBountySubmissionsCount,
} from "@/lib/swr/use-bounty-submissions-count";
import useGroups from "@/lib/swr/use-groups";
import { usePartnersCountByGroupIds } from "@/lib/swr/use-partners-count-by-groupids";
import useWorkspace from "@/lib/swr/use-workspace";
import { BountyThumbnailImage } from "@/ui/partners/bounties/bounty-thumbnail-image";
import { GroupColorCircle } from "@/ui/partners/groups/group-color-circle";
import { ScrollableTooltipContent, Tooltip } from "@dub/ui";
import { Calendar6, Gift, Users, Users6 } from "@dub/ui/icons";
import { formatDate, nFormatter, pluralize } from "@dub/utils";
import { CalendarDays, Gift, Users } from "lucide-react";
import { useMemo } from "react";
import { BountyActionButton } from "../bounty-action-button";

export function BountyInfo() {
const { bounty, loading } = useBounty();
const { isOwner } = useWorkspace();

const { submissionsCount } = useBountySubmissionsCount<
SubmissionsCountByStatus[]
Expand All @@ -34,9 +39,20 @@ export function BountyInfo() {

const { totalPartners, loading: totalPartnersForBountyLoading } =
usePartnersCountByGroupIds({
groupIds: bounty?.groups.map((group) => group.id),
groupIds: bounty?.groups?.map((group) => group.id) ?? [],
});

const { groups } = useGroups();

const eligibleGroups = useMemo(() => {
if (!groups || !bounty || bounty.groups.length === 0) {
return [];
}
return bounty.groups
.map((bountyGroup) => groups.find((g) => g.id === bountyGroup.id))
.filter((g): g is NonNullable<typeof g> => g !== undefined);
}, [groups, bounty]);

if (loading) {
return <BountyInfoSkeleton />;
}
Expand All @@ -46,19 +62,22 @@ export function BountyInfo() {
}

return (
<div className="flex flex-col items-center gap-3 sm:flex-row sm:items-start sm:gap-6">
<div className="relative flex h-[100px] w-[100px] shrink-0 items-center justify-center rounded-lg bg-neutral-100 p-3">
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:gap-6">
<div className="relative flex h-[100px] w-full items-center justify-center rounded-lg bg-neutral-100 p-4 sm:h-[100px] sm:w-[100px] sm:shrink-0">
<BountyThumbnailImage bounty={bounty} />
<div className="absolute right-2 top-2 sm:hidden">
<BountyActionButton bounty={bounty} />
</div>
</div>

<div className="flex min-w-0 flex-1 flex-col gap-1.5">
<h3 className="truncate text-base font-semibold leading-6 text-neutral-900">
<h3 className="break-words text-base font-semibold leading-6 text-neutral-900 sm:truncate">
{bounty.name}
</h3>

<div className="flex items-center space-x-2">
<CalendarDays className="size-4 shrink-0" />
<span className="text-sm font-medium text-neutral-500">
<div className="text-content-subtle font-regular flex items-center gap-2 text-sm">
<Calendar6 className="size-4 shrink-0" />
<span>
{formatDate(bounty.startsAt, { month: "short" })}
{" → "}
{bounty.endsAt
Expand All @@ -67,54 +86,101 @@ export function BountyInfo() {
</span>
</div>

<div className="text-content-subtle flex items-center gap-2 text-sm font-medium">
<Gift className="size-4 shrink-0" />
<span className="text-ellipsis">
{getBountyRewardDescription(bounty)}
</span>
</div>
{!isOwner && (
<div className="text-content-subtle font-regular flex items-center gap-2 text-sm">
<Gift className="size-4 shrink-0" />
<span className="text-ellipsis">
{getBountyRewardDescription(bounty)}
</span>
</div>
)}

<div className="flex items-center space-x-2">
<div className="text-content-subtle font-regular flex items-center gap-2 text-sm">
<Users className="size-4 shrink-0" />
<div className="text-sm text-neutral-500">
{totalSubmissions === totalPartners ? (
<>All</>
<div>
{totalPartnersForBountyLoading ? (
<span className="inline-block h-4 w-8 animate-pulse rounded bg-neutral-200 align-middle" />
) : totalPartners === 0 ? (
<>
<span className="text-content-default">0</span>{" "}
{pluralize("partner", 0)}{" "}
{bounty.type === "performance" ? "completed" : "submitted"}
</>
) : totalSubmissions === totalPartners ? (
<>
All{" "}
<span className="text-content-default">
{nFormatter(totalPartners, { full: true })}
</span>{" "}
{pluralize("partner", totalPartners)}{" "}
{bounty.type === "performance" ? "completed" : "submitted"}
</>
) : (
<>
<span className="font-medium text-neutral-700">
<span className="text-content-default">
{nFormatter(totalSubmissions ?? 0, {
full: true,
})}
</span>{" "}
of
</>
)}{" "}
{totalPartnersForBountyLoading ? (
<span className="inline-block h-4 w-8 animate-pulse rounded bg-neutral-200 align-middle" />
) : (
<>
<span className="font-medium text-neutral-700">
of{" "}
<span className="text-content-default">
{nFormatter(totalPartners, { full: true })}
</span>
</span>{" "}
{pluralize("partner", totalPartners)}{" "}
{bounty.type === "performance" ? "completed" : "submitted"}
</>
)}{" "}
{pluralize("partner", totalSubmissions ?? totalPartners)}{" "}
{bounty.type === "performance" ? "completed" : "submitted"}
)}
{readyForReviewSubmissions > 0 && (
<>
{" "}
(
<span className="font-medium text-neutral-700">
<span className="text-content-default">
{nFormatter(readyForReviewSubmissions, { full: true })}
</span>{" "}
awaiting review)
</>
)}
</div>
</div>

{isOwner && (
<div className="text-content-subtle font-regular flex items-center gap-2 text-sm">
<Users6 className="size-4 shrink-0" />
{bounty.groups.length === 0 ? (
<span>All groups</span>
) : eligibleGroups.length === 1 ? (
<div className="flex items-center gap-1.5">
<GroupColorCircle group={eligibleGroups[0]} />
<span className="truncate">{eligibleGroups[0].name}</span>
</div>
) : eligibleGroups.length > 1 ? (
<Tooltip
content={
<ScrollableTooltipContent>
{eligibleGroups.map((group) => (
<div key={group.id} className="flex items-center gap-2">
<GroupColorCircle group={group} />
<span className="font-regular text-sm text-neutral-700">
{group.name}
</span>
</div>
))}
</ScrollableTooltipContent>
}
>
<div className="flex items-center gap-1.5">
<GroupColorCircle group={eligibleGroups[0]} />
<span className="truncate">
{eligibleGroups[0].name} +{eligibleGroups.length - 1}
</span>
</div>
</Tooltip>
) : null}
</div>
)}
</div>

<div className="flex items-start">
<div className="hidden items-start sm:flex">
<BountyActionButton bounty={bounty} />
</div>
</div>
Expand Down
Loading