Files
2026-05-24 02:35:37 -04:00

132 lines
3.9 KiB
Dart

import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/models/marketplace.dart';
import '../../../core/services/marketplace_service.dart';
final marketplaceServiceProvider = Provider((_) => MarketplaceService());
final citiesProvider = FutureProvider<List<String>>((ref) {
return ref.read(marketplaceServiceProvider).getCities();
});
final offersProvider = FutureProvider<List<MarketplaceOffer>>((ref) {
return ref.read(marketplaceServiceProvider).getOffers();
});
class SearchParams {
final String? city;
final DateTime? startDate;
final DateTime? endDate;
final Set<String>? categories; // null = all; multi-select
final String? transmission;
final int? maxPriceCents;
final int? minPriceCents; // client-side filter only
final int page;
const SearchParams({
this.city,
this.startDate,
this.endDate,
this.categories,
this.transmission,
this.maxPriceCents,
this.minPriceCents,
this.page = 1,
});
bool get hasDateRange => startDate != null && endDate != null;
bool get hasFilters =>
city != null ||
(categories != null && categories!.isNotEmpty) ||
transmission != null ||
maxPriceCents != null ||
minPriceCents != null ||
hasDateRange;
SearchParams copyWith({
Object? city = _sentinel,
DateTime? startDate,
DateTime? endDate,
Object? categories = _sentinel,
Object? transmission = _sentinel,
Object? maxPriceCents = _sentinel,
Object? minPriceCents = _sentinel,
int? page,
bool clearDates = false,
}) =>
SearchParams(
city: city == _sentinel ? this.city : city as String?,
startDate: clearDates ? null : (startDate ?? this.startDate),
endDate: clearDates ? null : (endDate ?? this.endDate),
categories: categories == _sentinel
? this.categories
: categories as Set<String>?,
transmission: transmission == _sentinel
? this.transmission
: transmission as String?,
maxPriceCents: maxPriceCents == _sentinel
? this.maxPriceCents
: maxPriceCents as int?,
minPriceCents: minPriceCents == _sentinel
? this.minPriceCents
: minPriceCents as int?,
page: page ?? this.page,
);
static bool _setsEqual(Set<String>? a, Set<String>? b) {
if (a == null && b == null) return true;
if (a == null || b == null) return false;
if (a.length != b.length) return false;
return a.containsAll(b);
}
@override
bool operator ==(Object other) =>
other is SearchParams &&
other.city == city &&
other.startDate == startDate &&
other.endDate == endDate &&
_setsEqual(other.categories, categories) &&
other.transmission == transmission &&
other.maxPriceCents == maxPriceCents &&
other.minPriceCents == minPriceCents &&
other.page == page;
@override
int get hashCode {
final catKey = categories == null
? null
: (categories!.toList()..sort()).join(',');
return Object.hash(city, startDate, endDate, catKey, transmission,
maxPriceCents, minPriceCents, page);
}
}
const _sentinel = Object();
final searchParamsProvider = StateProvider<SearchParams>((_) {
final now = DateTime.now();
return SearchParams(
startDate: now,
endDate: now.add(const Duration(days: 1)),
);
});
final vehicleSearchProvider =
FutureProvider.family<MarketplaceSearchResult, SearchParams>(
(ref, params) {
// Only send a single category to the API; multi-category is filtered client-side
final apiCategory =
params.categories?.length == 1 ? params.categories!.first : null;
return ref.read(marketplaceServiceProvider).search(
city: params.city,
startDate: params.startDate,
endDate: params.endDate,
category: apiCategory,
transmission: params.transmission,
maxPriceCents: params.maxPriceCents,
page: params.page,
);
});