feat: HOME-007 — server-side event title/description search (q param)
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user