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
37 changes: 21 additions & 16 deletions apps/frontend/src/components/faq/FreshnessBadge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,16 @@ import {
badgeVerifiedBold,
badgeVerifiedWarn,
} from '../../styles/style_config';

interface FreshnessBadgeProps {
reviewStatus: 'verified' | 'pending_review' | 'update_requested' | undefined;
lastVerifiedDate: string | Date | undefined;
reviewIntervalDays: number;
freshnessTier: 'evergreen' | 'seasonal' | 'volatile' | undefined;
compact?: boolean;
}

function daysSince(date: Date): number {
return Math.floor((Date.now() - new Date(date).getTime()) / (1000 * 60 * 60 * 24));
}

export default function FreshnessBadge({
reviewStatus = 'verified',
lastVerifiedDate,
Expand All @@ -28,47 +25,55 @@ export default function FreshnessBadge({
compact = false,
}: FreshnessBadgeProps) {
if (!lastVerifiedDate) return null;

const days = daysSince(new Date(lastVerifiedDate));
const isEvergreen = freshnessTier === 'evergreen' || !freshnessTier;

if (reviewStatus === 'pending_review') {
return (
<span className={`${badgePendingReview} ${compact ? badgeCompact : ''}`}>
<span
className={`${badgePendingReview} ${compact ? badgeCompact : ''}`}
title="This answer is being re-checked against the latest information."
>
⏳ Under review
</span>
);
}

if (reviewStatus === 'update_requested') {
return (
<span className={`${badgeUpdateRequested} ${compact ? badgeCompact : ''}`}>
<span
className={`${badgeUpdateRequested} ${compact ? badgeCompact : ''}`}
title="A community member has flagged this answer as possibly outdated."
>
⚠ Update requested
</span>
);
}

if (isEvergreen) {
return (
<span className={`${badgeVerified} ${compact ? '' : 'font-medium'}`}>
<span
className={`${badgeVerified} ${compact ? '' : 'font-medium'}`}
title="This answer covers timeless information and doesn't need periodic re-review."
>
✓ Verified
</span>
);
}

const nearingExpiry = reviewIntervalDays > 0 && days >= reviewIntervalDays * 0.8;

if (nearingExpiry) {
return (
<span className={badgeVerifiedWarn}>
<span
className={badgeVerifiedWarn}
title={`Verified ${days} days ago. This answer is due for re-review soon.`}
>
✓ Verified {days}d ago
</span>
);
}

return (
<span className={badgeVerifiedBold}>
<span
className={badgeVerifiedBold}
title={`Verified ${days} days ago and still within its review window.`}
>
✓ Verified {days}d ago
</span>
);
}
}
69 changes: 69 additions & 0 deletions apps/frontend/src/components/faq/__tests__/FreshnessBadge.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
import React from 'react';
import FreshnessBadge from '@/components/faq/FreshnessBadge';

describe('FreshnessBadge tooltips', () => {
it('shows a tooltip explaining "pending_review" status', () => {
render(
<FreshnessBadge
reviewStatus="pending_review"
lastVerifiedDate={new Date()}
reviewIntervalDays={30}
freshnessTier="seasonal"
/>
);
const badge = screen.getByText('⏳ Under review');
expect(badge).toHaveAttribute(
'title',
'This answer is being re-checked against the latest information.'
);
});

it('shows a tooltip explaining "update_requested" status', () => {
render(
<FreshnessBadge
reviewStatus="update_requested"
lastVerifiedDate={new Date()}
reviewIntervalDays={30}
freshnessTier="seasonal"
/>
);
const badge = screen.getByText('⚠ Update requested');
expect(badge).toHaveAttribute(
'title',
'A community member has flagged this answer as possibly outdated.'
);
});

it('shows a tooltip explaining "verified" evergreen status', () => {
render(
<FreshnessBadge
reviewStatus="verified"
lastVerifiedDate={new Date()}
reviewIntervalDays={30}
freshnessTier="evergreen"
/>
);
const badge = screen.getByText('✓ Verified');
expect(badge).toHaveAttribute(
'title',
"This answer covers timeless information and doesn't need periodic re-review."
);
});

it('shows a days-based tooltip when verified and non-evergreen', () => {
const oldDate = new Date();
oldDate.setDate(oldDate.getDate() - 5);
render(
<FreshnessBadge
reviewStatus="verified"
lastVerifiedDate={oldDate}
reviewIntervalDays={30}
freshnessTier="volatile"
/>
);
const badge = screen.getByText(/Verified 5d ago/);
expect(badge).toHaveAttribute('title', expect.stringContaining('Verified 5 days ago'));
});
});