Skip to content
Open
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
112 changes: 95 additions & 17 deletions components/Bounty/BountyMetadataLine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { formatDeadline } from '@/utils/date';
import { CurrencyBadge } from '@/components/ui/CurrencyBadge';
import { RadiatingDot } from '@/components/ui/RadiatingDot';
import { ContentTypeBadge } from '@/components/ui/ContentTypeBadge';
import { Check } from 'lucide-react';
import { Check, Clock, XCircle } from 'lucide-react';
import { useCurrencyPreference } from '@/contexts/CurrencyPreferenceContext';

import { Tooltip } from '@/components/ui/Tooltip';
interface BountyMetadataLineProps {
amount: number;
expirationDate?: string;
isOpen: boolean;
reviewPeriodEndDate?: string;
status: 'OPEN' | 'CLOSED' | 'REVIEW_PERIOD' | 'EXPIRED' | 'CANCELLED';
expiringSoon: boolean;
className?: string;
solutionsCount?: number;
Expand All @@ -18,19 +19,65 @@ interface BountyMetadataLineProps {
export const BountyMetadataLine = ({
amount,
expirationDate,
isOpen,
reviewPeriodEndDate,
status,
expiringSoon,
className = '',
showDeadline = true,
}: BountyMetadataLineProps) => {
const { showUSD } = useCurrencyPreference();

// Format the deadline text
const deadlineText = isOpen
? expirationDate
? formatDeadline(expirationDate)
: 'No deadline'
: 'Completed';
const isOpen = status === 'OPEN';
const isActive = status === 'OPEN' || status === 'REVIEW_PERIOD';

const getDeadlineText = () => {
switch (status) {
case 'OPEN':
return expirationDate ? formatDeadline(expirationDate) : 'No deadline';
case 'REVIEW_PERIOD':
if (reviewPeriodEndDate) {
const deadline = formatDeadline(reviewPeriodEndDate);

const reviewTransformations: Record<string, string | ((match: string) => string)> = {
Ended: 'Review ended',
'Ended today': 'Review ended today',
'Ends today': 'Review ends today',
'Ends tomorrow': 'Review ends tomorrow',
'Ends in less than an hour': 'Review ends in less than an hour',
};

if (reviewTransformations[deadline]) {
return reviewTransformations[deadline] as string;
}

const patterns: Array<[RegExp, (match: RegExpMatchArray) => string]> = [
[/(\d+) days left/, (match) => `Review ends in ${match[1]} days`],
[/^Ends in (\d+ hours?)$/, (match) => `Review ends in ${match[1]}`],
[/^Ends (.+)$/, (match) => `Review ends ${match[1]}`],
];

for (const [pattern, transform] of patterns) {
const match = deadline.match(pattern);
if (match) {
return transform(match);
}
}

return `Review ${deadline.toLowerCase()}`;
}
return 'Under Review';
case 'CLOSED':
return 'Completed';
case 'EXPIRED':
return 'Bounty Ended';
case 'CANCELLED':
return 'Cancelled';
default:
return 'Completed';
}
};

const deadlineText = getDeadlineText();

return (
<div className={`space-y-3 ${className}`}>
Expand All @@ -49,16 +96,47 @@ export const BountyMetadataLine = ({

{showDeadline && (
<div className="flex items-center gap-2 text-sm">
{isOpen ? (
<RadiatingDot size={12} dotSize={6} isRadiating={isOpen} className="flex-shrink-0" />
{isActive ? (
<RadiatingDot
size={12}
dotSize={6}
isRadiating={isActive}
className="flex-shrink-0"
/>
) : status === 'EXPIRED' ? (
<XCircle size={14} className="text-gray-500 flex-shrink-0" />
) : (
<Check size={14} className="text-green-600 flex-shrink-0" />
)}
<span
className={`${isOpen ? (expiringSoon ? 'text-orange-600 font-medium' : 'text-gray-700') : 'text-green-700 font-medium'}`}
>
{deadlineText}
</span>
{status === 'REVIEW_PERIOD' ? (
<Tooltip
content={
<div className="flex items-start gap-3 text-left">
<div className="bg-orange-100 p-2 rounded-md flex items-center justify-center">
<Clock className="h-5 w-5 text-orange-600" />
</div>
<div>
Bounty creators get extra time after the deadline to review submissions and
award funds.
</div>
</div>
}
position="top"
width="w-[360px]"
>
<span
className={`${isActive ? (expiringSoon ? 'text-orange-600 font-medium' : 'text-gray-700') : status === 'EXPIRED' ? 'text-gray-500' : 'text-green-700 font-medium'} cursor-help underline decoration-dotted underline-offset-2`}
>
{deadlineText}
</span>
</Tooltip>
) : (
<span
className={`${isActive ? (expiringSoon ? 'text-orange-600 font-medium' : 'text-gray-700') : status === 'EXPIRED' ? 'text-gray-500' : 'text-green-700 font-medium'}`}
>
{deadlineText}
</span>
)}
</div>
)}
</div>
Expand Down
Loading