Files
ridebus-admin/frontend/src/components/BoardPassengerSettings.jsx

41 lines
6.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
export const passengerDefaults = { infoPlacesEnabled: true, infoFareEnabled: true, lastTripNoticeEnabled: true, weatherHintEnabled: true, places: [], fareRules: [], fareByType: [] };
export const cityFareDefaults = cityId => cityId === 1 ? [
{ cityId: 1, type: 'bus', price: '1,05 BYN', payment: 'QR-кодом или наличными', checkedAt: '2026-10-11' },
{ cityId: 1, type: 'minibus', price: '2,10 BYN', payment: 'QR-кодом или наличными', checkedAt: '2026-10-11' },
] : [];
const today = () => new Intl.DateTimeFormat('sv-SE', { timeZone: 'Europe/Minsk' }).format(Date.now());
export function BoardPassengerSettings({ settings, stops, routes, cityId, set }) {
const editPlace = (index, patch) => set('places', settings.places.map((value, i) => i === index ? { ...value, ...patch } : value));
const editRule = (index, patch) => set('fareRules', settings.fareRules.map((value, i) => i === index ? { ...value, ...patch } : value));
return <div className="device-passenger-settings">
<h3>Важные места</h3><p className="device-night-hint">До трёх остановок назначения. Укажите короткое название для пассажиров; табло найдёт ближайший прямой рейс с учётом его конечной и направления.</p>
{settings.places.map((place, index) => <fieldset className="device-passenger-editor" key={index}><legend>Место {index + 1}</legend>
<label>Остановка назначения<select aria-label={`Остановка места ${index + 1}`} required value={place.stopId || ''} onChange={e => { const stop = stops.find(s => s.id === Number(e.target.value)); editPlace(index, { stopId: Number(e.target.value), name: stop?.name.slice(0, 48) || '' }); }}>
<option value="">Выберите остановку</option>{stops.filter(s => s.id !== settings.stopId).map(stop => <option key={stop.id} value={stop.id} disabled={settings.places.some((p, i) => i !== index && p.stopId === stop.id)}>{stop.name}{stop.description ? ` · ${stop.description}` : ''}</option>)}
</select></label><label>Название на табло<input aria-label={`Название места ${index + 1}`} required maxLength={48} value={place.name} onChange={e => editPlace(index, { name: e.target.value })} /></label>
<button type="button" onClick={() => set('places', settings.places.filter((_, i) => i !== index))}>Удалить место {index + 1}</button>
</fieldset>)}
<button type="button" disabled={settings.places.length >= 3 || !stops.length} onClick={() => set('places', [...settings.places, { stopId: 0, name: '' }])}>Добавить место</button>
<h3>Оплата поездки</h3><p className="device-night-hint">Основные тарифы по типу транспорта. Только подтверждённые цена с валютой, способы оплаты и дата проверки. Без подходящего тарифа карточка скрыта.</p>
{['bus', 'minibus'].map(type => {
const rule = settings.fareByType.find(r => r.type === type && r.cityId === cityId), label = type === 'bus' ? 'автобуса' : 'маршрутки';
const edit = patch => set('fareByType', settings.fareByType.map(r => r.type === type && r.cityId === cityId ? { ...r, ...patch } : r));
return <fieldset className="device-passenger-editor" key={type}><legend>Тариф {label}</legend>
<label className="device-check"><input type="checkbox" checked={!!rule} onChange={e => set('fareByType', [...settings.fareByType.filter(r => r.type !== type), ...(e.target.checked ? [{ cityId, type, price: '', payment: '', checkedAt: today() }] : [])])} />Использовать тариф {label}</label>
{rule && <><div className="device-form-grid"><label>Стоимость с валютой<input aria-label={`Стоимость ${label}`} required maxLength={40} value={rule.price} onChange={e => edit({ price: e.target.value })} /></label><label>Проверено<input aria-label={`Дата проверки ${label}`} required type="date" max={today()} value={rule.checkedAt} onChange={e => edit({ checkedAt: e.target.value })} /></label></div>
<label>Способы оплаты<input aria-label={`Способы оплаты ${label}`} required maxLength={100} value={rule.payment} onChange={e => edit({ payment: e.target.value })} /></label></>}
</fieldset>;
})}
<h3>Исключения для маршрутов</h3><p className="device-night-hint">Индивидуальный тариф заменяет основной тариф типа транспорта.</p>
{settings.fareRules.map((rule, index) => <fieldset className="device-passenger-editor" key={index}><legend>Тариф {index + 1}</legend>
<label>Маршрут<select aria-label={`Маршрут тарифа ${index + 1}`} required value={rule.routeId || ''} onChange={e => editRule(index, { routeId: Number(e.target.value) })}>
<option value="">Выберите маршрут</option>{!routes.some(r => r.id === rule.routeId) && rule.routeId > 0 && <option value={rule.routeId}>Маршрут {rule.routeId} · сохранённый тариф</option>}
{routes.map(route => <option key={route.id} value={route.id} disabled={settings.fareRules.some((r, i) => i !== index && r.routeId === route.id)}>№{route.route_number} · {route.name}</option>)}
</select></label><div className="device-form-grid"><label>Стоимость с валютой<input aria-label={`Стоимость тарифа ${index + 1}`} required maxLength={40} value={rule.price} onChange={e => editRule(index, { price: e.target.value })} /></label><label>Проверено<input aria-label={`Дата проверки тарифа ${index + 1}`} required type="date" max={today()} value={rule.checkedAt} onChange={e => editRule(index, { checkedAt: e.target.value })} /></label></div>
<label>Способы оплаты<input aria-label={`Способы оплаты тарифа ${index + 1}`} required maxLength={100} value={rule.payment} onChange={e => editRule(index, { payment: e.target.value })} /></label>
<button type="button" onClick={() => set('fareRules', settings.fareRules.filter((_, i) => i !== index))}>Удалить тариф {index + 1}</button>
</fieldset>)}
<button type="button" disabled={settings.fareRules.length >= 32 || !routes.length} onClick={() => set('fareRules', [...settings.fareRules, { routeId: 0, price: '', payment: '', checkedAt: today() }])}>Добавить тариф</button>
</div>;
}