import 'package:flutter/material.dart'; import 'package:uto_manager/domain/entities/personnel_record.dart'; import 'package:uto_manager/domain/entities/position_slot.dart'; /// Displays a single position slot as a card in the inspector panel. /// /// Shows the slot's position title, MOS, rank, and assigned soldier (or VACANT). /// Supports drag-and-drop target for soldier assignment. class PositionSlotCard extends StatelessWidget { const PositionSlotCard({ required this.slot, super.key, this.assignedSoldier, this.onUnassign, this.onAcceptDrop, this.onEdit, this.onDelete, }); final PositionSlot slot; final PersonnelRecord? assignedSoldier; final VoidCallback? onUnassign; final void Function(PersonnelRecord soldier)? onAcceptDrop; /// Called when the user taps the edit button on this slot. final VoidCallback? onEdit; /// Called when the user taps the delete button on this slot. final VoidCallback? onDelete; @override Widget build(BuildContext context) { final theme = Theme.of(context); return DragTarget( onWillAcceptWithDetails: (details) => slot.isVacant, onAcceptWithDetails: (details) => onAcceptDrop?.call(details.data), builder: (context, candidateData, rejectedData) { final isDragOver = candidateData.isNotEmpty; return AnimatedContainer( duration: const Duration(milliseconds: 120), margin: const EdgeInsets.symmetric(vertical: 3, horizontal: 8), decoration: BoxDecoration( color: isDragOver ? theme.colorScheme.primaryContainer.withValues(alpha: 0.6) : slot.isFilled ? theme.colorScheme.surfaceContainerHighest : theme.colorScheme.surfaceContainerLow, border: Border.all( color: isDragOver ? theme.colorScheme.primary : slot.isKeyLeader ? const Color(0xFFFFD700) // Gold for key leaders : theme.colorScheme.outlineVariant, width: isDragOver ? 2 : 1, ), borderRadius: BorderRadius.circular(6), ), child: ListTile( dense: true, contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 2), leading: _buildMosBadge(theme), title: Text( slot.positionTitle, style: theme.textTheme.bodySmall?.copyWith( fontWeight: slot.isKeyLeader ? FontWeight.bold : FontWeight.normal, ), ), subtitle: Text( slot.isFilled ? (assignedSoldier?.name ?? 'Assigned') : 'VACANT', style: theme.textTheme.labelSmall?.copyWith( color: slot.isFilled ? _readinessColor(assignedSoldier?.readinessState) : theme.colorScheme.error, fontWeight: slot.isVacant ? FontWeight.bold : FontWeight.normal, ), ), trailing: _buildTrailing(theme), ), ); }, ); } Widget? _buildTrailing(ThemeData theme) { final hasActions = onUnassign != null || onEdit != null || onDelete != null; if (!hasActions) return null; return Row( mainAxisSize: MainAxisSize.min, children: [ if (slot.isFilled && onUnassign != null) IconButton( icon: const Icon(Icons.person_remove, size: 15), onPressed: onUnassign, tooltip: 'Unassign soldier', padding: EdgeInsets.zero, constraints: const BoxConstraints(minWidth: 26, minHeight: 26), ), if (onEdit != null) IconButton( icon: const Icon(Icons.edit_outlined, size: 15), onPressed: onEdit, tooltip: 'Edit slot', padding: EdgeInsets.zero, constraints: const BoxConstraints(minWidth: 26, minHeight: 26), ), if (onDelete != null) IconButton( icon: Icon(Icons.delete_outline, size: 15, color: theme.colorScheme.error), onPressed: onDelete, tooltip: 'Delete slot', padding: EdgeInsets.zero, constraints: const BoxConstraints(minWidth: 26, minHeight: 26), ), ], ); } Widget _buildMosBadge(ThemeData theme) { return Container( width: 40, height: 40, decoration: BoxDecoration( color: theme.colorScheme.primary.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(4), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( slot.dutyMos.code, style: theme.textTheme.labelSmall?.copyWith( fontWeight: FontWeight.bold, fontSize: 10, color: theme.colorScheme.primary, ), ), Text( slot.authorizedRank.payGrade, style: theme.textTheme.labelSmall?.copyWith( fontSize: 9, color: theme.colorScheme.onSurfaceVariant, ), ), ], ), ); } Color _readinessColor(ReadinessState? state) { switch (state) { case ReadinessState.fullyMissionCapable: return const Color(0xFF4CAF50); case ReadinessState.partiallyMissionCapable: return const Color(0xFFFF9800); case ReadinessState.notMissionCapable: return const Color(0xFFF44336); case ReadinessState.unknown: return Colors.grey; case null: return Colors.grey; } } }