From 31d613fc0949df7a3d80db75465d05e840c41bb0 Mon Sep 17 00:00:00 2001 From: ssbg04 Date: Tue, 21 Jul 2026 23:13:05 +0800 Subject: [PATCH] fix: dashboard search UI, student screen UI, reset requests modal timezone --- .../screens/dashboard/dashboard_screen.dart | 19 +++++++-------- .../ui/screens/students/students_screen.dart | 2 +- .../lib/ui/shared/inputs/app_search_bar.dart | 23 ++++++------------- .../shared/modals/reset_requests_modal.dart | 7 +++--- 4 files changed, 20 insertions(+), 31 deletions(-) diff --git a/frontend/lib/ui/screens/dashboard/dashboard_screen.dart b/frontend/lib/ui/screens/dashboard/dashboard_screen.dart index 6751108..622994b 100644 --- a/frontend/lib/ui/screens/dashboard/dashboard_screen.dart +++ b/frontend/lib/ui/screens/dashboard/dashboard_screen.dart @@ -555,27 +555,24 @@ class _DashboardScreenState extends ConsumerState { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - if (!_searchFocusNode.hasFocus) ...[ + if (!_searchFocusNode.hasFocus && _searchController.text.isEmpty) ...[ const Flexible( - child: FittedBox( - fit: BoxFit.scaleDown, - alignment: Alignment.centerLeft, - child: Text( - 'Dashboard Overview', - style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), - ), + child: Text( + 'Dashboard Overview', + style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + overflow: TextOverflow.ellipsis, ), ), const SizedBox(width: 16), ], Expanded( + key: const ValueKey('dashboard_search_expanded'), child: LayoutBuilder( builder: (context, constraints) { - final isSearching = _searchFocusNode.hasFocus; + final isSearching = _searchFocusNode.hasFocus || _searchController.text.isNotEmpty; return Align( alignment: Alignment.centerRight, child: AppSearchBar( - key: const ValueKey('dashboard_search'), controller: _searchController, focusNode: _searchFocusNode, collapsible: true, @@ -593,7 +590,7 @@ class _DashboardScreenState extends ConsumerState { }, ), ), - if (!_searchFocusNode.hasFocus) ...[ + if (!_searchFocusNode.hasFocus && _searchController.text.isEmpty) ...[ const SizedBox(width: 16), Row( children: [ diff --git a/frontend/lib/ui/screens/students/students_screen.dart b/frontend/lib/ui/screens/students/students_screen.dart index e6b31c6..91b64c6 100644 --- a/frontend/lib/ui/screens/students/students_screen.dart +++ b/frontend/lib/ui/screens/students/students_screen.dart @@ -590,7 +590,7 @@ class _StudentsScreenState extends ConsumerState { Widget _buildHeaderControls(BuildContext context, StudentQueryParams query, WidgetRef ref, int activeCount) { return LayoutBuilder(builder: (_, c) { final isDesktop = c.maxWidth > 800; - final bool isSearchActive = _searchFocusNode.hasFocus; + final bool isSearchActive = _searchFocusNode.hasFocus || _searchController.text.isNotEmpty; return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, diff --git a/frontend/lib/ui/shared/inputs/app_search_bar.dart b/frontend/lib/ui/shared/inputs/app_search_bar.dart index 6929931..8c35526 100644 --- a/frontend/lib/ui/shared/inputs/app_search_bar.dart +++ b/frontend/lib/ui/shared/inputs/app_search_bar.dart @@ -58,7 +58,7 @@ class _AppSearchBarState extends ConsumerState { super.initState(); _controller = widget.controller ?? TextEditingController(); _focusNode = widget.focusNode ?? FocusNode(); - _isExpanded = !widget.collapsible; + _isExpanded = !widget.collapsible || _controller.text.trim().isNotEmpty; _controller.addListener(_onTextChanged); _focusNode.addListener(_onFocusChanged); } @@ -76,7 +76,7 @@ class _AppSearchBarState extends ConsumerState { void _onFocusChanged() { if (mounted) { setState(() { - if (!_focusNode.hasFocus && widget.collapsible) { + if (!_focusNode.hasFocus && widget.collapsible && _controller.text.trim().isEmpty) { _isExpanded = false; } }); @@ -151,13 +151,15 @@ class _AppSearchBarState extends ConsumerState { itemBuilder: (context, index) { final term = history[index]; return InkWell( - onTapDown: (_) { + onTap: () { _controller.text = term; - _focusNode.unfocus(); if (widget.onSubmitted != null) { widget.onSubmitted!(term); } ref.read(searchHistoryProvider.notifier).addSearch(term); + Future.delayed(const Duration(milliseconds: 50), () { + if (mounted) _focusNode.unfocus(); + }); }, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), @@ -214,18 +216,7 @@ class _AppSearchBarState extends ConsumerState { @override Widget build(BuildContext context) { - if (!_isExpanded && widget.collapsible) { - if (_hasText) { - return IconButton( - onPressed: () { - _controller.clear(); - widget.onChanged?.call(''); - if (widget.onSubmitted != null) widget.onSubmitted!(''); - }, - icon: const Icon(Icons.close, size: 28, color: Colors.redAccent), - tooltip: 'Clear search', - ); - } + if (!_isExpanded && widget.collapsible && !_hasText) { return IconButton( onPressed: () { setState(() => _isExpanded = true); diff --git a/frontend/lib/ui/shared/modals/reset_requests_modal.dart b/frontend/lib/ui/shared/modals/reset_requests_modal.dart index 99b59d8..0332977 100644 --- a/frontend/lib/ui/shared/modals/reset_requests_modal.dart +++ b/frontend/lib/ui/shared/modals/reset_requests_modal.dart @@ -33,9 +33,10 @@ class _ResetRequestsModalState extends ConsumerState { String _formatDate(String isoString) { try { - final utcTime = DateTime.parse(isoString).toUtc(); - final phTime = utcTime.add(const Duration(hours: 8)); - return DateFormat('MMM d, yyyy, hh:mm a').format(phTime); + DateTime dt = DateTime.parse(isoString); + if (!dt.isUtc) dt = dt.toUtc(); + dt = dt.add(const Duration(hours: 8)); + return DateFormat('MMM d, yyyy, hh:mm a').format(dt); } catch (_) { return isoString; }