feat: HOME-007 — server-side event title/description search (q param)

This commit is contained in:
2026-04-04 17:33:56 +05:30
parent e9752c3d61
commit 7cd64883e2
2 changed files with 25 additions and 4 deletions

View File

@@ -43,10 +43,11 @@ class EventsService {
/// Get events filtered by pincode with pagination. /// Get events filtered by pincode with pagination.
/// [page] starts at 1. [pageSize] defaults to 50. /// [page] starts at 1. [pageSize] defaults to 50.
/// Returns a list of events for the requested page. /// Returns a list of events for the requested page.
Future<List<EventModel>> getEventsByPincode(String pincode, {int page = 1, int pageSize = 50, int perType = 5}) async { Future<List<EventModel>> getEventsByPincode(String pincode, {int page = 1, int pageSize = 50, int perType = 5, String q = ''}) async {
// Use cache for 'all' pincode queries (first page only for initial load) // Use cache for 'all' pincode queries (first page only, no active search)
if (pincode == 'all' && if (pincode == 'all' &&
page == 1 && page == 1 &&
q.isEmpty &&
_cachedAllEvents != null && _cachedAllEvents != null &&
_eventsCacheTime != null && _eventsCacheTime != null &&
DateTime.now().difference(_eventsCacheTime!) < _eventsCacheTTL) { DateTime.now().difference(_eventsCacheTime!) < _eventsCacheTTL) {
@@ -56,6 +57,8 @@ class EventsService {
final Map<String, dynamic> body = {'pincode': pincode, 'page': page, 'page_size': pageSize}; final Map<String, dynamic> body = {'pincode': pincode, 'page': page, 'page_size': pageSize};
// Diverse mode: fetch a few events per type so all categories are represented // Diverse mode: fetch a few events per type so all categories are represented
if (perType > 0 && page == 1) body['per_type'] = perType; if (perType > 0 && page == 1) body['per_type'] = perType;
// Server-side search filter
if (q.isNotEmpty) body['q'] = q;
final res = await _api.post( final res = await _api.post(
ApiEndpoints.eventsByPincode, ApiEndpoints.eventsByPincode,

View File

@@ -302,7 +302,9 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
builder: (context, scrollController) { builder: (context, scrollController) {
String query = ''; String query = '';
List<EventModel> results = List.from(_events); List<EventModel> results = List.from(_events);
bool searching = false;
return StatefulBuilder(builder: (context, setModalState) { return StatefulBuilder(builder: (context, setModalState) {
// Instant client-side filter while typing
void _onQueryChanged(String v) { void _onQueryChanged(String v) {
query = v.trim().toLowerCase(); query = v.trim().toLowerCase();
final r = _events.where((e) { final r = _events.where((e) {
@@ -314,6 +316,22 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
}); });
} }
// Server-side search on submit (keyboard action / enter)
Future<void> _onSubmitted(String v) async {
final q = v.trim();
if (q.isEmpty) return;
setModalState(() => searching = true);
try {
final serverResults = await _eventsService.getEventsByPincode(_pincode, q: q);
setModalState(() {
results = serverResults;
searching = false;
});
} catch (_) {
setModalState(() => searching = false);
}
}
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: theme.cardColor, color: theme.cardColor,
@@ -357,7 +375,7 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
autofocus: true, autofocus: true,
onChanged: _onQueryChanged, onChanged: _onQueryChanged,
textInputAction: TextInputAction.search, textInputAction: TextInputAction.search,
onSubmitted: (v) => _onQueryChanged(v), onSubmitted: _onSubmitted,
), ),
) )
], ],
@@ -372,7 +390,7 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
], ],
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
if (_loading) if (_loading || searching)
Center(child: CircularProgressIndicator(color: theme.colorScheme.primary)) Center(child: CircularProgressIndicator(color: theme.colorScheme.primary))
else if (results.isEmpty) else if (results.isEmpty)
Padding( Padding(