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
34 changes: 26 additions & 8 deletions app/earn/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export default function EarnPage() {
loadMore,
sort,
handleSortChange,
bountyFilter,
handleBountyFilterChange,
selectedHubs,
handleHubsChange,
restoredScrollPosition,
Expand All @@ -34,19 +36,35 @@ export default function EarnPage() {
{ label: 'RSC amount', value: '-total_amount' },
];

// Available bounty type options
const bountyTypeOptions = [
{ label: 'All Bounties', value: 'ALL' },
{ label: 'Foundation Only', value: 'FOUNDATION' },
{ label: 'Community Only', value: 'COMMUNITY' },
];

const renderFilters = () => (
<div className="mt-5 space-y-3">
{/* Top filter bar */}
<div className="flex items-center gap-0 sm:gap-2 flex-wrap justify-between">
<div className="w-1/2 sm:!w-[220px] flex-1 sm:!flex-none pr-1 sm:!pr-0">
<HubsSelector
selectedHubs={selectedHubs}
onChange={handleHubsChange}
displayCountOnly
hideSelectedItems={true}
/>
<div className="flex items-center gap-2 flex-1 sm:flex-none">
<div className="w-[180px] sm:!w-[220px]">
<HubsSelector
selectedHubs={selectedHubs}
onChange={handleHubsChange}
displayCountOnly
hideSelectedItems={true}
/>
</div>
<div className="w-[140px] sm:!w-[160px]">
<SortDropdown
value={bountyFilter}
onChange={(opt: SortOption) => handleBountyFilterChange(opt.value as any)}
options={bountyTypeOptions}
/>
</div>
</div>
<div className="w-1/2 sm:!w-[120px] flex-1 sm:!flex-none pl-1 sm:!pl-0">
<div className="w-1/2 sm:!w-[120px] flex-1 sm:!flex-none pl-1 sm:!pl-0 mt-2 sm:mt-0">
<SortDropdown
value={sort}
onChange={(opt: SortOption) => handleSortChange(opt.value)}
Expand Down
64 changes: 42 additions & 22 deletions components/Bounty/BountyMetadataLine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ 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, XCircle } from 'lucide-react';
import { useCurrencyPreference } from '@/contexts/CurrencyPreferenceContext';
import { BountyStatus } from '@/types/bounty';

interface BountyMetadataLineProps {
amount: number;
Expand All @@ -13,7 +14,7 @@ interface BountyMetadataLineProps {
className?: string;
solutionsCount?: number;
showDeadline?: boolean;
bountyStatus?: 'OPEN' | 'CLOSED' | 'ASSESSMENT';
bountyStatus?: BountyStatus;
/**
* If true, the amount is already in the target currency and should not be converted.
* Useful when the caller has pre-calculated the amount (e.g., Foundation bounty flat fee).
Expand All @@ -33,44 +34,63 @@ export const BountyMetadataLine = ({
}: BountyMetadataLineProps) => {
const { showUSD } = useCurrencyPreference();

// Format the deadline text
const deadlineText =
bountyStatus === 'ASSESSMENT'
? 'Assessment Period'
: isOpen
? expirationDate
? formatDeadline(expirationDate)
: 'No deadline'
: 'Completed';
// Helper to determine the deadline text
const getDeadlineText = () => {
if (bountyStatus === 'ASSESSMENT') return 'Assessment Period';
if (bountyStatus === 'EXPIRED') return 'Expired';
if (bountyStatus === 'CANCELLED') return 'Cancelled';
if (isOpen) {
return expirationDate ? formatDeadline(expirationDate) : 'No deadline';
}
return 'Completed';
};

const deadlineText = getDeadlineText();
const isInactive = bountyStatus === 'EXPIRED' || bountyStatus === 'CANCELLED';

// Helper to determine the status icon
const renderStatusIcon = () => {
if (isOpen) {
return <RadiatingDot size={12} dotSize={6} isRadiating={isOpen} className="flex-shrink-0" />;
}
if (isInactive) {
return <XCircle size={14} className="text-gray-400 flex-shrink-0" />;
}
return <Check size={14} className="text-green-600 flex-shrink-0" />;
};

// Helper to determine the status text color
const getStatusColorClass = () => {
if (isOpen) {
return expiringSoon ? 'text-orange-600 font-medium' : 'text-gray-700';
}
if (isInactive) {
return 'text-gray-500 italic';
}
return 'text-green-700 font-medium';
};

return (
<div className={`space-y-3 ${className}`}>
{/* Badges and date in one row */}
<div className="flex justify-between items-center w-full">
{/* Badges */}
<div className="flex flex-wrap gap-2">
<ContentTypeBadge type="bounty" />
<ContentTypeBadge type="bounty" className={isInactive ? 'opacity-50' : ''} />
<CurrencyBadge
amount={amount}
size="sm"
variant={isOpen ? 'badge' : 'disabled'}
currency={showUSD ? 'USD' : 'RSC'}
skipConversion={skipConversion}
className={isInactive ? 'grayscale opacity-60' : ''}
/>
</div>

{showDeadline && (
<div className="flex items-center gap-2 text-sm">
{isOpen ? (
<RadiatingDot size={12} dotSize={6} isRadiating={isOpen} className="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>
{renderStatusIcon()}
<span className={getStatusColorClass()}>{deadlineText}</span>
</div>
)}
</div>
Expand Down
1 change: 1 addition & 0 deletions components/Bounty/lib/bountyUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,7 @@ const getCreatorUserId = (bounty: Bounty): number | undefined => {
* @returns True if the bounty was created by the Foundation account
*/
export const isFoundationBounty = (bounty: Bounty): boolean => {
if (bounty.createdBy?.isOfficialAccount) return true;
if (!FOUNDATION_USER_ID) return false;

const creatorUserId = getCreatorUserId(bounty);
Expand Down
Loading