import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; import '../../data/api/report_data_source.dart'; import '../../data/models/models.dart'; import '../../data/providers.dart'; import '../../data/state/app_interaction_state.dart'; import '../../routing/app_routes.dart'; import '../../theme/app_icons.dart'; import '../../theme/yanting_text.dart'; import '../../theme/yanting_tokens.dart'; import '../../widgets/app_buttons.dart'; import '../../widgets/app_card.dart'; import '../../widgets/badges.dart'; import '../../widgets/sheets.dart'; import '../../widgets/states.dart'; import '../../widgets/institution_card.dart'; import '../shared/report_card_widget.dart'; class InstitutionDetailPage extends HookConsumerWidget { const InstitutionDetailPage({ required this.institutionId, required this.dataSource, super.key, }); final String institutionId; final ReportDataSource dataSource; @override Widget build(BuildContext context, WidgetRef ref) { final retryCount = useState(0); final future = useMemoized( () => dataSource.institutionDetail(institutionId), [dataSource, institutionId, retryCount.value], ); final snapshot = useFuture(future); final theme = ShadTheme.of(context); return Scaffold( backgroundColor: theme.colorScheme.background, appBar: AppBar( backgroundColor: theme.colorScheme.background, surfaceTintColor: Colors.transparent, elevation: 0, title: const Text('机构主页'), bottom: PreferredSize( preferredSize: const Size.fromHeight(1), child: ColoredBox( color: theme.colorScheme.border, child: const SizedBox(height: 1, width: double.infinity), ), ), ), body: snapshot.connectionState != ConnectionState.done ? const LoadingState() : snapshot.hasError ? ErrorState( message: snapshot.error.toString(), onRetry: () => retryCount.value++, ) : _InstitutionDetailContent( item: snapshot.data!, dataSource: dataSource, ), ); } } class _InstitutionDetailContent extends ConsumerWidget { const _InstitutionDetailContent({ required this.item, required this.dataSource, }); final Institution item; final ReportDataSource dataSource; @override Widget build(BuildContext context, WidgetRef ref) { return ListView( padding: const EdgeInsets.fromLTRB( YantingSpacing.x4, 4, YantingSpacing.x4, 16, ), children: [ AppCard( color: YantingColors.brandSoft, borderColor: YantingColors.brandSoftBorder, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ InstitutionLogo( logoUrl: item.logoUrl, initials: item.nameCn.isEmpty ? '研' : item.nameCn.characters.take(2).toString(), size: 48, ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.nameCn, style: YantingText.sectionTitle.copyWith( fontSize: 21, ), ), if (item.nameEn.isNotEmpty) Text(item.nameEn, style: YantingText.meta), ], ), ), ], ), const SizedBox(height: 14), Wrap( spacing: 8, runSpacing: 8, children: [ AppBadge(text: item.sourceTier, kind: BadgeKind.tier), AppBadge( text: '${item.reportCount} 份研报', kind: BadgeKind.brand, ), for (final topic in item.coveredTopics) AppBadge(text: topic), ], ), ], ), ), const SizedBox(height: YantingSpacing.x3), if (item.introCn.isNotEmpty) AppCard(child: Text(item.introCn, style: YantingText.body)), const SizedBox(height: YantingSpacing.x3), if (item.credibilityNote.isNotEmpty) AppCard( child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Icon(AppIcons.shield, color: YantingColors.chart2), const SizedBox(width: YantingSpacing.x2), Expanded( child: Text(item.credibilityNote, style: YantingText.body), ), ], ), ), const SizedBox(height: YantingSpacing.x6), Text('最新研报', style: YantingText.sectionTitle.copyWith(fontSize: 21)), const SizedBox(height: YantingSpacing.x3), if (item.recentReports.isEmpty) const EmptyState( title: '机构暂无研报', message: '稍后再试', icon: Icons.article_outlined, ) else for (final report in item.recentReports) ...[ ReportCardWidget( report: report, onTap: () { ref .read(profileControllerProvider.notifier) .addHistory(report.id); openReportDetail(context, dataSource, report); }, ), const SizedBox(height: YantingSpacing.x3), ], AppButton( label: '了解相关服务', icon: AppIcons.externalLink, kind: AppButtonKind.ghost, expand: true, onPressed: () => showOutboundSheet( context, title: item.nameCn, onConfirm: () => ref .read(outboundRepositoryProvider) .recordOutbound( OutboundEvent( scene: 'institution_service', refId: item.id, targetUrl: item.websiteUrl.isEmpty ? null : item.websiteUrl, ), ), ), ), ], ); } }