diff --git a/lib/pages/operation/reception/reception_search_page.dart b/lib/pages/operation/reception/reception_search_page.dart index 5bd3024..ed64bb4 100644 --- a/lib/pages/operation/reception/reception_search_page.dart +++ b/lib/pages/operation/reception/reception_search_page.dart @@ -1,7 +1,9 @@ import 'dart:async'; import 'package:e_scan/backend/objectbox/entities/stock_picking/stock_picking_record_entity.dart'; import 'package:e_scan/components/components.dart'; +import 'package:e_scan/pages/operation/reception/reception_page_model.dart'; import 'package:e_scan/pages/operation/reception/reception_search_page_model.dart'; +import 'package:e_scan/router/go_secure_router_builder.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:e_scan/themes/app_theme.dart'; import 'package:flutter/material.dart'; @@ -16,14 +18,17 @@ class ReceptionSearchPage extends ConsumerStatefulWidget { class _ReceptionSearchPageState extends ConsumerState { Timer? _debounce; + String _query = ''; void _onSearchChanged(String value) { _debounce?.cancel(); + _query = value; _debounce = Timer(const Duration(milliseconds: 500), () { ref .read(receptionSearchPageModelProvider.notifier) .allByName(name: value); }); + setState(() {}); // pour rafraîchir le highlight immédiatement } @override @@ -32,6 +37,47 @@ class _ReceptionSearchPageState extends ConsumerState { super.dispose(); } + /// Crée un TextSpan avec le texte où les parties correspondant à la requête sont surlignées. + TextSpan _buildHighlightedText( + String? text, + String query, + TextStyle defaultStyle, + ) { + if (text == null || query.isEmpty) { + return TextSpan(text: text ?? '', style: defaultStyle); + } + + final lcText = text.toLowerCase(); + final lcQuery = query.toLowerCase(); + final spans = []; + int start = 0; + + while (true) { + final index = lcText.indexOf(lcQuery, start); + if (index < 0) { + spans.add(TextSpan(text: text.substring(start), style: defaultStyle)); + break; + } + if (index > start) { + spans.add( + TextSpan(text: text.substring(start, index), style: defaultStyle), + ); + } + spans.add( + TextSpan( + text: text.substring(index, index + query.length), + style: defaultStyle.copyWith( + fontWeight: FontWeight.bold, + color: AppTheme.of(context).primaryText, + ), + ), + ); + start = index + query.length; + } + + return TextSpan(children: spans); + } + @override Widget build(BuildContext context) { final state = ref.watch(receptionSearchPageModelProvider); @@ -68,12 +114,26 @@ class _ReceptionSearchPageState extends ConsumerState { ? const Center(child: Text('Aucun résultat trouvé')) : SingleChildScrollView( child: Column( - children: receptions - .map( - (reception) => - ListTile(title: Text(reception.name ?? '')), - ) - .toList(), + children: receptions.map((reception) { + final name = reception.name ?? ''; + return ListTile( + onTap: () { + final id = reception.id; + ReceptionDetailsRoute( + receptionId: id, + ).pushReplacement(context); + }, + title: RichText( + text: _buildHighlightedText( + name, + _query, + AppTheme.of(context).bodyLarge.override( + color: AppTheme.of(context).secondaryText, + ), + ), + ), + ); + }).toList(), ), ), ),