Skip to content

Commit

Permalink
Fetch office listing with search and display the results
Browse files Browse the repository at this point in the history
  • Loading branch information
victorchabbert authored and PierreMartinFaberNovel committed Jan 13, 2022
1 parent 4cb48ea commit b741015
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 2 deletions.
35 changes: 33 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
import React, { FC, useState } from "react";
import React, { FC, useState, useEffect } from "react";
import "./styles/global.scss";
import styles from "./app.module.scss";
import Header from "./templates/header";
import { Office } from "./models/offices";
import { OfficeSearch } from "./models/offices";
import OfficeList from "./templates/office-list/OfficeList";
import { getStats } from "./services/offices";
import { getOffices } from "./services/offices";
import StatsBar from "./templates/stats-bar/StatsBar";
import { Stat } from "./models/stats";
import { usePrevious } from "./support/hooks/usePrevious";

type Sort = "asc" | "desc";

const App: FC = () => {
const [sort, setSort] = useState<Sort>("asc");
const [offices, setOffices] = useState<OfficeSearch | undefined>(undefined);
const [query, setQuery] = useState<any>(undefined);
const [stats, setStats] = useState<Stat | undefined>(undefined);

const handleSortToggle = () => {
setSort(sort === "asc" ? "desc" : "asc");
Expand All @@ -21,13 +31,34 @@ const App: FC = () => {
}
};

const prevQuery = usePrevious(query);

const handleOfficeSearch = (query: string) => {
getOffices(query).then((offices) => setOffices(() => offices));
};

useEffect(() => {
getOffices().then((offices) => setOffices(() => offices));
getStats().then((stats) => setStats(stats));

if (prevQuery !== query && query !== undefined) {
handleOfficeSearch(query);
}
}, [query, prevQuery]);

return (
<div className={styles.app}>
<Header
sort={sort}
onSortToggle={handleSortToggle}
onSearchSubmit={console.log}
onSearchSubmit={(query) => setQuery(query)}
/>
{stats && <StatsBar stats={stats} />}
{offices ? (
<OfficeList offices={offices.data.sort(sortOffices)} />
) : (
false
)}
</div>
);
};
Expand Down
6 changes: 6 additions & 0 deletions src/models/stats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type Stat = {
office_count: number;
employee_avg: number;
continent_count: number;
spoken_languages: Array<string>;
};
51 changes: 51 additions & 0 deletions src/services/offices.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { fetch } from "../fake-fetch";
import { OfficeSearch } from "../models/offices";
import { Stat } from "../models/stats";

export function getOffices(query: string = ""): Promise<OfficeSearch> {
return fetch(
`http://fake.fabernovel.com/api/offices?query=${query}`
).then(r => r.json());
}

export function getStats(query?: string): Promise<Stat> {
const initialStat: Stat = {
office_count: 0,
employee_avg: 0,
continent_count: 0,
spoken_languages: []
};
const continents: Array<string> = [];
const languages: Array<Array<string>> = [];

return getOffices(query)
.then(offices => {
return [
offices,
offices.data.reduce(function(stats, office) {
continents.push(office.location.continent.label);
languages.push(office.location.language);

const stat: Stat = {
...stats,
employee_avg: stats.employee_avg + office.employees_count,
office_count: stats.office_count + 1
};
return stat;
}, initialStat)
] as [OfficeSearch, Stat];
})
.then(data => {
const [offices, stats] = data;
return {
...stats,
employee_avg: Math.round(stats.employee_avg / offices.data.length),
continent_count: continents.filter(
(value, i) => continents.indexOf(value) === i
).length,
spoken_languages: ([] as Array<string>)
.concat(...languages)
.filter((value, i, langs) => langs.indexOf(value) === i)
};
});
}
11 changes: 11 additions & 0 deletions src/support/hooks/usePrevious.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useEffect, useRef } from "react";

export function usePrevious(value: any) {
const ref = useRef();

useEffect(() => {
ref.current = value;
}, [value]);

return ref.current;
}
27 changes: 27 additions & 0 deletions src/templates/stats-bar/StatsBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from "react";
import { Stat } from "../../models/stats";
import styles from "./stats-bar.module.scss";

const StatsBar = ({ stats }: { stats: Stat }) => {
return (
<div className={styles.statBar}>
<StatItem label="offices" value={`${stats.office_count}`} />
<StatItem
label="avg employees per location"
value={`${stats.employee_avg}`}
/>
<StatItem label="continents" value={`${stats.continent_count}`} />
<StatItem label="languages" value={`${stats.spoken_languages.length}`} />
</div>
);
};

const StatItem = ({ label, value }: { label: string; value: string }) => (
<div>
<h1>
{value} <span>{label}</span>
</h1>
</div>
);

export default StatsBar;
51 changes: 51 additions & 0 deletions src/templates/stats-bar/stats-bar.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
.statBar {
max-width: 1280px;
margin: 0 auto;
padding: 0 16px 23px;

&::after {
content: "";
display: table;
clear: both;
}

div {
margin: 0;
float: left;
padding-right: 16px;
margin-right: 16px;
position: relative;

&:not(:last-of-type)::after {
content: "";
width: 1px;
position: absolute;
top: 50%;
bottom: 20%;
right: 1px;
background: #ccc;
}

&:nth-child(3n + 0) {
color: #E05A4F;
}

&:nth-child(3n + 1) {
color: #00BD9D;
}

&:nth-child(3n + 2) {
color: #008AFD;
}

h1 {
display: inline-block;
vertical-align: baseline;

span {
font-size: 12px;
color: #321000;
}
}
}
}

0 comments on commit b741015

Please sign in to comment.