Skip to content
Closed
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
25 changes: 14 additions & 11 deletions frontend/src/components/ContributorProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import type { ContributorBadgeStats } from '../types/badges';
import { computeBadges } from '../types/badges';
import { BadgeGrid } from './badges';
import { TimeAgo } from './common/TimeAgo';
import { BountyTags } from './bounties/BountyTags';
import type { BountyTier } from '../types/bounty';

interface RecentBounty {
title: string;
issueUrl: string;
tier: 1 | 2 | 3;
tier: BountyTier;
earned: number;
completedAt: string;
}
Expand Down Expand Up @@ -111,10 +113,6 @@ function formatJoinDate(isoDate: string): string {
return `Member since ${month} ${d.getFullYear()}`;
}

function tierLabel(tier: 1 | 2 | 3): string {
return `T${tier}`;
}

export const ContributorProfile: React.FC<ContributorProfileProps> = ({
username,
avatarUrl,
Expand Down Expand Up @@ -276,13 +274,18 @@ export const ContributorProfile: React.FC<ContributorProfileProps> = ({
<div className="flex items-center justify-between gap-2">
<div className="flex-1 min-w-0">
<p className="text-sm text-white truncate">{bounty.title}</p>
<p className="text-xs text-gray-500 mt-0.5">
{tierLabel(bounty.tier)} · {bounty.earned.toLocaleString()} FNDRY · {new Date(bounty.completedAt).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
</p>
<div className="flex items-center gap-2 mt-1">
<BountyTags
tier={bounty.tier}
skills={[]}
showTier
className="inline-flex"
/>
<span className="text-xs text-gray-500">
{bounty.earned.toLocaleString()} FNDRY · {new Date(bounty.completedAt).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
</span>
Comment on lines +284 to +286
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add a safe fallback for malformed completedAt values.

Inline date parsing has no invalid-date guard, so bad input can render Invalid Date in the activity feed. This should degrade gracefully (e.g., fallback text) to avoid user-facing noise.

As per coding guidelines, "frontend/**: React/TypeScript frontend. Check: ... Error/loading/empty state handling".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/ContributorProfile.tsx` around lines 284 - 286, The
inline date formatting can produce "Invalid Date" for malformed
bounty.completedAt; update ContributorProfile.tsx to parse and validate the date
before rendering: create a local variable (e.g., parsedDate = new
Date(bounty.completedAt)) and check validity with isNaN(parsedDate.getTime())
(or similar), then use a fallback string (e.g., "—" or "Unknown date") when
invalid; replace the direct new Date(...).toLocaleDateString(...) usage in the
span with the validated/formatted value so the UI degrades gracefully for bad
input.

</div>
</div>
<span className={`text-xs font-medium ${TIER_COLORS[bounty.tier].text}`}>
{tierLabel(bounty.tier)}
</span>
</div>
</a>
))}
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/components/bounties/CreatorBountyCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useState } from 'react';
import { useToast } from '../../hooks/useToast';
import { BountyTags } from './BountyTags';

interface Submission {
id: string;
Expand Down Expand Up @@ -139,6 +140,16 @@ export function CreatorBountyCard({ bounty, onUpdate }: CreatorBountyCardProps)
)}
</div>

<BountyTags
tier={bounty.tier ?? 'T1'}
skills={bounty.required_skills ?? bounty.skills ?? []}
category={bounty.category}
showTier
maxSkills={4}
className="mb-2"
data-testid="creator-bounty"
/>

<div className="flex flex-wrap items-center gap-x-6 gap-y-2 text-sm text-gray-400">
<span className={`font-semibold ${getStatusColor(bounty.status)} uppercase tracking-wider text-xs`}>
{bounty.status.replace(/_/g, ' ')}
Expand Down
Loading