import 'display_category.dart'; /// 갤러리 UI 가 소비하는 통합 카탈로그 항목. /// /// 3 source (ProtocolsTable / BreakProtocolsTable / DietPatternsTable) 를 /// 단일 sealed 계층으로 통합. 카드/필터링은 공통 필드만 보면 충분. sealed class CatalogItem { String get id; String get title; String? get titleEn; /// 카드용 1줄 요약 (≤ 60자). String get summary; DisplayCategory get displayCategory; /// 'strong_rct' / 'meta_analysis' / 'observational' / 'mechanistic' / 'expert_opinion' / null. /// DietPattern 은 'strong'/'moderate'/'mixed'/'weak'. String? get evidenceStrength; List get referenceIds; } /// Protocols 테이블 1:1 매핑. final class ProtocolCatalogItem implements CatalogItem { ProtocolCatalogItem({ required this.id, required this.title, required this.titleEn, required this.summary, required this.displayCategory, required this.evidenceStrength, required this.referenceIds, required this.what, required this.whenText, required this.dose, required this.why, required this.how, required this.checkText, required this.caution, required this.defaultAnchor, required this.minDoseForStart, required this.sourceDoc, }); @override final String id; @override final String title; @override final String? titleEn; @override final String summary; @override final DisplayCategory displayCategory; @override final String? evidenceStrength; @override final List referenceIds; final String what; final String whenText; final String dose; final String why; final List how; final String checkText; final String? caution; final Map? defaultAnchor; final String? minDoseForStart; final String? sourceDoc; } /// BreakProtocols 테이블 1:1 매핑. displayCategory 는 항상 breakHabit. final class BreakCatalogItem implements CatalogItem { BreakCatalogItem({ required this.id, required this.title, required this.titleEn, required this.summary, required this.evidenceStrength, required this.referenceIds, required this.breakCategory, required this.hubermanSummary, required this.phases, required this.defaultCommonFrames, required this.tools, required this.medicalWarning, }); @override final String id; @override final String title; @override final String? titleEn; @override final String summary; @override DisplayCategory get displayCategory => DisplayCategory.breakHabit; @override final String? evidenceStrength; @override final List referenceIds; /// 원본 break 카테고리 (alcohol / nicotine / ...). 카드 sub-tag 로 활용. final String breakCategory; final String hubermanSummary; final List phases; final List defaultCommonFrames; final List tools; final String? medicalWarning; } /// DietPatterns 테이블 1:1 매핑. displayCategory 는 항상 nutrition. final class DietCatalogItem implements CatalogItem { DietCatalogItem({ required this.id, required this.title, required this.titleEn, required this.summary, required this.evidenceStrength, required this.referenceIds, required this.name, required this.core, required this.strengths, required this.weaknesses, required this.koreanContextFit, required this.starterLevers, required this.medicalWarning, required this.linkedProtocolIds, }); @override final String id; @override final String title; @override final String? titleEn; @override final String summary; @override DisplayCategory get displayCategory => DisplayCategory.nutrition; @override final String? evidenceStrength; @override final List referenceIds; final String name; final String core; final List strengths; final List weaknesses; final String? koreanContextFit; final List starterLevers; final String? medicalWarning; final List linkedProtocolIds; } /// 그룹핑 헬퍼. 빈 카테고리 키는 결과 map 에 미포함. Map> groupByCategory( List items) { final result = >{}; for (final item in items) { result.putIfAbsent(item.displayCategory, () => []).add(item); } return result; }