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
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:
@@ -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;
|
||||
Reference in New Issue
Block a user