# Back Button + Flight Schedule Timeline (B.6)

## Goal

Add two Angular-parity elements to the React Online Board details page: a back-navigation button in the header (replacing `PageTabs`), and a flight-schedule timeline showing scheduled times, flight duration, and a days-of-operation indicator with the week's valid-through date range.

## Scope

Sub-feature **B.6** of the Flight Details parity work. Online Board details page only.

## Current State

- React details page uses `<PageTabs viewType="onlineboard" />` in `PageLayout.headerLeft`.
- No flight-schedule timeline component exists.
- `IFlightLeg` type has no `daysOfWeek` field.

## Approach

Two small independent component families:

1. **`DetailsBackButton`** — stateless `<Link>`-based button styled as the Angular back button. Replaces `PageTabs` in the details page's `headerLeft` slot.
2. **`FlightSchedule`** — single-tab PrimeReact `Accordion` with the Schedule section (times + duration) inside, plus a persistent Days-of-week strip + date-range note below.

## Types Extension

`src/features/online-board/types.ts` gets:

```typescript
export interface IDaysOfWeek {
  current: string;   // 7-char bit string for today (e.g., "1000010")
  flight: string;    // 7-char bit string for scheduled days (e.g., "1111111")
}
```

And `IFlightLeg` gains:

```typescript
export interface IFlightLeg {
  // ... existing fields ...
  daysOfWeek?: IDaysOfWeek;
}
```

Position 0 = Monday, position 6 = Sunday. Character `"1"` means active; any other character means inactive.

## Architecture

### `DetailsBackButton`

File: `src/features/online-board/components/DetailsBackButton/DetailsBackButton.tsx`.

```typescript
export interface DetailsBackButtonProps {
  locale: string;
}
```

Renders:

```tsx
<Link
  to={`/${locale}/onlineboard`}
  className="details-back-button"
  data-testid="details-back-button"
>
  <span className="details-back-button__arrow" aria-hidden="true">←</span>
  <span>{t("SHARED.BACK-BOARD")}</span>
</Link>
```

Always visible. Uses React Router `<Link>` (client-side nav). No conditional rendering.

### `FlightSchedule`

File: `src/features/online-board/components/FlightSchedule/FlightSchedule.tsx`.

```typescript
export interface FlightScheduleProps {
  flight: ISimpleFlight;
}
```

Renders `null` when `firstLeg.daysOfWeek?.flight` is undefined or empty string.

Otherwise renders:

1. **PrimeReact `<Accordion multiple={false} activeIndex={0}>`** with a single `<AccordionTab header={t("SHARED.SCHEDULE-FLIGHT")}>` containing:
   - Row: label `SHARED.DEPARTURE-SCHEDULED` + value `firstLeg.departure.times.scheduledDeparture.local`
   - Row: label `SHARED.ARRIVAL-SCHEDULED` + value `lastLeg.arrival.times.scheduledArrival.local`
   - Row: label `SHARED.PATH-TIME` + value `flight.flyingTime`
2. **Below the accordion** (always visible):
   - Section title: `SHARED.DAYS-EXECUTE-FLIGHT`
   - `<DaysOfWeekStrip flightBitString={firstLeg.daysOfWeek.flight} />`
   - Note: `t("SHARED.NOTE-TIME-SCHEDULE").replace("{START_DATE}", start).replace("{END_DATE}", end)`

Where `start` / `end` come from `getWeekDateRange(firstLeg.departure.times.scheduledDeparture.local)`.

Default `activeIndex={0}` opens the tab on mount (matches Angular's `selected = true`).

### `DaysOfWeekStrip`

File: `src/features/online-board/components/FlightSchedule/DaysOfWeekStrip.tsx`.

```typescript
export interface DaysOfWeekStripProps {
  flightBitString: string;
}
```

Renders 7 spans in a flex row. For index `i = 0..6`:

- Active if `flightBitString[i] === "1"`
- Label: `t("DAYS." + (i + 1))` (keys `DAYS.1` through `DAYS.7`)
- Class: `day` (active) or `day day--inactive` (inactive)

### `weekDateRange` helper

File: `src/features/online-board/components/FlightSchedule/weekDateRange.ts`.

```typescript
import { parseISO, startOfISOWeek, endOfISOWeek, format, isValid } from "date-fns";

export function getWeekDateRange(scheduledLocal: string | undefined): {
  start: string;
  end: string;
} {
  if (!scheduledLocal) return { start: "", end: "" };
  const d = parseISO(scheduledLocal);
  if (!isValid(d)) return { start: "", end: "" };
  return {
    start: format(startOfISOWeek(d), "dd.MM.yyyy"),
    end: format(endOfISOWeek(d), "dd.MM.yyyy"),
  };
}
```

Pure function — no React, no side effects. ISO week starts on Monday, ends on Sunday.

### Integration

In `src/features/online-board/components/OnlineBoardDetailsPage.tsx`:

1. Replace `headerLeft={<PageTabs viewType="onlineboard" />}` with `headerLeft={<DetailsBackButton locale={locale} />}` in the main happy-path return.
   - Also replace in the loading/error/not-found branches' `commonLayoutProps` for consistency.
2. After `<FlightLegs legs={legs} />` (or after the flying-time div), add `<FlightSchedule flight={displayFlight} />`.

## Styling

### `DetailsBackButton.scss`

```scss
.details-back-button {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  background: #e3f0ff;
  color: #1a3a5c;
  border-radius: 6px;
  text-decoration: none;
  font-size: 14px;
  font-family: inherit;
  cursor: pointer;

  &:hover { background: #c7dff5; }

  &__arrow {
    display: inline-block;
    font-size: 16px;
  }
}
```

### `FlightSchedule.scss`

```scss
.flight-schedule {
  background: #fff;
  border-radius: 8px;
  padding: 16px 24px;
  margin-top: 16px;

  .p-accordion-content {
    padding: 12px 0;
  }

  &__row {
    display: flex;
    justify-content: space-between;
    padding: 4px 0;
  }

  &__label { color: #666; }
  &__value { font-weight: 500; }

  &__days-section {
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid #eee;
  }

  &__section-title {
    font-size: 12px;
    color: #666;
    text-transform: uppercase;
    margin-bottom: 8px;
  }

  &__note {
    margin-top: 12px;
    font-size: 12px;
    color: #666;
    font-style: italic;
  }
}

.days-of-week-strip {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;

  .day {
    padding: 8px 12px;
    border-radius: 6px;
    background: #e6f1fb;
    font-size: 14px;
    font-weight: 500;
    color: #1a3a5c;

    &--inactive {
      background: #f6f6f6;
      color: rgba(102, 102, 102, 0.5);
    }
  }
}
```

## Testing

### `DetailsBackButton.test.tsx`
- Renders link with `href="/ru/onlineboard"` for `locale="ru"`
- Renders link label from `SHARED.BACK-BOARD` key
- Has `data-testid="details-back-button"`
- Renders arrow icon

### `DaysOfWeekStrip.test.tsx`
- Renders 7 day boxes
- First 3 active for `"1110000"`, last 4 have `--inactive` modifier
- All active for `"1111111"`
- All inactive for `"0000000"`
- Renders DAYS.1 through DAYS.7 labels in order

### `weekDateRange.test.ts`
- Returns Monday/Sunday formatted `dd.MM.yyyy` for a mid-week date
- Handles a Sunday input (returns that week's Mon-Sun)
- Handles a Monday input (returns same day as start)
- Returns `{ start: "", end: "" }` for invalid input
- Returns `{ start: "", end: "" }` for undefined input

### `FlightSchedule.test.tsx`
- Returns null when firstLeg has no `daysOfWeek`
- Returns null when `daysOfWeek.flight` is empty string
- Renders scheduled times + duration when `daysOfWeek` is present
- Renders DaysOfWeekStrip with `daysOfWeek.flight`
- Substitutes `{START_DATE}` and `{END_DATE}` into the note

### `OnlineBoardDetailsPage.test.tsx` (update existing)
- `headerLeft` renders `DetailsBackButton` (add `data-testid` assertion)
- `FlightSchedule` rendered below legs when `daysOfWeek` is present
- `FlightSchedule` not rendered when `daysOfWeek` is absent

## i18n

All required keys already present in React locale files. Confirmed via grep:

- `DAYS.1` through `DAYS.7`
- `SHARED.SCHEDULE-FLIGHT`, `SHARED.DAYS-EXECUTE-FLIGHT`, `SHARED.NOTE-TIME-SCHEDULE`
- `SHARED.PATH-TIME`, `SHARED.DEPARTURE-SCHEDULED`, `SHARED.ARRIVAL-SCHEDULED`
- `SHARED.BACK-BOARD`

No new keys needed.

## Out of Scope

- Schedule feature's back button (would navigate to `/schedule` — different viewType)
- Schedule feature's flight-schedule (only needed on Online Board details per Angular's `!flight.isMultiLeg` gate in Schedule variant)
- Custom accordion primitive extraction (we use PrimeReact directly)
- Changes to existing breadcrumbs (back button is additional navigation, not replacing breadcrumbs)

## Files Touched

### New

- `src/features/online-board/components/DetailsBackButton/DetailsBackButton.tsx`
- `src/features/online-board/components/DetailsBackButton/DetailsBackButton.scss`
- `src/features/online-board/components/DetailsBackButton/DetailsBackButton.test.tsx`
- `src/features/online-board/components/DetailsBackButton/index.ts`
- `src/features/online-board/components/FlightSchedule/FlightSchedule.tsx`
- `src/features/online-board/components/FlightSchedule/FlightSchedule.scss`
- `src/features/online-board/components/FlightSchedule/FlightSchedule.test.tsx`
- `src/features/online-board/components/FlightSchedule/DaysOfWeekStrip.tsx`
- `src/features/online-board/components/FlightSchedule/DaysOfWeekStrip.test.tsx`
- `src/features/online-board/components/FlightSchedule/weekDateRange.ts`
- `src/features/online-board/components/FlightSchedule/weekDateRange.test.ts`
- `src/features/online-board/components/FlightSchedule/index.ts`

### Modified

- `src/features/online-board/types.ts` — add `IDaysOfWeek`, extend `IFlightLeg` with optional `daysOfWeek`
- `src/features/online-board/types.test.ts` — test new types
- `src/features/online-board/components/OnlineBoardDetailsPage.tsx` — swap PageTabs for DetailsBackButton in 4 PageLayout call sites; add `<FlightSchedule>` below legs
- `src/features/online-board/components/OnlineBoardDetailsPage.test.tsx` — update mocks and add integration tests
