Wrap Schedule page in PageLayout with tabs and Angular-matching styles
Schedule start page now uses PageLayout with PageTabs (schedule tab active), filter form in content-left column, and the info section with titles-container and PopularRequests in the main content area. SCSS matches Angular start.scss.
This commit is contained in:
@@ -1,47 +1,147 @@
|
||||
@use "../../../styles/variables" as vars;
|
||||
@use "../../../styles/fonts" as fonts;
|
||||
@use "../../../styles/colors" as colors;
|
||||
@use "../../../styles/screen" as screen;
|
||||
@use "../../../styles/shadows" as shadows;
|
||||
|
||||
.schedule-start {
|
||||
&__title {
|
||||
font-size: 22px;
|
||||
margin: 0 0 vars.$space-xl;
|
||||
}
|
||||
.schedule-start-page {
|
||||
section.frame {
|
||||
padding: 0;
|
||||
|
||||
&__form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: vars.$space-m;
|
||||
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
h2 {
|
||||
padding: 50px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
@include screen.mobile {
|
||||
.card {
|
||||
padding: vars.$space-xl vars.$space-xl 0;
|
||||
.titles-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
padding: 0 50px;
|
||||
padding-bottom: 50px;
|
||||
|
||||
.title {
|
||||
width: 50%;
|
||||
padding: 30px;
|
||||
padding-right: 50px;
|
||||
padding-left: 65px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: left center;
|
||||
|
||||
a {
|
||||
cursor: default;
|
||||
font-size: fonts.$font-size-xl;
|
||||
}
|
||||
|
||||
div {
|
||||
color: colors.$gray;
|
||||
padding-top: vars.$space-s;
|
||||
}
|
||||
|
||||
&.title1 {
|
||||
background-image: url('/assets/img/schedule-title-icon-1.svg');
|
||||
}
|
||||
|
||||
&.title2 {
|
||||
background-image: url('/assets/img/schedule-title-icon-2.svg');
|
||||
}
|
||||
|
||||
&.title3 {
|
||||
background-image: url('/assets/img/schedule-title-icon-3.svg');
|
||||
}
|
||||
|
||||
&.title4 {
|
||||
background-image: url('/assets/img/schedule-title-icon-4.svg');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__field {
|
||||
@media (max-width: vars.$media-breakpoint-mobile) {
|
||||
section.frame h2 {
|
||||
padding: 20px;
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
padding-bottom: 0px;
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
.titles-container {
|
||||
padding: 20px !important;
|
||||
padding-top: 0 !important;
|
||||
|
||||
div.title {
|
||||
width: 100% !important;
|
||||
padding: 20px !important;
|
||||
padding-left: 50px !important;
|
||||
background-size: 35px auto !important;
|
||||
|
||||
a {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.page-title {
|
||||
width: auto;
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
white-space: normal !important;
|
||||
}
|
||||
|
||||
h1.text--white {
|
||||
@include fonts.font-overflow();
|
||||
|
||||
@include screen.smTablet {
|
||||
font-size: fonts.$font-size-xxxl--tablet;
|
||||
margin-bottom: vars.$space-m + vars.$space-s;
|
||||
overflow: visible;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
@include screen.mobile {
|
||||
font-size: fonts.$font-size-xxxl--mobile;
|
||||
margin-bottom: vars.$space-m + vars.$space-s;
|
||||
margin-top: vars.$space-m;
|
||||
overflow: visible;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule filter panel in content-left column
|
||||
.schedule-start__form {
|
||||
@include shadows.box-shadow-small;
|
||||
background-color: colors.$white;
|
||||
border-radius: vars.$border-radius;
|
||||
padding: vars.$space-xl;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: vars.$space-m;
|
||||
}
|
||||
|
||||
.schedule-start__field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: vars.$space-s;
|
||||
|
||||
label {
|
||||
font-weight: 500;
|
||||
@include fonts.font-small(colors.$gray);
|
||||
margin-bottom: vars.$label-margin-bottom;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="date"] {
|
||||
@include shadows.control-border-shadow();
|
||||
height: vars.$standard-button-height;
|
||||
padding: 0 vars.$space-l;
|
||||
border: 1px solid colors.$border-input;
|
||||
border-radius: vars.$border-radius;
|
||||
font-size: 16px;
|
||||
transition: border-color 0.2s;
|
||||
font-size: fonts.$font-size-l;
|
||||
font-weight: fonts.$font-regular;
|
||||
color: colors.$text-color;
|
||||
width: 100%;
|
||||
transition-duration: 0.2s;
|
||||
@include fonts.font-overflow();
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
@@ -51,17 +151,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__submit {
|
||||
.schedule-start__submit {
|
||||
margin-top: vars.$space-xl;
|
||||
width: 100%;
|
||||
height: vars.$standard-button-height;
|
||||
background-color: colors.$blue-light;
|
||||
background-color: colors.$blue;
|
||||
color: colors.$white;
|
||||
border: none;
|
||||
border-radius: vars.$border-radius;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition-duration: 0.2s;
|
||||
font-weight: fonts.$font-bold;
|
||||
font-size: fonts.$font-size-m;
|
||||
|
||||
&:hover {
|
||||
background-color: colors.$blue-light--hover;
|
||||
background-color: colors.$blue--hover;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
@@ -69,39 +173,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.schedule-home-page-header {
|
||||
display: flex;
|
||||
margin: vars.$space-xl;
|
||||
|
||||
@include screen.mobile {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.schedule-home-page-header-border {
|
||||
border-bottom: 1px solid colors.$border;
|
||||
|
||||
@include screen.mobile {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.schedule-controls {
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@include screen.mobile {
|
||||
.card {
|
||||
padding: vars.$space-xl vars.$space-xl 0;
|
||||
}
|
||||
|
||||
&__tabs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
|
||||
import { type FC, useState, useCallback, type FormEvent } from "react";
|
||||
import { useNavigate, useParams } from "@modern-js/runtime/router";
|
||||
import { useTranslation } from "@/i18n/provider.js";
|
||||
import { PageLayout } from "@/ui/layout/PageLayout.js";
|
||||
import { PageTabs } from "@/ui/layout/PageTabs.js";
|
||||
import { PopularRequestsPanel } from "@/features/popular-requests/components/PopularRequestsPanel.js";
|
||||
import type { PopularRequest } from "@/features/popular-requests/types.js";
|
||||
import { buildScheduleUrl } from "../url.js";
|
||||
import "./ScheduleStartPage.scss";
|
||||
|
||||
@@ -43,6 +48,7 @@ function addDaysToDateInput(value: string, days: number): string {
|
||||
|
||||
export const ScheduleStartPage: FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
const routeParams = useParams<{ lang: string }>();
|
||||
const lang = routeParams.lang ?? "ru";
|
||||
|
||||
@@ -92,109 +98,161 @@ export const ScheduleStartPage: FC = () => {
|
||||
[departureAirport, arrivalAirport, dateFrom, dateTo, isRoundTrip, returnDateFrom, returnDateTo, navigate, lang],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="schedule-start" data-testid="schedule-start">
|
||||
<h1 className="schedule-start__title">Flight Schedule</h1>
|
||||
const handlePopularRequestClick = useCallback((_request: PopularRequest) => {
|
||||
// Navigation handled by PopularRequestItem internally
|
||||
}, []);
|
||||
|
||||
<form
|
||||
className="schedule-start__form"
|
||||
data-testid="schedule-search-form"
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<div className="schedule-start__field">
|
||||
<label htmlFor="schedule-departure">Departure</label>
|
||||
const scheduleFilter = (
|
||||
<form
|
||||
className="schedule-start__form"
|
||||
data-testid="schedule-search-form"
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<div className="schedule-start__field">
|
||||
<label htmlFor="schedule-departure">Departure</label>
|
||||
<input
|
||||
id="schedule-departure"
|
||||
type="text"
|
||||
placeholder="e.g. SVO"
|
||||
maxLength={3}
|
||||
value={departureAirport}
|
||||
onChange={(e) => setDepartureAirport(e.target.value)}
|
||||
data-testid="departure-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="schedule-start__field">
|
||||
<label htmlFor="schedule-arrival">Arrival</label>
|
||||
<input
|
||||
id="schedule-arrival"
|
||||
type="text"
|
||||
placeholder="e.g. LED"
|
||||
maxLength={3}
|
||||
value={arrivalAirport}
|
||||
onChange={(e) => setArrivalAirport(e.target.value)}
|
||||
data-testid="arrival-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="schedule-start__field">
|
||||
<label htmlFor="schedule-date-from">Date from</label>
|
||||
<input
|
||||
id="schedule-date-from"
|
||||
type="date"
|
||||
value={dateFrom}
|
||||
onChange={(e) => setDateFrom(e.target.value)}
|
||||
data-testid="date-from-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="schedule-start__field">
|
||||
<label htmlFor="schedule-date-to">Date to</label>
|
||||
<input
|
||||
id="schedule-date-to"
|
||||
type="date"
|
||||
value={dateTo}
|
||||
onChange={(e) => setDateTo(e.target.value)}
|
||||
data-testid="date-to-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="schedule-start__field">
|
||||
<label>
|
||||
<input
|
||||
id="schedule-departure"
|
||||
type="text"
|
||||
placeholder="e.g. SVO"
|
||||
maxLength={3}
|
||||
value={departureAirport}
|
||||
onChange={(e) => setDepartureAirport(e.target.value)}
|
||||
data-testid="departure-input"
|
||||
type="checkbox"
|
||||
checked={isRoundTrip}
|
||||
onChange={(e) => setIsRoundTrip(e.target.checked)}
|
||||
data-testid="round-trip-toggle"
|
||||
/>
|
||||
</div>
|
||||
Round trip
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="schedule-start__field">
|
||||
<label htmlFor="schedule-arrival">Arrival</label>
|
||||
<input
|
||||
id="schedule-arrival"
|
||||
type="text"
|
||||
placeholder="e.g. LED"
|
||||
maxLength={3}
|
||||
value={arrivalAirport}
|
||||
onChange={(e) => setArrivalAirport(e.target.value)}
|
||||
data-testid="arrival-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="schedule-start__field">
|
||||
<label htmlFor="schedule-date-from">Date from</label>
|
||||
<input
|
||||
id="schedule-date-from"
|
||||
type="date"
|
||||
value={dateFrom}
|
||||
onChange={(e) => setDateFrom(e.target.value)}
|
||||
data-testid="date-from-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="schedule-start__field">
|
||||
<label htmlFor="schedule-date-to">Date to</label>
|
||||
<input
|
||||
id="schedule-date-to"
|
||||
type="date"
|
||||
value={dateTo}
|
||||
onChange={(e) => setDateTo(e.target.value)}
|
||||
data-testid="date-to-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="schedule-start__field">
|
||||
<label>
|
||||
{isRoundTrip && (
|
||||
<>
|
||||
<div className="schedule-start__field">
|
||||
<label htmlFor="schedule-return-date-from">Return date from</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isRoundTrip}
|
||||
onChange={(e) => setIsRoundTrip(e.target.checked)}
|
||||
data-testid="round-trip-toggle"
|
||||
id="schedule-return-date-from"
|
||||
type="date"
|
||||
value={returnDateFrom}
|
||||
onChange={(e) => setReturnDateFrom(e.target.value)}
|
||||
data-testid="return-date-from-input"
|
||||
/>
|
||||
Round trip
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isRoundTrip && (
|
||||
<>
|
||||
<div className="schedule-start__field">
|
||||
<label htmlFor="schedule-return-date-from">Return date from</label>
|
||||
<input
|
||||
id="schedule-return-date-from"
|
||||
type="date"
|
||||
value={returnDateFrom}
|
||||
onChange={(e) => setReturnDateFrom(e.target.value)}
|
||||
data-testid="return-date-from-input"
|
||||
/>
|
||||
<div className="schedule-start__field">
|
||||
<label htmlFor="schedule-return-date-to">Return date to</label>
|
||||
<input
|
||||
id="schedule-return-date-to"
|
||||
type="date"
|
||||
value={returnDateTo}
|
||||
onChange={(e) => setReturnDateTo(e.target.value)}
|
||||
data-testid="return-date-to-input"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="schedule-start__submit"
|
||||
data-testid="schedule-search-submit"
|
||||
>
|
||||
Search
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="schedule-start-page" data-testid="schedule-start">
|
||||
<PageLayout
|
||||
headerLeft={
|
||||
<PageTabs viewType="schedule" />
|
||||
}
|
||||
title={
|
||||
<h1 className="text--white page-title">
|
||||
{t("SCHEDULE.TITLE")}
|
||||
</h1>
|
||||
}
|
||||
contentLeft={scheduleFilter}
|
||||
>
|
||||
<section className="frame">
|
||||
<h2>{t("SCHEDULE.SCHEDULE-START")}</h2>
|
||||
|
||||
<div className="titles-container">
|
||||
<div className="title title1">
|
||||
<a>{t("SCHEDULE.SCHEDULE-START-TITLE1")}</a>
|
||||
<div>
|
||||
{t("SCHEDULE.SCHEDULE-START-TITLE1-DESCRIPTION")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="schedule-start__field">
|
||||
<label htmlFor="schedule-return-date-to">Return date to</label>
|
||||
<input
|
||||
id="schedule-return-date-to"
|
||||
type="date"
|
||||
value={returnDateTo}
|
||||
onChange={(e) => setReturnDateTo(e.target.value)}
|
||||
data-testid="return-date-to-input"
|
||||
/>
|
||||
<div className="title title2">
|
||||
<a>{t("SCHEDULE.SCHEDULE-START-TITLE2")}</a>
|
||||
<div>
|
||||
{t("SCHEDULE.SCHEDULE-START-TITLE2-DESCRIPTION")}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="schedule-start__submit"
|
||||
data-testid="schedule-search-submit"
|
||||
>
|
||||
Search
|
||||
</button>
|
||||
</form>
|
||||
<div className="title title3">
|
||||
<a>{t("SCHEDULE.SCHEDULE-START-TITLE3")}</a>
|
||||
<div>
|
||||
{t("SCHEDULE.SCHEDULE-START-TITLE3-DESCRIPTION")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="title title4">
|
||||
<a>{t("SCHEDULE.SCHEDULE-START-TITLE4")}</a>
|
||||
<div>
|
||||
{t("SCHEDULE.SCHEDULE-START-TITLE4-DESCRIPTION")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PopularRequestsPanel onRequestClick={handlePopularRequestClick} />
|
||||
</section>
|
||||
</PageLayout>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user