import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../domain/catalog/catalog_item.dart'; import '../../state/catalog_providers.dart'; import '../widgets/reference_expand_card.dart'; class ProtocolPreviewScreen extends ConsumerWidget { const ProtocolPreviewScreen({super.key, required this.item}); final CatalogItem item; @override Widget build(BuildContext context, WidgetRef ref) { return Scaffold( appBar: AppBar( title: Text(item.title, maxLines: 1, overflow: TextOverflow.ellipsis), ), body: ListView( padding: const EdgeInsets.fromLTRB(16, 16, 16, 96), children: [ _Header(item: item), const SizedBox(height: 16), ..._buildBody(context), const SizedBox(height: 24), _References(referenceIds: item.referenceIds), ], ), bottomNavigationBar: const _ImportFooter(), ); } List _buildBody(BuildContext context) { return switch (item) { ProtocolCatalogItem p => [ _section(context, '무엇 (What)', p.what), _section(context, '언제 (When)', p.whenText), _section(context, '도즈 (Dose)', p.dose), _section(context, '왜 (Why)', p.why), if (p.how.isNotEmpty) _howSection(context, p.how), _section(context, '체크 (Check)', p.checkText), if (p.caution != null) _section(context, '주의 (Caution)', p.caution!), if (p.defaultAnchor != null) _section(context, '기본 앵커', _anchorText(p.defaultAnchor!)), if (p.minDoseForStart != null) _section(context, '최소 도즈 (시작용)', p.minDoseForStart!), if (p.sourceDoc != null) _section(context, '출처 문서', p.sourceDoc!), ], BreakCatalogItem b => [ _section(context, '요약 (Huberman)', b.hubermanSummary), _section(context, '구분', b.breakCategory), if (b.phases.isNotEmpty) _section(context, '단계', b.phases.join(' / ')), if (b.defaultCommonFrames.isNotEmpty) _section(context, '기본 공통 프레임', b.defaultCommonFrames.join(', ')), if (b.tools.isNotEmpty) _section(context, '도구', b.tools.join(', ')), if (b.medicalWarning != null) _section(context, '의료 경고', b.medicalWarning!), ], DietCatalogItem d => [ _section(context, '핵심', d.core), if (d.strengths.isNotEmpty) _section(context, '강점', d.strengths.join('\n• ')), if (d.weaknesses.isNotEmpty) _section(context, '약점', d.weaknesses.join('\n• ')), if (d.koreanContextFit != null) _section(context, '한국 컨텍스트 적합도', d.koreanContextFit!), if (d.starterLevers.isNotEmpty) _section(context, '시작 레버', d.starterLevers.join(', ')), if (d.medicalWarning != null) _section(context, '의료 경고', d.medicalWarning!), if (d.linkedProtocolIds.isNotEmpty) _section(context, '연결 프로토콜', d.linkedProtocolIds.join(', ')), ], }; } String _anchorText(Map m) { final when = m['when'] ?? ''; final after = m['after_what'] ?? ''; if (when == '' && after == '') return m.toString(); return [if (when != '') 'when: $when', if (after != '') 'after: $after'] .join(' · '); } Widget _section(BuildContext context, String label, String body) { return Padding( padding: const EdgeInsets.only(bottom: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: Theme.of(context).textTheme.labelLarge), const SizedBox(height: 4), Text(body, style: Theme.of(context).textTheme.bodyMedium), ], ), ); } Widget _howSection(BuildContext context, List steps) { return Padding( padding: const EdgeInsets.only(bottom: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('어떻게 (How)', style: Theme.of(context).textTheme.labelLarge), const SizedBox(height: 4), for (var i = 0; i < steps.length; i++) Padding( padding: const EdgeInsets.only(bottom: 2), child: Text('${i + 1}. ${steps[i]}', style: Theme.of(context).textTheme.bodyMedium), ), ], ), ); } } class _Header extends StatelessWidget { const _Header({required this.item}); final CatalogItem item; @override Widget build(BuildContext context) { final dc = item.displayCategory; return Row( children: [ Icon(dc.icon, size: 20), const SizedBox(width: 8), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(dc.label, style: Theme.of(context).textTheme.labelMedium?.copyWith( color: Colors.grey, )), if (item.titleEn != null) Text(item.titleEn!, style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Colors.grey, )), ], ), ), if (item.evidenceStrength != null) Padding( padding: const EdgeInsets.only(left: 8), child: Text('근거: ${item.evidenceStrength!}', style: Theme.of(context).textTheme.bodySmall), ), ], ); } } class _References extends ConsumerWidget { const _References({required this.referenceIds}); final List referenceIds; @override Widget build(BuildContext context, WidgetRef ref) { if (referenceIds.isEmpty) return const SizedBox.shrink(); final refsAsync = ref.watch(referencesByIdsProvider(referenceIds)); return refsAsync.when( loading: () => const SizedBox.shrink(), error: (_, _) => const SizedBox.shrink(), data: (refs) { if (refs.isEmpty) return const SizedBox.shrink(); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('참고 (${refs.length})', style: Theme.of(context).textTheme.titleSmall), const SizedBox(height: 8), for (final r in refs) ReferenceExpandCard(reference: r), ], ); }, ); } } class _ImportFooter extends StatelessWidget { const _ImportFooter(); @override Widget build(BuildContext context) { return SafeArea( child: Padding( padding: const EdgeInsets.all(16), child: Tooltip( message: '다음 업데이트 예정', child: FilledButton.icon( onPressed: null, icon: const Icon(Icons.add_task), label: const Text('내 습관으로 (다음 업데이트 예정)'), ), ), ), ); } }