feat: переезд внутри карточки точки, время выезда обязательно
Транспорт и время выезда переехали в карточку точки, куда едут («Как добраться: Алматы → Астана»), обратный переезд — под переключателем «Возвращение». Отдельной строкой на линии маршрута участок пропускали. Время выезда теперь обязательно — оно нужно тревел-координатору под билеты. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -50,8 +50,11 @@
|
|||||||
каждого участка — вид транспорта плашками (`legTransport`, `TRANSPORT_TYPES`: авиа, ж/д,
|
каждого участка — вид транспорта плашками (`legTransport`, `TRANSPORT_TYPES`: авиа, ж/д,
|
||||||
такси, служебный и личный автотранспорт; «Автобус» заменён на «Такси» 10-23), обязателен.
|
такси, служебный и личный автотранспорт; «Автобус» заменён на «Такси» 10-23), обязателен.
|
||||||
Там же **предпочтительное время выезда** (с 10-23): «Утро / День / Вечер / Ночь»
|
Там же **предпочтительное время выезда** (с 10-23): «Утро / День / Вечер / Ночь»
|
||||||
(`legDepartureTime`, `DEPARTURE_TIMES`), необязательно — повторный клик снимает выбор. На
|
(`legDepartureTime`, `DEPARTURE_TIMES`), тоже обязательно — нужно тревел-координатору под
|
||||||
схеме — только в подсказке плашки транспорта, вёрстку схемы не трогали.
|
билеты. На схеме — только в подсказке плашки транспорта, вёрстку схемы не трогали.
|
||||||
|
- **Переезд — внутри карточки точки** (с 10-23, `LegFields`): блок «Как добраться: Алматы →
|
||||||
|
Астана» внизу карточки точки, куда едут; обратный — под переключателем «Возвращение».
|
||||||
|
Раньше участок был отдельной строкой на линии маршрута — его пропускали при заполнении.
|
||||||
- Объект точки по Казахстану — только выбор из справочника (`projectId`): по объектам
|
- Объект точки по Казахстану — только выбор из справочника (`projectId`): по объектам
|
||||||
разносят затраты. У зарубежной точки поле «Объект / место назначения» принимает и свой
|
разносят затраты. У зарубежной точки поле «Объект / место назначения» принимает и свой
|
||||||
текст (завод, выставка — в справочнике их нет), обязательно лишь непустое значение.
|
текст (завод, выставка — в справочнике их нет), обязательно лишь непустое значение.
|
||||||
|
|||||||
@@ -12,15 +12,15 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Предпочтительное время выезда плашками. Поле необязательное: повторный клик по
|
* Предпочтительное время выезда плашками (обязательно — см. validation.ts).
|
||||||
* выбранной плашке снимает выбор. В режиме чтения — только выбранная плашка
|
* Повторный клик по выбранной плашке снимает выбор. В режиме чтения — только
|
||||||
* (не указано — ничего не показываем).
|
* выбранная плашка.
|
||||||
*/
|
*/
|
||||||
export const DepartureTimeChips: React.FC<Props> = ({ value, onChange, ariaLabel }) => {
|
export const DepartureTimeChips: React.FC<Props> = ({ value, onChange, ariaLabel }) => {
|
||||||
const readOnly = useFormReadOnly();
|
const readOnly = useFormReadOnly();
|
||||||
|
|
||||||
if (readOnly) {
|
if (readOnly) {
|
||||||
return value ? <span className={`${styles.chip} ${styles.selected} ${styles.static}`}>{value}</span> : null;
|
return <span className={`${styles.chip} ${styles.selected} ${styles.static}`}>{value || 'не указано'}</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
@import '../../../../../../styles/design-tokens';
|
||||||
|
|
||||||
|
// Переезд внутри карточки точки — отдельная плашка с фоном, чтобы её не пропускали.
|
||||||
|
.leg {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $sp-3;
|
||||||
|
padding: $sp-3;
|
||||||
|
border-radius: $r-sm;
|
||||||
|
background: $gray-50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-family: $font-sans;
|
||||||
|
font-size: $fs-sm;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $navy-900;
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import styles from './LegFields.module.scss';
|
||||||
|
import { FormField } from '../../../../shared/ui';
|
||||||
|
import { IRouteLeg, legLabel } from '../../route';
|
||||||
|
import { fieldKey } from '../../validation';
|
||||||
|
import { TransportChips } from '../TransportChips';
|
||||||
|
import { DepartureTimeChips } from '../DepartureTimeChips';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
leg: IRouteLeg;
|
||||||
|
transport: string;
|
||||||
|
onTransportChange: (value: string) => void;
|
||||||
|
departureTime: string;
|
||||||
|
onDepartureTimeChange: (value: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Переезд «как добраться»: вид транспорта и время выезда (оба обязательны).
|
||||||
|
* Живёт внутри карточки точки, куда едут (обратный — в блоке «Возвращение»):
|
||||||
|
* отдельной строкой на линии маршрута его пропускали при заполнении.
|
||||||
|
*/
|
||||||
|
export const LegFields: React.FC<Props> = ({ leg, transport, onTransportChange, departureTime, onDepartureTimeChange }) => (
|
||||||
|
<div className={styles.leg}>
|
||||||
|
<span className={styles.title}>Как добраться: {legLabel(leg)}</span>
|
||||||
|
<FormField label="Вид транспорта" fieldKey={fieldKey.legTransport(leg.id)} required>
|
||||||
|
<TransportChips value={transport} onChange={onTransportChange} ariaLabel={`Вид транспорта: ${legLabel(leg)}`} />
|
||||||
|
</FormField>
|
||||||
|
<FormField label="Время выезда" fieldKey={fieldKey.legDepartureTime(leg.id)} required>
|
||||||
|
<DepartureTimeChips value={departureTime} onChange={onDepartureTimeChange} ariaLabel={`Время выезда: ${legLabel(leg)}`} />
|
||||||
|
</FormField>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
@@ -108,7 +108,11 @@ $marker-size: 26px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Возвращение: переключатель, а под ним — обратный переезд (транспорт, время выезда).
|
||||||
.end {
|
.end {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $sp-3;
|
||||||
padding-top: $sp-2;
|
padding-top: $sp-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { CITY_PLACEHOLDER, IRouteStop, ITripRoute } from '../../model';
|
|||||||
import { IRouteLeg, createStop, originLabel, routeLegs, withoutStopLegs } from '../../route';
|
import { IRouteLeg, createStop, originLabel, routeLegs, withoutStopLegs } from '../../route';
|
||||||
import { fieldKey } from '../../validation';
|
import { fieldKey } from '../../validation';
|
||||||
import { RouteStopCard } from './RouteStopCard';
|
import { RouteStopCard } from './RouteStopCard';
|
||||||
import { RouteLegRow } from './RouteLegRow';
|
import { LegFields } from './LegFields';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
route: ITripRoute;
|
route: ITripRoute;
|
||||||
@@ -33,9 +33,10 @@ const TimelineRow: React.FC<{ marker: MarkerKind; label?: string; children: Reac
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Маршрут командировки лентой: город отправления → точки (город + объект +
|
* Маршрут командировки лентой: город отправления → точки (город + объект +
|
||||||
* даты) → обратно. Между точками — участки пути с видом транспорта; участки
|
* даты) → обратно. Переезд (транспорт, время выезда) — внутри карточки точки, куда
|
||||||
* не хранятся, а выводятся из точек (route.ts/routeLegs). Простая поездка — одна
|
* едут, обратный — в блоке «Возвращение»: отдельной строкой на линии его
|
||||||
* точка; «+ Добавить точку» — для поездок по нескольким объектам.
|
* пропускали. Участки не хранятся, а выводятся из точек (route.ts/routeLegs).
|
||||||
|
* Простая поездка — одна точка; «+ Добавить точку» — для поездок по нескольким объектам.
|
||||||
*/
|
*/
|
||||||
export const RouteEditor: React.FC<Props> = ({ route, onChange, context }) => {
|
export const RouteEditor: React.FC<Props> = ({ route, onChange, context }) => {
|
||||||
const readOnly = useFormReadOnly();
|
const readOnly = useFormReadOnly();
|
||||||
@@ -60,17 +61,15 @@ export const RouteEditor: React.FC<Props> = ({ route, onChange, context }) => {
|
|||||||
const setDepartureTime = (id: string, value: string): void =>
|
const setDepartureTime = (id: string, value: string): void =>
|
||||||
onChange({ ...route, legDepartureTime: { ...route.legDepartureTime, [id]: value } });
|
onChange({ ...route, legDepartureTime: { ...route.legDepartureTime, [id]: value } });
|
||||||
|
|
||||||
const legRow = (leg: IRouteLeg | undefined): React.ReactNode =>
|
const legFields = (leg: IRouteLeg | undefined): React.ReactNode =>
|
||||||
leg && (
|
leg && (
|
||||||
<TimelineRow marker="line">
|
<LegFields
|
||||||
<RouteLegRow
|
leg={leg}
|
||||||
leg={leg}
|
transport={route.legTransport[leg.id] || ''}
|
||||||
transport={route.legTransport[leg.id] || ''}
|
onTransportChange={(v) => setTransport(leg.id, v)}
|
||||||
onTransportChange={(v) => setTransport(leg.id, v)}
|
departureTime={route.legDepartureTime[leg.id] || ''}
|
||||||
departureTime={route.legDepartureTime[leg.id] || ''}
|
onDepartureTimeChange={(v) => setDepartureTime(leg.id, v)}
|
||||||
onDepartureTimeChange={(v) => setDepartureTime(leg.id, v)}
|
/>
|
||||||
/>
|
|
||||||
</TimelineRow>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -89,18 +88,16 @@ export const RouteEditor: React.FC<Props> = ({ route, onChange, context }) => {
|
|||||||
</TimelineRow>
|
</TimelineRow>
|
||||||
|
|
||||||
{route.stops.map((stop, i) => (
|
{route.stops.map((stop, i) => (
|
||||||
<React.Fragment key={stop.id}>
|
<TimelineRow key={stop.id} marker="stop" label={String(i + 1)}>
|
||||||
{legRow(legTo(stop.id))}
|
<RouteStopCard
|
||||||
<TimelineRow marker="stop" label={String(i + 1)}>
|
stop={stop}
|
||||||
<RouteStopCard
|
index={i}
|
||||||
stop={stop}
|
context={context}
|
||||||
index={i}
|
onChange={(patch) => updateStop(stop.id, patch)}
|
||||||
context={context}
|
onRemove={!readOnly && route.stops.length > 1 ? () => removeStop(stop.id) : undefined}
|
||||||
onChange={(patch) => updateStop(stop.id, patch)}
|
inbound={legFields(legTo(stop.id))}
|
||||||
onRemove={!readOnly && route.stops.length > 1 ? () => removeStop(stop.id) : undefined}
|
/>
|
||||||
/>
|
</TimelineRow>
|
||||||
</TimelineRow>
|
|
||||||
</React.Fragment>
|
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{!readOnly && (
|
{!readOnly && (
|
||||||
@@ -112,8 +109,6 @@ export const RouteEditor: React.FC<Props> = ({ route, onChange, context }) => {
|
|||||||
</TimelineRow>
|
</TimelineRow>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{legRow(returnLeg)}
|
|
||||||
|
|
||||||
<TimelineRow marker="endpoint">
|
<TimelineRow marker="endpoint">
|
||||||
<div className={styles.end}>
|
<div className={styles.end}>
|
||||||
{readOnly ? (
|
{readOnly ? (
|
||||||
@@ -127,6 +122,8 @@ export const RouteEditor: React.FC<Props> = ({ route, onChange, context }) => {
|
|||||||
onLabel={`Возвращение в ${originLabel(route) || 'город отправления'}`}
|
onLabel={`Возвращение в ${originLabel(route) || 'город отправления'}`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{/* Обратный переезд — здесь: своей карточки точки у него нет. */}
|
||||||
|
{legFields(returnLeg)}
|
||||||
</div>
|
</div>
|
||||||
</TimelineRow>
|
</TimelineRow>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
@import '../../../../../../styles/design-tokens';
|
|
||||||
|
|
||||||
// Участок — без карточки: это «переезд» между точками, он лежит прямо на линии маршрута.
|
|
||||||
.leg {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: $sp-2;
|
|
||||||
padding-block: $sp-2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Время выезда — второстепенно: подпись слева, плашки в строку; на узком экране переносятся.
|
|
||||||
.time {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: center;
|
|
||||||
gap: $sp-2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeLabel {
|
|
||||||
font-family: $font-sans;
|
|
||||||
font-size: $fs-label;
|
|
||||||
color: $gray-500;
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import * as React from 'react';
|
|
||||||
import styles from './RouteLegRow.module.scss';
|
|
||||||
import { FormField, useFormReadOnly } from '../../../../shared/ui';
|
|
||||||
import { IRouteLeg, legLabel } from '../../route';
|
|
||||||
import { fieldKey } from '../../validation';
|
|
||||||
import { TransportChips } from '../TransportChips';
|
|
||||||
import { DepartureTimeChips } from '../DepartureTimeChips';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
leg: IRouteLeg;
|
|
||||||
transport: string;
|
|
||||||
onTransportChange: (value: string) => void;
|
|
||||||
/** Предпочтительное время выезда; '' — не указано. */
|
|
||||||
departureTime: string;
|
|
||||||
onDepartureTimeChange: (value: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Участок пути между точками на линии маршрута: «Алматы → Атырау», вид транспорта
|
|
||||||
* и предпочтительное время выезда (необязательно).
|
|
||||||
*/
|
|
||||||
export const RouteLegRow: React.FC<Props> = ({ leg, transport, onTransportChange, departureTime, onDepartureTimeChange }) => {
|
|
||||||
const readOnly = useFormReadOnly();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.leg}>
|
|
||||||
<FormField label={legLabel(leg)} fieldKey={fieldKey.legTransport(leg.id)}>
|
|
||||||
<TransportChips value={transport} onChange={onTransportChange} ariaLabel={`Вид транспорта: ${legLabel(leg)}`} />
|
|
||||||
</FormField>
|
|
||||||
{/* В просмотре без указанного времени строка не нужна. */}
|
|
||||||
{(!readOnly || departureTime) && (
|
|
||||||
<div className={styles.time}>
|
|
||||||
<span className={styles.timeLabel}>Выезд</span>
|
|
||||||
<DepartureTimeChips
|
|
||||||
value={departureTime}
|
|
||||||
onChange={onDepartureTimeChange}
|
|
||||||
ariaLabel={`Время выезда: ${legLabel(leg)}`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -18,6 +18,8 @@ interface Props {
|
|||||||
onChange: (patch: Partial<IRouteStop>) => void;
|
onChange: (patch: Partial<IRouteStop>) => void;
|
||||||
/** Не передан — точку удалить нельзя (она единственная). */
|
/** Не передан — точку удалить нельзя (она единственная). */
|
||||||
onRemove?: () => void;
|
onRemove?: () => void;
|
||||||
|
/** Переезд в эту точку («Как добраться») — собирает RouteEditor, он знает участки. */
|
||||||
|
inbound?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** id карточки точки в DOM — к ней прокручивает клик по точке на схеме маршрута. */
|
/** id карточки точки в DOM — к ней прокручивает клик по точке на схеме маршрута. */
|
||||||
@@ -33,10 +35,11 @@ const LOCATIONS: ReadonlyArray<ISegmentedOption<StopLocation>> = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Точка маршрута: Казахстан или другая страна, город, объект из справочника и даты
|
* Точка маршрута: Казахстан или другая страна, город, объект из справочника, даты
|
||||||
* пребывания. В режиме чтения переключатель не нужен — страна видна в полях.
|
* пребывания и переезд сюда (транспорт, время выезда). В режиме чтения переключатель
|
||||||
|
* не нужен — страна видна в полях.
|
||||||
*/
|
*/
|
||||||
export const RouteStopCard: React.FC<Props> = ({ stop, index, context, onChange, onRemove }) => {
|
export const RouteStopCard: React.FC<Props> = ({ stop, index, context, onChange, onRemove, inbound }) => {
|
||||||
const readOnly = useFormReadOnly();
|
const readOnly = useFormReadOnly();
|
||||||
const nights = stopNights(stop);
|
const nights = stopNights(stop);
|
||||||
|
|
||||||
@@ -98,6 +101,8 @@ export const RouteStopCard: React.FC<Props> = ({ stop, index, context, onChange,
|
|||||||
/>
|
/>
|
||||||
</FormField>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
|
{/* После города и дат — тогда в заголовке переезда уже видно, куда едут. */}
|
||||||
|
{inbound}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const ROUTE: ITripRoute = {
|
|||||||
stops: [{ id: 's1', abroad: false, country: '', city: 'Атырау', cityKz: '', cityKato: '', project: 'Нефтебаза-2', projectId: 'p1', dateFrom: '2026-10-05', dateTo: '2026-10-08' }],
|
stops: [{ id: 's1', abroad: false, country: '', city: 'Атырау', cityKz: '', cityKato: '', project: 'Нефтебаза-2', projectId: 'p1', dateFrom: '2026-10-05', dateTo: '2026-10-08' }],
|
||||||
returnToOrigin: true,
|
returnToOrigin: true,
|
||||||
legTransport: { 'origin>s1': 'Авиа', 's1>origin': 'Авиа' },
|
legTransport: { 'origin>s1': 'Авиа', 's1>origin': 'Авиа' },
|
||||||
legDepartureTime: {}
|
legDepartureTime: { 'origin>s1': 'Утро', 's1>origin': 'Вечер' }
|
||||||
};
|
};
|
||||||
|
|
||||||
const FILLED: IFormData = {
|
const FILLED: IFormData = {
|
||||||
@@ -79,6 +79,11 @@ describe('validation: маршрут', () => {
|
|||||||
expect(fields(submit({ ...FILLED, Route: route }))).toEqual(['leg-s1>origin']);
|
expect(fields(submit({ ...FILLED, Route: route }))).toEqual(['leg-s1>origin']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('у каждого участка нужно время выезда', () => {
|
||||||
|
const route = { ...ROUTE, legDepartureTime: { 'origin>s1': 'Утро' } };
|
||||||
|
expect(fields(submit({ ...FILLED, Route: route }))).toEqual(['leg-s1>origin-time']);
|
||||||
|
});
|
||||||
|
|
||||||
it('прибытие задним числом не отправить, сегодня — можно; черновик сохраняется', () => {
|
it('прибытие задним числом не отправить, сегодня — можно; черновик сохраняется', () => {
|
||||||
expect(fields(submitIssues(FILLED, [], '2026-10-06'))).toEqual(['stop-s1-from']);
|
expect(fields(submitIssues(FILLED, [], '2026-10-06'))).toEqual(['stop-s1-from']);
|
||||||
expect(submitIssues(FILLED, [], '2026-10-05')).toEqual([]);
|
expect(submitIssues(FILLED, [], '2026-10-05')).toEqual([]);
|
||||||
@@ -99,7 +104,7 @@ describe('validation: маршрут', () => {
|
|||||||
|
|
||||||
it('прибытие в следующую точку раньше отъезда из предыдущей', () => {
|
it('прибытие в следующую точку раньше отъезда из предыдущей', () => {
|
||||||
const second = { id: 's2', abroad: false, country: '', city: 'Актау', cityKz: '', cityKato: '', project: 'Порт', projectId: 'p2', dateFrom: '2026-10-06', dateTo: '2026-10-09' };
|
const second = { id: 's2', abroad: false, country: '', city: 'Актау', cityKz: '', cityKato: '', project: 'Порт', projectId: 'p2', dateFrom: '2026-10-06', dateTo: '2026-10-09' };
|
||||||
const route = { ...ROUTE, stops: [...ROUTE.stops, second], legTransport: { 'origin>s1': 'Авиа', 's1>s2': 'Авиа', 's2>origin': 'Авиа' } };
|
const route = { ...ROUTE, stops: [...ROUTE.stops, second], legTransport: { 'origin>s1': 'Авиа', 's1>s2': 'Авиа', 's2>origin': 'Авиа' }, legDepartureTime: { 'origin>s1': 'Утро', 's1>s2': 'День', 's2>origin': 'Вечер' } };
|
||||||
expect(fields(submit({ ...FILLED, Route: route }))).toEqual(['stop-s2-from']);
|
expect(fields(submit({ ...FILLED, Route: route }))).toEqual(['stop-s2-from']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export const fieldKey = {
|
|||||||
stopFrom: (stop: IRouteStop): string => `stop-${stop.id}-from`,
|
stopFrom: (stop: IRouteStop): string => `stop-${stop.id}-from`,
|
||||||
stopTo: (stop: IRouteStop): string => `stop-${stop.id}-to`,
|
stopTo: (stop: IRouteStop): string => `stop-${stop.id}-to`,
|
||||||
legTransport: (legId: string): string => `leg-${legId}`,
|
legTransport: (legId: string): string => `leg-${legId}`,
|
||||||
|
legDepartureTime: (legId: string): string => `leg-${legId}-time`,
|
||||||
articleTitle: (article: IEstimateArticle): string => `article-${article.id}-title`,
|
articleTitle: (article: IEstimateArticle): string => `article-${article.id}-title`,
|
||||||
itemLink: (item: IEstimateItem): string => `item-${item.id}-link`,
|
itemLink: (item: IEstimateItem): string => `item-${item.id}-link`,
|
||||||
itemAmount: (item: IEstimateItem): string => `item-${item.id}-amount`,
|
itemAmount: (item: IEstimateItem): string => `item-${item.id}-amount`,
|
||||||
@@ -105,7 +106,7 @@ function tripLengthIssues(route: ITripRoute): IFieldIssue[] {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Маршрут для отправки: город отправления, все точки, вид транспорта на каждом участке. */
|
/** Маршрут для отправки: город отправления, все точки, вид транспорта и время выезда на каждом участке. */
|
||||||
function routeIssues(route: ITripRoute, today: string): IFieldIssue[] {
|
function routeIssues(route: ITripRoute, today: string): IFieldIssue[] {
|
||||||
const issues: IFieldIssue[] = [];
|
const issues: IFieldIssue[] = [];
|
||||||
if (!route.origin.trim()) issues.push({ field: fieldKey.origin, label: 'Маршрут', message: 'укажите город отправления' });
|
if (!route.origin.trim()) issues.push({ field: fieldKey.origin, label: 'Маршрут', message: 'укажите город отправления' });
|
||||||
@@ -113,8 +114,13 @@ function routeIssues(route: ITripRoute, today: string): IFieldIssue[] {
|
|||||||
issues.push(...stopDateOrderIssues(route));
|
issues.push(...stopDateOrderIssues(route));
|
||||||
issues.push(...tripLengthIssues(route));
|
issues.push(...tripLengthIssues(route));
|
||||||
routeLegs(route).forEach((leg) => {
|
routeLegs(route).forEach((leg) => {
|
||||||
|
const label = `Переезд ${legLabel(leg)}`;
|
||||||
if (!route.legTransport[leg.id]) {
|
if (!route.legTransport[leg.id]) {
|
||||||
issues.push({ field: fieldKey.legTransport(leg.id), label: `Участок ${legLabel(leg)}`, message: 'выберите вид транспорта' });
|
issues.push({ field: fieldKey.legTransport(leg.id), label, message: 'выберите вид транспорта' });
|
||||||
|
}
|
||||||
|
// Время выезда нужно тревел-координатору под билеты — без него не отправить.
|
||||||
|
if (!route.legDepartureTime[leg.id]) {
|
||||||
|
issues.push({ field: fieldKey.legDepartureTime(leg.id), label, message: 'выберите время выезда' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return issues;
|
return issues;
|
||||||
|
|||||||
Reference in New Issue
Block a user