import 'package:flutter/material.dart'; class UsageHistory { final String date; final String startTime; final String endTime; final int duration; final String startStation; final String endStation; final String status; final bool isSanitized; final bool isDried; UsageHistory({ required this.date, required this.startTime, required this.endTime, required this.duration, required this.startStation, required this.endStation, required this.status, required this.isSanitized, required this.isDried, }); } class HistoryScreen extends StatefulWidget { const HistoryScreen({super.key}); @override State createState() => _HistoryScreenState(); } class _HistoryScreenState extends State { // 🟦 μ΅œμ’… ν™•μ • ν…Œλ§ˆ 컬러 μ •μ˜ (λŒ€λΉ„ ꡬ쑰 ꡐ체) final Color _mainBlueColor = const Color(0xFF002FA7); // μ§„ν•œ 블루 (0xFF002FA7 -> 0xFF0A68FF둜 톡일) final Color _mainTextColor = const Color(0xFF1C1C1E); // μ–΄λ‘μš΄ ν…μŠ€νŠΈ final Color _subTextColor = const Color(0xFF6A717B); // 보쑰 ν…μŠ€νŠΈ final Color _pageBackgroundColor = const Color(0xFFF5F7F9); // 🚩 Scaffold λ°°κ²½ (μ—°ν•œ 그레이) final Color _cardBackgroundColor = Colors.white; // 🚩 μΉ΄λ“œ λ°°κ²½ (순수 ν™”μ΄νŠΈ) final Color _accentContainerColor = const Color(0xFFF0F2F5); // 🚩 λ‚΄λΆ€ 상세 λ°•μŠ€ λ°°κ²½ (쀑간 그레이) final Color _borderColor = const Color(0xFF0A68FF).withOpacity(0.8); // 🚩 그림자 μŠ€νƒ€μΌ μ •μ˜ static const BoxShadow _cleanShadow = BoxShadow( color: Color.fromRGBO(0, 0, 0, 0.07), blurRadius: 8, offset: Offset(0, 4), spreadRadius: 0, ); int _selectedFilterIndex = 0; final List _allHistoryList = [ UsageHistory( date: '2025.11.25 (ν™”)', // πŸ‡°πŸ‡· μš”μΌ λ³€κ²½ startTime: '18:30', endTime: '19:15', duration: 45, startStation: 'STATION A - GU.UNIV', endStation: 'STATION B - CITY HALL', status: 'RETURNED', isSanitized: true, isDried: true, ), UsageHistory( date: '2025.11.23 (일)', // πŸ‡°πŸ‡· μš”μΌ λ³€κ²½ startTime: '14:00', endTime: '15:30', duration: 90, startStation: 'STATION B - CITY HALL', endStation: 'STATION B - CITY HALL', status: 'RETURNED', isSanitized: true, isDried: false, ), UsageHistory( date: '2025.11.06 (λͺ©)', // πŸ‡°πŸ‡· μš”μΌ λ³€κ²½ startTime: '09:00', endTime: '09:30', duration: 30, startStation: 'STATION A - GU.UNIV', endStation: 'STATION C - PARK', status: 'RETURNED', isSanitized: true, isDried: true, ), UsageHistory( date: '2025.09.20 (ν† )', // πŸ‡°πŸ‡· μš”μΌ λ³€κ²½ startTime: '20:00', endTime: '21:00', duration: 60, startStation: 'STATION C - PARK', endStation: 'STATION B - CITY HALL', status: 'RETURNED', isSanitized: true, isDried: true, ), ]; List get _filteredList { if (_selectedFilterIndex == 0) { return _allHistoryList; } final now = DateTime(2025, 11, 26); return _allHistoryList.where((history) { String dateStr = history.date.split(' ')[0]; List parts = dateStr.split('.'); DateTime historyDate = DateTime( int.parse(parts[0]), int.parse(parts[1]), int.parse(parts[2]) ); int difference = now .difference(historyDate) .inDays; if (_selectedFilterIndex == 1) { return difference <= 7; } else if (_selectedFilterIndex == 2) { return difference <= 30; } return true; }).toList(); } @override Widget build(BuildContext context) { final currentList = _filteredList; return Scaffold( backgroundColor: _pageBackgroundColor, appBar: AppBar( title: Text('λ‚˜μ˜ 기둝', style: TextStyle(fontWeight: FontWeight .bold, fontSize: 18, letterSpacing: 0.5, color: _mainTextColor)), backgroundColor: _pageBackgroundColor, scrolledUnderElevation: 0, elevation: 0, centerTitle: false, actions: [ IconButton(icon: Icon(Icons.filter_list, color: _mainTextColor), onPressed: () {}), ], ), body: Column( children: [ _buildFilterTabs(), Expanded( child: currentList.isEmpty ? Center( child: Text("기둝이 μ—†μŠ΅λ‹ˆλ‹€.", style: TextStyle(color: _subTextColor))) : ListView.builder( padding: const EdgeInsets.all(16), itemCount: currentList.length, itemBuilder: (context, index) { final history = currentList[index]; bool showHeader = true; if (index > 0 && currentList[index - 1].date == history.date) { showHeader = false; } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (showHeader) _buildDateHeader(history.date), _buildHistoryCard(history), ], ); }, ), ), ], ), ); } Widget _buildFilterTabs() { final filters = ["전체", "μ§€λ‚œ 7일", "μ§€λ‚œ 30일"]; return Container( height: 50, padding: const EdgeInsets.symmetric(horizontal: 16), child: ListView.separated( scrollDirection: Axis.horizontal, itemCount: filters.length, separatorBuilder: (c, i) => const SizedBox(width: 10), itemBuilder: (context, index) { final isSelected = _selectedFilterIndex == index; return ChoiceChip( label: Text(filters[index]), selected: isSelected, onSelected: (selected) { setState(() => _selectedFilterIndex = index); }, backgroundColor: _cardBackgroundColor, selectedColor: _mainBlueColor, checkmarkColor: Colors.white, labelStyle: TextStyle( color: isSelected ? Colors.white : _mainTextColor, fontWeight: FontWeight.bold, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: BorderSide.none), ); }, ), ); } Widget _buildDateHeader(String date) { return Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: Text( date, style: TextStyle( color: _mainTextColor, fontSize: 14, fontWeight: FontWeight.bold ), ), ); } Widget _buildHistoryCard(UsageHistory history) { return Container( margin: const EdgeInsets.only(bottom: 12), decoration: BoxDecoration( color: _cardBackgroundColor, borderRadius: BorderRadius.circular(8), boxShadow: [_cleanShadow] ), child: InkWell( onTap: () => _showHistoryDetail(context, history), borderRadius: BorderRadius.circular(8), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '${history.startTime} - ${history.endTime}', style: TextStyle(color: _mainTextColor, fontSize: 16, fontWeight: FontWeight.bold), ), Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4), decoration: BoxDecoration( color: _mainBlueColor, borderRadius: BorderRadius.circular(8), ), child: Text( '${history.duration}λΆ„', // πŸ‡°πŸ‡· min -> λΆ„ style: const TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.w600), ), ), ], ), const SizedBox(height: 12), Row( children: [ _buildDot(_subTextColor), const SizedBox(width: 8), Expanded(child: Text(history.startStation, style: TextStyle(color: _mainTextColor, fontSize: 13), overflow: TextOverflow.ellipsis)), ], ), Container( margin: const EdgeInsets.only(left: 3), height: 10, decoration: BoxDecoration( border: Border( left: BorderSide(color: _subTextColor.withOpacity(0.5), width: 1)), ), ), Row( children: [ _buildDot(_mainBlueColor), const SizedBox(width: 8), Expanded(child: Text(history.endStation, style: TextStyle(color: _mainTextColor, fontSize: 13), overflow: TextOverflow.ellipsis)), ], ), const SizedBox(height: 12), Divider(color: _subTextColor.withOpacity(0.5), thickness: 0.5,), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ if (history.isSanitized) _buildDot(_mainBlueColor), if (history.isSanitized) const SizedBox(width: 8), Text( history.isSanitized ? "μ‚΄κ·  μ™„λ£Œ" : "μ§„ν–‰ 쀑", // πŸ‡°πŸ‡· Pending -> μ§„ν–‰ 쀑 style: TextStyle( color: history.isSanitized ? _mainTextColor : _subTextColor, fontSize: 14, fontWeight: FontWeight.bold), ), ], ), Text( "상세 보기 >", // πŸ‡°πŸ‡· VIEW DETAILS > -> 상세 보기 > style: TextStyle(color: _subTextColor, fontSize: 11), ), ], ), ], ), ), ), ); } Widget _buildDot(Color color) { return Container(width: 8, height: 8, decoration: BoxDecoration(color: color, shape: BoxShape.circle)); } void _showHistoryDetail(BuildContext context, UsageHistory history) { int cost = history.duration * 100; showModalBottomSheet( context: context, backgroundColor: Colors.transparent, isScrollControlled: true, builder: (context) { return Container( height: MediaQuery .of(context) .size .height * 0.75, decoration: BoxDecoration( color: _cardBackgroundColor, borderRadius: const BorderRadius.vertical(top: Radius.circular(12)), boxShadow: [_cleanShadow] ), child: Column( children: [ const SizedBox(height: 12), Container(width: 40, height: 4, decoration: BoxDecoration(color: _subTextColor, borderRadius: BorderRadius.circular(2))), Expanded( child: SingleChildScrollView( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("이용 상세 정보", style: TextStyle( // πŸ‡°πŸ‡· HISTORY DETAILS color: _mainTextColor, fontSize: 20, fontWeight: FontWeight.bold)), const SizedBox(height: 8), Text( history.date, style: TextStyle(color: _subTextColor), ), const SizedBox(height: 30), Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration(color: _accentContainerColor, borderRadius: BorderRadius.circular(8)), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("이용 μ‹œκ°„", style: TextStyle( // πŸ‡°πŸ‡· 이용 μ‹œκ°„ color: _subTextColor, fontSize: 12)), const SizedBox(height: 4), Text("${history.duration}λΆ„", // πŸ‡°πŸ‡· min -> λΆ„ style: TextStyle(color: _mainTextColor, fontSize: 24, fontWeight: FontWeight.bold)), ], ), Container( width: 1, height: 40, color: _subTextColor.withOpacity(0.5)), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("이용 κΈˆμ•‘", style: TextStyle( // πŸ‡°πŸ‡· 이용 κΈˆμ•‘ color: _subTextColor, fontSize: 12)), const SizedBox(height: 4), Text("β‚© $cost", style: TextStyle( // πŸ‡°πŸ‡· β‚© μœ„μΉ˜ μˆ˜μ • color: _mainTextColor, fontSize: 24, fontWeight: FontWeight.bold)), ], ), ], ), ), const SizedBox(height: 30), Text("μ‹œκ°„λ³„ 기둝", style: TextStyle( // πŸ‡°πŸ‡· TIMELINE color: _mainTextColor, fontSize: 16, fontWeight: FontWeight.bold, letterSpacing: 1.0)), const SizedBox(height: 20), _buildTimelineItem( history.startTime, "λŒ€μ—¬ μ‹œμž‘", history.startStation, isFirst: true, isCompleted: true), _buildTimelineItem( history.endTime, "λ°˜λ‚© μ™„λ£Œ", history.endStation, isCompleted: true), _buildTimelineItem( history.endTime, "μ‚΄κ·  μ‹œμž‘", "μžλ™ μ‚΄κ·  μ‹œμŠ€ν…œ", // πŸ‡°πŸ‡· Auto-Cleaning System isCompleted: true), _buildTimelineItem( "15λΆ„ ν›„", "μ‚΄κ·  μ™„λ£Œ", "λ‹€μŒ μ‚¬μš©μž μ€€λΉ„ μ™„λ£Œ", // πŸ‡°πŸ‡· 15 min later, Ready for next user isLast: true, isCompleted: history.isSanitized), const SizedBox(height: 30), Text("μ‚΄κ· /건쑰 μƒνƒœ", style: TextStyle( // πŸ‡°πŸ‡· CONDITION color: _mainTextColor, fontSize: 16, fontWeight: FontWeight.bold)), const SizedBox(height: 8), Row( children: [ Expanded(child: _buildConditionCard( "μ‚΄κ· ", history.isSanitized, Icons.sentiment_satisfied_alt)), const SizedBox(width: 12), Expanded(child: _buildConditionCard( "건쑰", history.isDried, Icons.sentiment_very_satisfied)), ], ), ], ), ), ), ], ), ); }, ); } Widget _buildTimelineItem(String time, String title, String subtitle, {bool isFirst = false, bool isLast = false, bool isCompleted = false}) { // πŸ‡°πŸ‡· 15 min later -> 15λΆ„ ν›„ 둜직 처리 String displayTime = time.contains("min later") ? "15λΆ„ ν›„" : time; return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 60, child: Text( displayTime, style: TextStyle(color: _mainTextColor, fontSize: 12)), ), Column( children: [ Icon( isCompleted ? Icons.check_circle : Icons.radio_button_unchecked, color: isCompleted ? _mainBlueColor : _subTextColor, size: 20), if (!isLast) Container(width: 2, height: 40, color: isCompleted ? _mainBlueColor.withOpacity(0.5) : Colors.grey.shade300), ], ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: TextStyle( color: isCompleted ? _mainTextColor : _subTextColor, fontWeight: FontWeight.bold, fontSize: 14)), const SizedBox(height: 4), Text(subtitle, style: TextStyle(color: _subTextColor, fontSize: 12)), const SizedBox(height: 24), ], ), ), ], ); } Widget _buildConditionCard(String title, bool isDone, IconData icon) { return Container( height: 120, width: double.infinity, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: _accentContainerColor, // λ‚΄λΆ€ 상세 λ°•μŠ€ λ°°κ²½ (쀑간 그레이) boxShadow: isDone ? [_cleanShadow] : null, border: Border.all( color: isDone ? _mainBlueColor.withOpacity(0.9) : _subTextColor.withOpacity(0.3), width: 1.0), borderRadius: BorderRadius.circular(8), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(icon, color: _mainBlueColor, size: 28), const SizedBox(height: 8), Text( title, style: TextStyle( color: _mainTextColor, fontSize: 14, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Text( isDone ? "μ™„λ£Œλ¨" : "μ§„ν–‰ 쀑", // πŸ‡°πŸ‡· Completed/In Progress style: TextStyle( color: _subTextColor, fontWeight: FontWeight.bold, fontSize: 14 ), ), ], ), ); } }