import 'dart:math' as math; import 'package:flutter/material.dart'; import '../../../core/theme/tokens.dart'; import '../../../core/ui/app_icons.dart'; import '../../quest/widgets/quest_card_tile.dart' show QuestIconBox, questImageFor, questSubLine; import '../home_data.dart'; /// 홈 「TODAY'S QUEST」 묶음 — Figma 「홈」 `2097:16428`: 343×270 · r12 안에 343×90 줄 /// 세 개를 간격 없이 쌓는다. 앞 두 줄에 `#242424` 1 획(사본 spec) — 줄 사이 구분선으로 보인다. /// /// 항목은 설명 프레임 `2097:16397` 「관리자 퀘스트 1·2·3 — 최신순 최대 3개 · 클릭 시 해당 /// 퀘스트 상세」. 사용자 생성 퀘스트는 저장소가 거른다. class QuestRows extends StatelessWidget { const QuestRows({super.key, required this.quests, required this.onTap}); final List quests; final ValueChanged onTap; /// 시안 줄 수. 나머지는 「더 보기」(퀘스트 목록)에서 본다. static const maxRows = 3; @override Widget build(BuildContext context) => ClipRRect( borderRadius: BorderRadius.circular(12), child: Column( children: [ for (final (i, q) in quests.take(maxRows).indexed) QuestRow( quest: q, divider: i < math.min(quests.length, maxRows) - 1, onTap: () => onTap(q), ), ], ), ); } /// 한 줄 — `2097:16428` 「출석체크 / 오늘의 출석체크」 행: #1A1A1C · 패딩 20 · 왼쪽 50×50 /// #2E2E2E r16 아이콘 상자(42 이미지) · 간격 19 · 제목 18/600 + 보조 14/500(간격 4) · /// 오른쪽 chevron 24 #4F4F4F. /// /// 보조 줄은 카테고리(사본 「오늘의 출석체크」 · 「커뮤니티」)다. 「홈」 프레임에는 보상 숫자 · /// 진행 상태가 없어 그리지 않는다. 타임어택 남은 시간도 그리지 않는다 — 설명 프레임 /// `2097:16397` 데이터는 「어드민에서 등록된 퀘스트」뿐이고, FR-QST-06 · 정책 원장 C-94 의 카운트다운 /// 표시 위치는 퀘스트 목록 · 상세다. class QuestRow extends StatelessWidget { const QuestRow({ super.key, required this.quest, this.onTap, this.divider = false, }); final QuestSummary quest; final VoidCallback? onTap; /// 아래 변 `#242424` 1 획. final bool divider; static const height = 90.0; @override Widget build(BuildContext context) { return Material( color: AppColors.cosmicSurfaceContainer, shape: divider ? const Border(bottom: BorderSide(color: Color(0xFF242424))) : null, child: InkWell( onTap: onTap, child: ConstrainedBox( // 글자 확대 때는 늘어난다 — 고정하면 잘린다. constraints: const BoxConstraints(minHeight: height), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), child: Row( children: [ QuestIconBox(image: questImageFor(quest.type, quest.title)), const SizedBox(width: 19), Expanded( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( quest.title, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( fontFamily: AppFonts.body, fontSize: 18, fontWeight: FontWeight.w600, height: 20 / 18, color: AppColors.onCosmic, ), ), const SizedBox(height: 4), // 진행중 탭 카드와 같은 일시 정지 표기(정책 원장 C-83). _sub(questSubLine(quest.category ?? '', quest.pausedSession)), ], ), ), const SizedBox(width: AppSpace.s2), const ImageIcon( AppIcons.chevronRight, size: 24, color: Color(0xFF4F4F4F), ), ], ), ), ), ), ); } } Widget _sub(String text) => Text( text, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( fontFamily: AppFonts.body, fontSize: 14, fontWeight: FontWeight.w500, height: 20 / 14, color: AppColors.onCosmicVariant, ), );