fromis_9/app/lib/views/schedule/widgets/search_card.dart

258 lines
9.6 KiB
Dart
Raw Permalink Normal View History

/// 검색 결과 카드 위젯
library;
import 'package:flutter/material.dart';
import '../../../core/constants.dart';
import '../../../models/schedule.dart';
import 'member_chip.dart';
import 'schedule_card.dart' show decodeHtmlEntities;
/// 검색 결과 카드 (웹과 동일한 디자인 - 왼쪽에 날짜, 오른쪽에 내용)
class SearchScheduleCard extends StatelessWidget {
final Schedule schedule;
final Color categoryColor;
const SearchScheduleCard({
super.key,
required this.schedule,
required this.categoryColor,
});
/// 날짜 파싱
Map<String, dynamic>? _parseDate(String? dateStr) {
if (dateStr == null) return null;
try {
final date = DateTime.parse(dateStr);
const weekdays = ['', '', '', '', '', '', ''];
return {
'year': date.year,
'month': date.month,
'day': date.day,
'weekday': weekdays[date.weekday % 7],
'isSunday': date.weekday == 7,
'isSaturday': date.weekday == 6,
};
} catch (_) {
return null;
}
}
@override
Widget build(BuildContext context) {
final memberList = schedule.memberList;
final dateInfo = _parseDate(schedule.date);
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.04),
blurRadius: 12,
offset: const Offset(0, 2),
),
],
border: Border.all(
color: AppColors.border.withValues(alpha: 0.5),
width: 1,
),
),
child: IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// 왼쪽 날짜 영역 (카드 높이에 맞춤)
if (dateInfo != null)
Container(
width: 72,
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 6),
decoration: const BoxDecoration(
color: AppColors.background,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(7),
bottomLeft: Radius.circular(7),
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 년도
Text(
'${dateInfo['year']}',
style: const TextStyle(
fontFamily: 'Pretendard',
fontSize: 10,
color: AppColors.textTertiary,
),
),
// 월.일 (줄바꿈 방지)
FittedBox(
fit: BoxFit.scaleDown,
child: Text(
'${dateInfo['month']}.${dateInfo['day']}',
style: const TextStyle(
fontFamily: 'Pretendard',
fontSize: 18,
fontWeight: FontWeight.bold,
color: AppColors.textPrimary,
),
),
),
// 요일
Text(
'${dateInfo['weekday']}요일',
style: TextStyle(
fontFamily: 'Pretendard',
fontSize: 11,
fontWeight: FontWeight.w500,
color: dateInfo['isSunday'] == true
? Colors.red.shade500
: dateInfo['isSaturday'] == true
? Colors.blue.shade500
: AppColors.textSecondary,
),
),
],
),
),
// 오른쪽 콘텐츠 영역
Expanded(
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 시간 및 카테고리 뱃지
Row(
children: [
// 시간 뱃지
if (schedule.formattedTime != null)
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 2,
),
decoration: BoxDecoration(
color: categoryColor,
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.access_time,
size: 10,
color: Colors.white,
),
const SizedBox(width: 4),
Text(
schedule.formattedTime!,
style: const TextStyle(
fontFamily: 'Pretendard',
fontSize: 10,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
],
),
),
if (schedule.formattedTime != null)
const SizedBox(width: 6),
// 카테고리 뱃지
if (schedule.categoryName != null)
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 2,
),
decoration: BoxDecoration(
color: categoryColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(12),
),
child: Text(
schedule.categoryName!,
style: TextStyle(
fontFamily: 'Pretendard',
fontSize: 10,
fontWeight: FontWeight.w500,
color: categoryColor,
),
),
),
],
),
const SizedBox(height: 8),
// 제목
Text(
decodeHtmlEntities(schedule.title),
style: const TextStyle(
fontFamily: 'Pretendard',
fontSize: 14,
fontWeight: FontWeight.bold,
color: AppColors.textPrimary,
height: 1.4,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
// 출처 (빈 문자열이 아닌 경우에만 표시)
if (schedule.sourceName != null &&
schedule.sourceName!.isNotEmpty) ...[
const SizedBox(height: 6),
Row(
children: [
const Icon(
Icons.link,
size: 12,
color: AppColors.textTertiary,
),
const SizedBox(width: 4),
Expanded(
child: Text(
schedule.sourceName!,
style: const TextStyle(
fontFamily: 'Pretendard',
fontSize: 12,
color: AppColors.textTertiary,
),
overflow: TextOverflow.ellipsis,
),
),
],
),
],
// 멤버
if (memberList.isNotEmpty) ...[
const SizedBox(height: 10),
// divider (전체 너비)
Container(
width: double.infinity,
height: 1,
color: AppColors.divider,
),
const SizedBox(height: 10),
Wrap(
spacing: 4,
runSpacing: 4,
children: memberList.length >= 5
? [
const SearchMemberChip(name: '프로미스나인'),
]
: memberList
.map((name) => SearchMemberChip(name: name))
.toList(),
),
],
],
),
),
),
],
),
),
);
}
}