import 'package:flutter/material.dart'; class OverviewPage extends StatelessWidget { const OverviewPage({super.key}); static const navigationItems = [ (Icons.space_dashboard_outlined, '개요'), (Icons.error_outline, '오류'), (Icons.account_tree_outlined, '프로젝트'), (Icons.build_outlined, '수정 작업'), (Icons.fact_check_outlined, '검토'), (Icons.settings_outlined, '설정'), ]; @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, constraints) { final showRail = constraints.maxWidth >= 920; final compactOrganization = constraints.maxWidth < 720; final compactBrand = constraints.maxWidth < 420; return Scaffold( drawer: showRail ? null : const _NavigationDrawer(), appBar: AppBar( toolbarHeight: 68, titleSpacing: showRail ? 24 : 0, title: _Brand(compact: compactBrand), actions: [ _OrganizationBadge(compact: compactOrganization), const SizedBox(width: 16), ], ), body: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if (showRail) const _NavigationRail(), const Expanded(child: _OverviewContent()), ], ), ); }, ); } } class _Brand extends StatelessWidget { const _Brand({required this.compact}); final bool compact; @override Widget build(BuildContext context) { return Row( mainAxisSize: MainAxisSize.min, children: [ const _BrandMark(), if (!compact) ...[ const SizedBox(width: 10), const Text( 'ARIADNE', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w800, letterSpacing: 2.1, ), ), ], ], ); } } class _BrandMark extends StatelessWidget { const _BrandMark(); @override Widget build(BuildContext context) { return Container( width: 32, height: 32, decoration: BoxDecoration( color: Theme.of(context).colorScheme.primary, borderRadius: BorderRadius.circular(10), ), child: const Icon(Icons.route, color: Colors.white, size: 19), ); } } class _OrganizationBadge extends StatelessWidget { const _OrganizationBadge({required this.compact}); final bool compact; @override Widget build(BuildContext context) { return Semantics( button: true, enabled: false, label: '조직 선택 기능 준비 중', child: Tooltip( message: '조직 선택 기능 준비 중', child: Container( padding: EdgeInsets.symmetric( horizontal: compact ? 10 : 12, vertical: 8, ), decoration: BoxDecoration( border: Border.all(color: const Color(0xFFDCE1E6)), borderRadius: BorderRadius.circular(10), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.apartment, size: 17), if (!compact) ...[ const SizedBox(width: 7), const Text( '조직 선택', style: TextStyle(fontSize: 13, fontWeight: FontWeight.w600), ), const SizedBox(width: 5), const Icon(Icons.expand_more, size: 18), ], ], ), ), ), ); } } class _NavigationRail extends StatelessWidget { const _NavigationRail(); @override Widget build(BuildContext context) { return Container( width: 224, color: Colors.white, padding: const EdgeInsets.fromLTRB(12, 20, 12, 12), child: Column( children: [ for ( var index = 0; index < OverviewPage.navigationItems.length; index++ ) _NavigationTile( icon: OverviewPage.navigationItems[index].$1, label: OverviewPage.navigationItems[index].$2, selected: index == 0, ), const Spacer(), const _ConnectionStatus(), ], ), ); } } class _NavigationDrawer extends StatelessWidget { const _NavigationDrawer(); @override Widget build(BuildContext context) { return Drawer( child: SafeArea( child: Padding( padding: const EdgeInsets.all(12), child: Column( children: [ for ( var index = 0; index < OverviewPage.navigationItems.length; index++ ) _NavigationTile( icon: OverviewPage.navigationItems[index].$1, label: OverviewPage.navigationItems[index].$2, selected: index == 0, ), const Spacer(), const _ConnectionStatus(), ], ), ), ), ); } } class _NavigationTile extends StatelessWidget { const _NavigationTile({ required this.icon, required this.label, required this.selected, }); final IconData icon; final String label; final bool selected; @override Widget build(BuildContext context) { final color = selected ? Theme.of(context).colorScheme.primary : const Color(0xFF5C6870); return Semantics( button: true, enabled: false, selected: selected, child: Container( margin: const EdgeInsets.only(bottom: 4), decoration: BoxDecoration( color: selected ? Theme.of(context).colorScheme.primary.withValues(alpha: 0.09) : Colors.transparent, borderRadius: BorderRadius.circular(10), ), child: ListTile( dense: true, leading: Icon(icon, color: color, size: 21), title: Text( label, style: TextStyle( color: color, fontSize: 14, fontWeight: selected ? FontWeight.w700 : FontWeight.w500, ), ), ), ), ); } } class _ConnectionStatus extends StatelessWidget { const _ConnectionStatus(); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: const Color(0xFFF5F7F8), borderRadius: BorderRadius.circular(12), ), child: const Row( children: [ Icon(Icons.circle, color: Color(0xFF87939B), size: 9), SizedBox(width: 8), Expanded( child: Text( 'IOP 연결 대기', style: TextStyle( color: Color(0xFF5C6870), fontSize: 12, fontWeight: FontWeight.w600, ), ), ), ], ), ); } } class _OverviewContent extends StatelessWidget { const _OverviewContent(); @override Widget build(BuildContext context) { return CustomScrollView( slivers: [ SliverPadding( padding: const EdgeInsets.fromLTRB(28, 28, 28, 40), sliver: SliverToBoxAdapter( child: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 1180), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( '오류 흐름 개요', style: Theme.of(context).textTheme.headlineMedium, ), const SizedBox(height: 8), const Text( '조직의 오류 수집부터 분석, 수정과 병합 검토까지 한곳에서 관리합니다.', style: TextStyle(color: Color(0xFF66727A), fontSize: 14), ), const SizedBox(height: 24), const _MetricGrid(), const SizedBox(height: 22), const _EmptyState(), ], ), ), ), ), ), ], ); } } class _MetricGrid extends StatelessWidget { const _MetricGrid(); static const metrics = [ (Icons.bug_report_outlined, '열린 오류', '0', Color(0xFFC24B3F)), (Icons.manage_search_outlined, '분석 중', '0', Color(0xFF9A6A16)), (Icons.auto_fix_high_outlined, '수정 가능', '0', Color(0xFF2458A6)), (Icons.rate_review_outlined, '병합 검토', '0', Color(0xFF4A6A54)), ]; @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, constraints) { final columns = constraints.maxWidth >= 980 ? 4 : constraints.maxWidth >= 560 ? 2 : 1; final width = (constraints.maxWidth - ((columns - 1) * 14)) / columns; return Wrap( spacing: 14, runSpacing: 14, children: [ for (final metric in metrics) SizedBox( width: width, child: _MetricCard( icon: metric.$1, label: metric.$2, value: metric.$3, color: metric.$4, ), ), ], ); }, ); } } class _MetricCard extends StatelessWidget { const _MetricCard({ required this.icon, required this.label, required this.value, required this.color, }); final IconData icon; final String label; final String value; final Color color; @override Widget build(BuildContext context) { return Card( margin: EdgeInsets.zero, child: Padding( padding: const EdgeInsets.all(18), child: Row( children: [ Container( width: 42, height: 42, decoration: BoxDecoration( color: color.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(12), ), child: Icon(icon, color: color, size: 22), ), const SizedBox(width: 14), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( value, style: const TextStyle( fontSize: 24, fontWeight: FontWeight.w800, height: 1, ), ), const SizedBox(height: 7), Text( label, style: const TextStyle( color: Color(0xFF66727A), fontSize: 13, ), ), ], ), ], ), ), ); } } class _EmptyState extends StatelessWidget { const _EmptyState(); @override Widget build(BuildContext context) { return Card( margin: EdgeInsets.zero, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 52), child: Column( children: [ Container( width: 58, height: 58, decoration: BoxDecoration( color: Theme.of( context, ).colorScheme.primary.withValues(alpha: 0.08), shape: BoxShape.circle, ), child: Icon( Icons.route_outlined, color: Theme.of(context).colorScheme.primary, size: 28, ), ), const SizedBox(height: 18), Text( '첫 프로젝트를 연결하세요', style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: 8), const Text( '저장소와 오류 입력을 연결하면 Ariadne가 문제의 경로를 추적합니다.', textAlign: TextAlign.center, style: TextStyle(color: Color(0xFF66727A), fontSize: 14), ), const SizedBox(height: 20), FilledButton.icon( onPressed: null, icon: const Icon(Icons.add, size: 18), label: const Text('프로젝트 연결 준비 중'), ), ], ), ), ); } }