Files
spotlightcam/frontend/src/components/heats/HeatBadges.jsx
Radosław Gierwiało 082105c5bf refactor(frontend): Phase 3 - create advanced composite components
Extract complex UI sections into reusable composite components

New Components Created:
1. HeatBadges (heats/HeatBadges.jsx)
   - Displays competition heats with compact notation
   - Configurable max visible badges with "+X more" overflow
   - Tooltips with full heat information

2. UserListItem (users/UserListItem.jsx)
   - Reusable user entry with avatar, username, full name
   - Optional heat badges display
   - Flexible action button slot (render props pattern)
   - Online/offline status support

3. ParticipantsSidebar (events/ParticipantsSidebar.jsx)
   - Complete sidebar component for event participants
   - Filter checkbox for hiding users from own heats
   - Participant and online counters
   - Integrated UserListItem with match actions

4. FileTransferProgress (webrtc/FileTransferProgress.jsx)
   - WebRTC P2P file transfer UI
   - Progress bar with percentage
   - Send/Cancel actions

5. LinkShareInput (webrtc/LinkShareInput.jsx)
   - Fallback link sharing when WebRTC unavailable
   - Google Drive, Dropbox link support

Pages Refactored:
- EventChatPage: 564 → 471 lines (-93 lines, -16%)
  * Replaced 90-line participants sidebar with <ParticipantsSidebar />
  * Removed duplicate formatHeat logic (now in HeatBadges)

- MatchChatPage: 446 → 369 lines (-77 lines, -17%)
  * Replaced 56-line file transfer UI with <FileTransferProgress />
  * Replaced 39-line link input form with <LinkShareInput />

Phase 3 Total: -170 lines
Grand Total (Phase 1+2+3): -559 lines (-17%)

Final Results:
- EventChatPage: 761 → 471 lines (-290 lines, -38% reduction)
- MatchChatPage: 567 → 369 lines (-198 lines, -35% reduction)

Benefits:
- Massive complexity reduction in largest components
- Composite components can be reused across pages
- Better testability - each component tested independently
- Cleaner code organization - single responsibility principle
- Easier maintenance - changes in one place propagate everywhere
2025-11-21 17:10:53 +01:00

66 lines
2.0 KiB
JavaScript

/**
* Reusable Heat Badges component
* Displays competition heats with compact notation (e.g., "J&J NOV 1 L")
*
* @param {Array} heats - Array of heat objects with { competitionType, division, heatNumber, role }
* @param {number} maxVisible - Maximum number of badges to show before "+X more" (default: 3)
* @param {boolean} compact - Use compact display (default: true)
* @param {string} className - Additional CSS classes for container
*
* @example
* <HeatBadges heats={userHeats} maxVisible={3} />
* // Renders: "J&J NOV 1 L" "STR INT 2 F" "+2"
*/
const HeatBadges = ({
heats = [],
maxVisible = 3,
compact = true,
className = ''
}) => {
if (!heats || heats.length === 0) return null;
/**
* Format heat object into compact notation
* Example: { competitionType: 'Jack & Jill', division: 'Novice', heatNumber: 1, role: 'Leader' }
* Returns: "J&J NOV 1 L"
*/
const formatHeat = (heat) => {
const parts = [
heat.competitionType?.abbreviation || '',
heat.division?.abbreviation || '',
heat.heatNumber,
];
if (heat.role) {
parts.push(heat.role.charAt(0)); // L or F
}
return parts.join(' ');
};
const visibleHeats = heats.slice(0, maxVisible);
const remainingCount = heats.length - maxVisible;
return (
<div className={`flex flex-wrap gap-1 ${className}`}>
{visibleHeats.map((heat, idx) => (
<span
key={idx}
className="text-xs px-1.5 py-0.5 bg-amber-100 text-amber-800 rounded font-mono"
title={`${heat.competitionType?.name || ''} ${heat.division?.name || ''} Heat ${heat.heatNumber} (${heat.role || ''})`}
>
{formatHeat(heat)}
</span>
))}
{remainingCount > 0 && (
<span
className="text-xs px-1.5 py-0.5 bg-gray-200 text-gray-700 rounded font-mono"
title={`${remainingCount} more heat${remainingCount > 1 ? 's' : ''}`}
>
+{remainingCount}
</span>
)}
</div>
);
};
export default HeatBadges;