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)
This commit is contained in:
parent
b86b506d72
commit
1a47b3643f
9 changed files with 309 additions and 425 deletions
|
|
@ -3,12 +3,13 @@
|
|||
import { useState } from "react";
|
||||
import { invitesApi } from "../api";
|
||||
import { PageLayout } from "../components/PageLayout";
|
||||
import { StatusBadge } from "../components/StatusBadge";
|
||||
import { useRequireAuth } from "../hooks/useRequireAuth";
|
||||
import { useAsyncData } from "../hooks/useAsyncData";
|
||||
import { components } from "../generated/api";
|
||||
import constants from "../../../shared/constants.json";
|
||||
import { Permission } from "../auth-context";
|
||||
import { cardStyles, typographyStyles, badgeStyles, buttonStyles } from "../styles/shared";
|
||||
import { cardStyles, typographyStyles, buttonStyles } from "../styles/shared";
|
||||
|
||||
// Use generated type from OpenAPI schema
|
||||
type Invite = components["schemas"]["UserInviteResponse"];
|
||||
|
|
@ -67,10 +68,10 @@ export default function InvitesPage() {
|
|||
</div>
|
||||
|
||||
{invites.length === 0 ? (
|
||||
<div style={styles.emptyState}>
|
||||
<p style={styles.emptyText}>You don't have any invites yet.</p>
|
||||
<p style={styles.emptyHint}>Contact an admin if you need invite codes to share.</p>
|
||||
</div>
|
||||
<EmptyState
|
||||
message="You don't have any invites yet."
|
||||
hint="Contact an admin if you need invite codes to share."
|
||||
/>
|
||||
) : (
|
||||
<div style={styles.sections}>
|
||||
{/* Ready Invites */}
|
||||
|
|
@ -107,9 +108,7 @@ export default function InvitesPage() {
|
|||
<div key={invite.id} style={styles.inviteCardSpent}>
|
||||
<div style={styles.inviteCode}>{invite.identifier}</div>
|
||||
<div style={styles.inviteeMeta}>
|
||||
<span style={{ ...badgeStyles.badge, ...badgeStyles.badgeSuccess }}>
|
||||
Used
|
||||
</span>
|
||||
<StatusBadge variant="success">Used</StatusBadge>
|
||||
<span style={styles.inviteeEmail}>by {invite.used_by_email}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -126,9 +125,7 @@ export default function InvitesPage() {
|
|||
{revokedInvites.map((invite) => (
|
||||
<div key={invite.id} style={styles.inviteCardRevoked}>
|
||||
<div style={styles.inviteCode}>{invite.identifier}</div>
|
||||
<span style={{ ...badgeStyles.badge, ...badgeStyles.badgeError }}>
|
||||
Revoked
|
||||
</span>
|
||||
<StatusBadge variant="error">Revoked</StatusBadge>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue