arbret/frontend/app/components/EmptyState.tsx
counterweight 1a47b3643f
Extract reusable UI components to reduce DRY violations
- Created StatusBadge component: Standardizes status badge display
  - Supports tradeStatus prop for trade-specific styling
  - Supports variant prop for simple badges (success/error/ready)
  - Eliminates repetitive badge style combinations

- Created EmptyState component: Standardizes empty state display
  - Handles loading and empty states consistently
  - Supports message, hint, and action props
  - Used across trades, invites, admin pages

- Created ConfirmationButton component: Standardizes confirmation flows
  - Two-step confirmation pattern (action -> confirm/cancel)
  - Supports different variants (danger/success/primary)
  - Handles loading states automatically
  - Used for cancel, complete, no-show actions

- Migrated pages to use new components:
  - trades/page.tsx: StatusBadge, EmptyState, ConfirmationButton
  - trades/[id]/page.tsx: StatusBadge
  - invites/page.tsx: StatusBadge, EmptyState
  - admin/trades/page.tsx: StatusBadge, EmptyState, ConfirmationButton
  - admin/invites/page.tsx: StatusBadge

Benefits:
- Eliminated ~50+ lines of repetitive badge styling code
- Consistent UI patterns across all pages
- Easier to maintain and update styling
- Better type safety

All tests passing (32 frontend, 33 e2e)
2025-12-25 21:40:07 +01:00

50 lines
1.4 KiB
TypeScript

import { utilityStyles } from "../styles/shared";
interface EmptyStateProps {
/** Message to display when empty */
message: string;
/** Optional hint/subtitle text */
hint?: string;
/** Show loading state instead of empty message */
isLoading?: boolean;
/** Optional action element (e.g., link or button) */
action?: React.ReactNode;
/** Custom style override */
style?: React.CSSProperties;
}
/**
* Standardized empty state component.
* Displays a message when there's no data, or a loading state.
*/
export function EmptyState({ message, hint, isLoading, action, style }: EmptyStateProps) {
if (isLoading) {
return <div style={{ ...utilityStyles.emptyState, ...style }}>Loading...</div>;
}
return (
<div style={{ ...utilityStyles.emptyState, ...style }}>
<p style={styles.emptyText}>{message}</p>
{hint && <p style={styles.emptyHint}>{hint}</p>}
{action && <div style={styles.action}>{action}</div>}
</div>
);
}
const styles: Record<string, React.CSSProperties> = {
emptyText: {
fontFamily: "'DM Sans', system-ui, sans-serif",
color: "rgba(255, 255, 255, 0.6)",
fontSize: "1rem",
margin: 0,
},
emptyHint: {
fontFamily: "'DM Sans', system-ui, sans-serif",
color: "rgba(255, 255, 255, 0.4)",
fontSize: "0.85rem",
marginTop: "0.5rem",
},
action: {
marginTop: "1rem",
},
};