Skip to content

Commit

Permalink
feat: add aircraft selection in homepage (#134)
Browse files Browse the repository at this point in the history
  • Loading branch information
jpedroh authored Apr 25, 2023
1 parent b32b579 commit abbf54c
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 45 deletions.
28 changes: 18 additions & 10 deletions packages/app/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import GeneralLayout from '../src/layouts/GeneralLayout';
import Lead from '../src/components/Lead';
import SearchFlightsForm from '../src/components/SearchFlightsForm';
import { fetchCompanies } from '../src/services/fetch-companies';
import { fetchAirports } from '../src/services/fetch-airports';
import GeneralLayout from "../src/layouts/GeneralLayout";
import Lead from "../src/components/Lead";
import SearchFlightsForm from "../src/components/SearchFlightsForm";
import { fetchCompanies } from "../src/services/fetch-companies";
import { fetchAircraftIcaoCodes } from "../src/services/fetch-aircraft-icao-codes";
import { fetchAirports } from "../src/services/fetch-airports";

export const revalidate = 3600;

export default async function Page() {
const companies = await fetchCompanies()
const airports = await fetchAirports();
const companies = await fetchCompanies();
const airports = await fetchAirports();
const aircraftIcaoCodes = await fetchAircraftIcaoCodes();

return <GeneralLayout>
<Lead>To begin, fill at least one of the following fields.</Lead>
<SearchFlightsForm airports={airports} companies={companies} />
return (
<GeneralLayout>
<Lead>To begin, fill at least one of the following fields.</Lead>
<SearchFlightsForm
airports={airports}
companies={companies}
aircraftIcaoCodes={aircraftIcaoCodes}
/>
</GeneralLayout>
);
}
125 changes: 90 additions & 35 deletions packages/app/src/components/SearchFlightsForm/index.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,55 @@
"use client"
"use client";

import { ChangeEventHandler, FC, useState } from 'react'
import { Airport } from '../../services/fetch-airports'
import { formatAirport } from '../../utils/format-airport'
import Button from '../Button'
import { SelectInput } from '../SelectInput'
import styles from './index.module.css'
import { ChangeEventHandler, FC, useState } from "react";
import { Airport } from "../../services/fetch-airports";
import { formatAirport } from "../../utils/format-airport";
import Button from "../Button";
import { SelectInput } from "../SelectInput";
import styles from "./index.module.css";

export type SearchFlightsFormFields = { departureIcao: string; arrivalIcao: string, company: string }
export type SearchFlightsFormFields = {
departureIcao: string;
arrivalIcao: string;
company: string;
aircraftIcaoCode: string;
};

type Props = {
companies: string[]
airports: Airport[]
}
companies: string[];
aircraftIcaoCodes: string[];
airports: Airport[];
};

const SearchFlightsForm: FC<Props> = ({ companies, airports }) => {
const SearchFlightsForm: FC<Props> = ({
companies,
aircraftIcaoCodes,
airports,
}) => {
const [form, setForm] = useState<SearchFlightsFormFields>({
arrivalIcao: '',
departureIcao: '',
company: ''
})
arrivalIcao: "",
departureIcao: "",
company: "",
aircraftIcaoCode: "",
});

const isSubmitDisabled = form.arrivalIcao.trim().length === 0 && form.departureIcao.trim().length === 0 && form.company.length === 0
const isSubmitDisabled =
form.arrivalIcao.trim().length === 0 &&
form.departureIcao.trim().length === 0 &&
form.company.length === 0 &&
form.aircraftIcaoCode.length === 0;

const onChange: ChangeEventHandler<HTMLInputElement | HTMLSelectElement> = evt => {
setForm(form => ({
const onChange: ChangeEventHandler<HTMLInputElement | HTMLSelectElement> = (
evt
) => {
setForm((form) => ({
...form,
[evt.target.name]: evt.target.value.toUpperCase()
}))
}
[evt.target.name]: evt.target.value.toUpperCase(),
}));
};

const airportsOptions = airports.map(airport => {
return { value: airport.AeroCode, label: formatAirport(airport) }
})
const airportsOptions = airports.map((airport) => {
return { value: airport.AeroCode, label: formatAirport(airport) };
});

return (
<form className={styles.container} action={"/search"}>
Expand All @@ -41,7 +58,9 @@ const SearchFlightsForm: FC<Props> = ({ companies, airports }) => {
<SelectInput
options={airportsOptions}
name="departureIcao"
onChange={departureIcao => setForm(form => ({ ...form, departureIcao }))}
onChange={(departureIcao) =>
setForm((form) => ({ ...form, departureIcao }))
}
/>
</div>

Expand All @@ -50,28 +69,64 @@ const SearchFlightsForm: FC<Props> = ({ companies, airports }) => {
<SelectInput
options={airportsOptions}
name="arrivalIcao"
onChange={arrivalIcao => setForm(form => ({ ...form, arrivalIcao }))}
onChange={(arrivalIcao) =>
setForm((form) => ({ ...form, arrivalIcao }))
}
/>
</div>

<div>
<label htmlFor="company">Company</label>
<select name="company" id="company" value={form.company} onChange={onChange}>
<option value="" disabled>Pick a company</option>
<select
name="company"
id="company"
value={form.company}
onChange={onChange}
>
<option value="" disabled>
Pick a company
</option>
{companies.map((company) => {
return <option key={company} value={company}>{company}</option>
return (
<option key={company} value={company}>
{company}
</option>
);
})}
</select>
</div>

<section className='flex gap-3 items-center'>
<div>
<label htmlFor="aircraftIcaoCode">Aircraft</label>
<select
name="aircraftIcaoCode"
id="aircraftIcaoCode"
value={form.aircraftIcaoCode}
onChange={onChange}
>
<option value="" disabled>
Pick an Aircraft
</option>
{aircraftIcaoCodes.map((aircraftIcaoCode) => {
return (
<option key={aircraftIcaoCode} value={aircraftIcaoCode}>
{aircraftIcaoCode}
</option>
);
})}
</select>
</div>

<section className="flex gap-3 items-center">
<input type="checkbox" name="onlyCurrent" id="onlyCurrent" />
<label htmlFor="onlyCurrent">Show only current flights.</label>
</section>

<Button type="submit" disabled={isSubmitDisabled}>Search flights</Button>
<Button type="submit" disabled={isSubmitDisabled}>
Search flights
</Button>
</form>
)
}
);
};

export default SearchFlightsForm
export default SearchFlightsForm;
11 changes: 11 additions & 0 deletions packages/app/src/services/fetch-aircraft-icao-codes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { FlightModel } from "@mach/database";
import { QueryTypes } from "sequelize";

export async function fetchAircraftIcaoCodes() {
const companies = await FlightModel.sequelize?.query(
`SELECT DISTINCT(aircraft#>>'{icaoCode}') AS "aircraftIcaoCode" FROM "flights" AS "flight";`,
{ type: QueryTypes.SELECT }
);

return companies?.map((v: any) => v.aircraftIcaoCode) ?? [];
}
7 changes: 7 additions & 0 deletions packages/app/src/services/fetch-flights.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ const schema = z.object({
.string()
.transform((v) => v.toUpperCase())
.optional(),
aircraftIcaoCode: z
.string()
.transform((v) => v.toUpperCase())
.optional(),
onlyCurrent: z
.string()
.transform((v) => Boolean(v))
Expand All @@ -31,6 +35,9 @@ export async function fetchFlights(searchParams: Record<string, unknown>) {
...(where.departureIcao && { departureIcao: where.departureIcao }),
...(where.arrivalIcao && { arrivalIcao: where.arrivalIcao }),
...(where.company && { company: where.company }),
...(where.aircraftIcaoCode && {
aircraft: { icaoCode: where.aircraftIcaoCode },
}),
...(where.onlyCurrent && {
beginDate: {
[Op.lte]: today,
Expand Down

1 comment on commit abbf54c

@vercel
Copy link

@vercel vercel bot commented on abbf54c Apr 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

mach – ./

mach-five.vercel.app
mach-git-master-jpedroh.vercel.app
mach-jpedroh.vercel.app
mach.jpedroh.dev

Please sign in to comment.