Make EV charging an amenity toggle and drop filter select
This commit is contained in:
parent
c08dca02e7
commit
3d13bf3ba3
2 changed files with 16 additions and 45 deletions
|
|
@ -137,6 +137,7 @@ export default function NewListingPage() {
|
||||||
{ key: 'microwave', label: t('amenityMicrowave'), icon: '🍲', checked: hasMicrowave, toggle: setHasMicrowave },
|
{ key: 'microwave', label: t('amenityMicrowave'), icon: '🍲', checked: hasMicrowave, toggle: setHasMicrowave },
|
||||||
{ key: 'parking', label: t('amenityFreeParking'), icon: '🅿️', checked: hasFreeParking, toggle: setHasFreeParking },
|
{ key: 'parking', label: t('amenityFreeParking'), icon: '🅿️', checked: hasFreeParking, toggle: setHasFreeParking },
|
||||||
{ key: 'ski', label: t('amenitySkiPass'), icon: '⛷️', checked: hasSkiPass, toggle: setHasSkiPass },
|
{ key: 'ski', label: t('amenitySkiPass'), icon: '⛷️', checked: hasSkiPass, toggle: setHasSkiPass },
|
||||||
|
{ key: 'ev', label: t('amenityEvAvailable'), icon: '⚡', checked: evChargingAvailable, toggle: setEvChargingAvailable },
|
||||||
];
|
];
|
||||||
|
|
||||||
function updateTranslation(locale: Locale, field: keyof LocaleFields, value: string) {
|
function updateTranslation(locale: Locale, field: keyof LocaleFields, value: string) {
|
||||||
|
|
@ -704,28 +705,6 @@ export default function NewListingPage() {
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
<div className="amenity-ev">
|
|
||||||
<div className="amenity-ev-label">{t('evChargingLabel')}</div>
|
|
||||||
<div style={{ color: '#cbd5e1', fontSize: 12, marginBottom: 6 }}>{t('evChargingExplain')}</div>
|
|
||||||
<div className="ev-toggle-group">
|
|
||||||
{[
|
|
||||||
{ value: true, label: t('evChargingYes'), icon: '⚡' },
|
|
||||||
{ value: false, label: t('evChargingNo'), icon: '🚗' },
|
|
||||||
].map((opt) => (
|
|
||||||
<button
|
|
||||||
key={String(opt.value)}
|
|
||||||
type="button"
|
|
||||||
className={`ev-toggle ${evChargingAvailable === opt.value ? 'active' : ''}`}
|
|
||||||
onClick={() => setEvChargingAvailable(opt.value)}
|
|
||||||
>
|
|
||||||
<span aria-hidden className="amenity-emoji">
|
|
||||||
{opt.icon}
|
|
||||||
</span>
|
|
||||||
{opt.label}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'grid', gap: 8, gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))' }}>
|
<div style={{ display: 'grid', gap: 8, gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))' }}>
|
||||||
<label>
|
<label>
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,6 @@ export default function ListingsIndexPage() {
|
||||||
const [query, setQuery] = useState('');
|
const [query, setQuery] = useState('');
|
||||||
const [city, setCity] = useState('');
|
const [city, setCity] = useState('');
|
||||||
const [region, setRegion] = useState('');
|
const [region, setRegion] = useState('');
|
||||||
const [evCharging, setEvCharging] = useState<'ALL' | 'YES' | 'NO'>('ALL');
|
|
||||||
const [listings, setListings] = useState<ListingResult[]>([]);
|
const [listings, setListings] = useState<ListingResult[]>([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
@ -207,10 +206,7 @@ export default function ListingsIndexPage() {
|
||||||
});
|
});
|
||||||
}, [listings, addressCenter, radiusKm]);
|
}, [listings, addressCenter, radiusKm]);
|
||||||
|
|
||||||
const filtered = useMemo(() => {
|
const filtered = filteredByAddress;
|
||||||
if (evCharging === 'ALL') return filteredByAddress;
|
|
||||||
return filteredByAddress.filter((l) => (evCharging === 'YES' ? l.evChargingAvailable : !l.evChargingAvailable));
|
|
||||||
}, [filteredByAddress, evCharging]);
|
|
||||||
|
|
||||||
const amenityOptions = [
|
const amenityOptions = [
|
||||||
{ key: 'sauna', label: t('amenitySauna'), icon: amenityIcons.sauna },
|
{ key: 'sauna', label: t('amenitySauna'), icon: amenityIcons.sauna },
|
||||||
|
|
@ -226,6 +222,7 @@ export default function ListingsIndexPage() {
|
||||||
{ key: 'microwave', label: t('amenityMicrowave'), icon: amenityIcons.microwave },
|
{ key: 'microwave', label: t('amenityMicrowave'), icon: amenityIcons.microwave },
|
||||||
{ key: 'parking', label: t('amenityFreeParking'), icon: amenityIcons.parking },
|
{ key: 'parking', label: t('amenityFreeParking'), icon: amenityIcons.parking },
|
||||||
{ key: 'skipass', label: t('amenitySkiPass'), icon: amenityIcons.ski },
|
{ key: 'skipass', label: t('amenitySkiPass'), icon: amenityIcons.ski },
|
||||||
|
{ key: 'ev', label: t('amenityEvAvailable'), icon: amenityIcons.ev },
|
||||||
];
|
];
|
||||||
|
|
||||||
async function fetchListings() {
|
async function fetchListings() {
|
||||||
|
|
@ -236,10 +233,13 @@ export default function ListingsIndexPage() {
|
||||||
if (query) params.set('q', query);
|
if (query) params.set('q', query);
|
||||||
if (city) params.set('city', city);
|
if (city) params.set('city', city);
|
||||||
if (region) params.set('region', region);
|
if (region) params.set('region', region);
|
||||||
if (evCharging !== 'ALL') params.set('evCharging', evCharging === 'YES' ? 'true' : 'false');
|
|
||||||
if (startDate) params.set('availableStart', startDate);
|
if (startDate) params.set('availableStart', startDate);
|
||||||
if (endDate) params.set('availableEnd', endDate);
|
if (endDate) params.set('availableEnd', endDate);
|
||||||
amenities.forEach((a) => params.append('amenity', a));
|
const evSelected = amenities.includes('ev');
|
||||||
|
if (evSelected) params.set('evCharging', 'true');
|
||||||
|
amenities
|
||||||
|
.filter((a) => a !== 'ev')
|
||||||
|
.forEach((a) => params.append('amenity', a));
|
||||||
const res = await fetch(`/api/listings?${params.toString()}`, { cache: 'no-store' });
|
const res = await fetch(`/api/listings?${params.toString()}`, { cache: 'no-store' });
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (!res.ok || data.error) {
|
if (!res.ok || data.error) {
|
||||||
|
|
@ -321,14 +321,6 @@ export default function ListingsIndexPage() {
|
||||||
{t('regionFilter')}
|
{t('regionFilter')}
|
||||||
<input value={region} onChange={(e) => setRegion(e.target.value)} placeholder={t('regionFilter')} />
|
<input value={region} onChange={(e) => setRegion(e.target.value)} placeholder={t('regionFilter')} />
|
||||||
</label>
|
</label>
|
||||||
<label>
|
|
||||||
{t('evChargingLabel')}
|
|
||||||
<select value={evCharging} onChange={(e) => setEvCharging(e.target.value as any)}>
|
|
||||||
<option value="ALL">{t('evChargingAny')}</option>
|
|
||||||
<option value="YES">{t('evChargingYes')}</option>
|
|
||||||
<option value="NO">{t('evChargingNo')}</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'grid', gap: 10, gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', marginTop: 12 }}>
|
<div style={{ display: 'grid', gap: 10, gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', marginTop: 12 }}>
|
||||||
<label>
|
<label>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue