import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import '../../../common/theme/theme.dart'; import '../../../sample/community_sample_data.dart'; /// Palet lembut khusus EnakCommunity. class CommunityColor { static const Color background = Color(0xFFFAF7F5); static const Color blush = Color(0xFFFFEEEC); static const Color blushDeep = Color(0xFFFFDCD7); static const Color divider = Color(0xFFF1ECE9); static const Color muted = Color(0xFF9A918D); /// Pasangan (latar, aksen) untuk komunitas & avatar. static const List<(Color, Color)> tints = [ (Color(0xFFFFF1E6), Color(0xFFE07A3F)), // peach (Color(0xFFF1ECE4), Color(0xFF8A6A4F)), // latte (Color(0xFFEAF3FC), Color(0xFF4A86C5)), // sky (Color(0xFFFFEEEC), Color(0xFFD14B4B)), // blush (Color(0xFFE9F7EF), Color(0xFF3F9A67)), // mint (Color(0xFFF1EDFB), Color(0xFF7D63C9)), // lavender ]; static (Color, Color) tintOf(int index) => tints[index % tints.length]; } /// Komunitas yang diikuti, dibagi antar layar. Sementara di memori sampai /// API komunitas tersedia. final ValueNotifier> joinedCommunityIds = ValueNotifier({ for (final group in communityGroups) if (group.isJoined) group.id, }); void toggleJoinCommunity(String groupId) { final ids = {...joinedCommunityIds.value}; if (!ids.remove(groupId)) ids.add(groupId); joinedCommunityIds.value = ids; } /// Bayangan tipis agar kartu tampak "mengambang" tanpa garis tepi. const List communitySoftShadow = [ BoxShadow(color: Color(0x0D3B2A24), blurRadius: 24, offset: Offset(0, 8)), ]; BoxDecoration communityCardDecoration({double radius = 20}) => BoxDecoration( color: AppColor.white, borderRadius: BorderRadius.circular(radius), boxShadow: communitySoftShadow, ); /// "Baru saja", "5 mnt", "3 jam", "2 hari", lalu tanggal pendek. String formatTimeAgo(DateTime time) { final diff = DateTime.now().difference(time); if (diff.inMinutes < 1) return 'Baru saja'; if (diff.inHours < 1) return '${diff.inMinutes} mnt'; if (diff.inDays < 1) return '${diff.inHours} jam'; if (diff.inDays < 7) return '${diff.inDays} hari'; return DateFormat('d MMM', 'id_ID').format(time); } /// 950 → "950", 12480 → "12,4rb". String formatCompact(int value) { if (value < 1000) return '$value'; if (value < 1000000) { return '${NumberFormat('0.#', 'id_ID').format(value / 1000)}rb'; } return '${NumberFormat('0.#', 'id_ID').format(value / 1000000)}jt'; } /// Avatar inisial dengan warna lembut yang konsisten per anggota. class CommunityAvatar extends StatelessWidget { const CommunityAvatar({super.key, required this.member, this.size = 40}); final CommunityMember member; final double size; @override Widget build(BuildContext context) { final (background, accent) = CommunityColor.tintOf( member.id.codeUnits.fold(0, (sum, c) => sum + c), ); return Container( width: size, height: size, alignment: Alignment.center, decoration: BoxDecoration(color: background, shape: BoxShape.circle), child: Text( member.initials, style: AppStyle.md.copyWith( fontSize: size * 0.36, color: accent, fontWeight: FontWeight.w700, ), ), ); } } /// Deretan avatar bertumpuk, mis. peserta event. class CommunityAvatarStack extends StatelessWidget { const CommunityAvatarStack({ super.key, required this.members, this.size = 26, this.max = 3, }); final List members; final double size; final int max; @override Widget build(BuildContext context) { final shown = members.take(max).toList(); final overlap = size * 0.65; return SizedBox( width: size + overlap * (shown.length - 1), height: size, child: Stack( children: [ for (var i = 0; i < shown.length; i++) Positioned( left: overlap * i, child: Container( padding: const EdgeInsets.all(1.5), decoration: const BoxDecoration( color: AppColor.white, shape: BoxShape.circle, ), child: CommunityAvatar(member: shown[i], size: size - 3), ), ), ], ), ); } } /// Emoji komunitas di atas kotak berwarna lembut. class CommunityEmojiBadge extends StatelessWidget { const CommunityEmojiBadge({super.key, required this.group, this.size = 44}); final CommunityGroup group; final double size; @override Widget build(BuildContext context) { final (background, _) = CommunityColor.tintOf(group.tint); return Container( width: size, height: size, alignment: Alignment.center, decoration: BoxDecoration( color: background, borderRadius: BorderRadius.circular(size * 0.32), ), child: Text(group.emoji, style: TextStyle(fontSize: size * 0.48)), ); } } /// Tombol pil "Gabung" / "Tergabung". class CommunityJoinButton extends StatelessWidget { const CommunityJoinButton({ super.key, required this.isJoined, required this.onPressed, this.compact = false, }); final bool isJoined; final VoidCallback onPressed; final bool compact; @override Widget build(BuildContext context) { return AnimatedContainer( duration: const Duration(milliseconds: 200), decoration: BoxDecoration( color: isJoined ? CommunityColor.blush : AppColor.primary, borderRadius: BorderRadius.circular(999), ), child: Material( type: MaterialType.transparency, child: InkWell( borderRadius: BorderRadius.circular(999), onTap: onPressed, child: Padding( padding: EdgeInsets.symmetric( horizontal: compact ? 12 : 16, vertical: compact ? 6 : 8, ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( isJoined ? Icons.check_rounded : Icons.add_rounded, size: compact ? 14 : 16, color: isJoined ? AppColor.primary : AppColor.white, ), const SizedBox(width: 4), Text( isJoined ? 'Tergabung' : 'Gabung', style: (compact ? AppStyle.xs : AppStyle.sm).copyWith( color: isJoined ? AppColor.primary : AppColor.white, fontWeight: FontWeight.w700, ), ), ], ), ), ), ), ); } } /// Judul seksi dengan aksi opsional di kanan. class CommunitySectionTitle extends StatelessWidget { const CommunitySectionTitle({ super.key, required this.title, this.actionLabel, this.onAction, }); final String title; final String? actionLabel; final VoidCallback? onAction; @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.fromLTRB(20, 20, 12, 10), child: Row( children: [ Expanded( child: Text( title, style: AppStyle.lg.copyWith(fontWeight: FontWeight.w700), ), ), if (actionLabel != null) TextButton( onPressed: onAction, child: Text( actionLabel!, style: AppStyle.sm.copyWith( color: AppColor.primary, fontWeight: FontWeight.w700, ), ), ), ], ), ); } }