feat: add type-specific metadata and expiration badges to documents UX (refs #43)
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 2m46s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 37s
Deploy to Staging / Verify Staging (pull_request) Successful in 6s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 6s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped

- Create ExpirationBadge component with 30-day warning and expired states
- Create DocumentCardMetadata component for type-specific field display
- Update DocumentsPage to show metadata and expiration badges on cards
- Update DocumentsMobileScreen with metadata and badges (mobile variant)
- Redesign DocumentDetailPage with side-by-side layout (desktop) and
  stacked layout (mobile) showing full metadata panel
- Add 33 unit tests for new components
- Fix jest.config.ts testMatch pattern for test discovery

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Eric Gullickson
2026-01-17 20:29:54 -06:00
parent 2ebae468c6
commit b0e392fef1
8 changed files with 742 additions and 53 deletions

View File

@@ -0,0 +1,60 @@
import React from 'react';
import dayjs from 'dayjs';
interface ExpirationBadgeProps {
/** The expiration date in ISO format (YYYY-MM-DD) */
expirationDate: string | null | undefined;
/** Additional CSS classes */
className?: string;
}
/**
* Displays a warning badge for documents expiring within 30 days or already expired.
* Returns null if no expiration date is provided.
*/
export const ExpirationBadge: React.FC<ExpirationBadgeProps> = ({
expirationDate,
className = '',
}) => {
if (!expirationDate) {
return null;
}
const today = dayjs().startOf('day');
const expDate = dayjs(expirationDate).startOf('day');
const daysUntilExpiration = expDate.diff(today, 'day');
// Already expired
if (daysUntilExpiration < 0) {
return (
<span
className={`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400 ${className}`}
>
Expired
</span>
);
}
// Expiring within 30 days
if (daysUntilExpiration <= 30) {
const label =
daysUntilExpiration === 0
? 'Expires today'
: daysUntilExpiration === 1
? 'Expires tomorrow'
: `Expires in ${daysUntilExpiration} days`;
return (
<span
className={`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-400 ${className}`}
>
{label}
</span>
);
}
// Not expiring soon - no badge
return null;
};
export default ExpirationBadge;