Skip to content

Commit b04e466

Browse files
Merge pull request #42 from martinbagshaw/chore-convert-logbook-to-typescript
convert logbook list to typescript, organise data and types better
2 parents 802a7d2 + 6dd9fb8 commit b04e466

File tree

13 files changed

+241
-173
lines changed

13 files changed

+241
-173
lines changed

src/components/App.tsx

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import React, { useEffect, useState, FC } from "react";
22
import styled from "styled-components";
33

4-
import { allLogs, OutputObject } from "../utils/formatData";
4+
import { OutputObject } from "../utils/common-types";
5+
import { allLogs } from "../utils/processed-data";
56
import Stats from "./stats/Stats.jsx";
6-
import Logbook from "./logbook/Logbook.jsx";
7+
import Logbook from "./logbook/Logbook";
78

89
import { breakpoint, colors, fonts, fontSize } from "./common/styleVars";
910
import { buttonBase } from "./common/Buttons.jsx";
@@ -19,7 +20,7 @@ const Header = styled.header`
1920
display: flex;
2021
`;
2122

22-
const Button = styled.button<{ readonly isActive: boolean }>`
23+
const ViewButton = styled.button<{ readonly isActive: boolean }>`
2324
${buttonBase};
2425
display: flex;
2526
width: 50%;
@@ -33,12 +34,6 @@ const Button = styled.button<{ readonly isActive: boolean }>`
3334
&:first-child {
3435
justify-content: flex-end;
3536
}
36-
> span {
37-
user-select: none;
38-
max-width: 23rem;
39-
width: 100%;
40-
display: block;
41-
}
4237
&:hover {
4338
background-color: ${({ isActive }) => colors[isActive ? "lightRed" : "midGrey"]};
4439
}
@@ -50,6 +45,13 @@ const Button = styled.button<{ readonly isActive: boolean }>`
5045
`}
5146
`;
5247

48+
const ViewButtonText = styled.span`
49+
user-select: none;
50+
max-width: 23rem;
51+
width: 100%;
52+
display: block;
53+
`;
54+
5355
const DailyMessage = styled.p`
5456
text-align: center;
5557
padding: 0.25rem;
@@ -63,7 +65,7 @@ const ViewContainer = styled.div`
6365
overflow: hidden;
6466
`;
6567

66-
const Views = styled.div<{ readonly isLogbook: boolean }>`
68+
const ViewPanel = styled.div<{ readonly isLogbook: boolean }>`
6769
display: flex;
6870
width: 200%;
6971
${({ isLogbook }) =>
@@ -78,19 +80,28 @@ const Views = styled.div<{ readonly isLogbook: boolean }>`
7880
`}
7981
`;
8082

83+
interface Views {
84+
s: string;
85+
l: string;
86+
}
87+
const views: Views = {
88+
s: "Stats",
89+
l: "Logbook"
90+
};
91+
8192
interface Filter {
8293
day: string;
8394
month: string;
8495
year: string;
8596
}
8697
const App: FC = () => {
87-
const [view, setView] = useState<string>("Stats");
98+
const [activeView, setActiveView] = useState<string>(views.s);
8899
const [logs, setLogs] = useState<OutputObject[]>(allLogs);
89100
const [message, setMessage] = useState<string | null>(null);
90101

91102
const handleSingleDay = (logs: OutputObject[], filter: Filter) => {
92103
const { day, month, year } = filter;
93-
setView("Logbook");
104+
setActiveView(views.l);
94105
setLogs(logs);
95106
setMessage(
96107
`Showing ${logs.length} ${logs.length === 1 ? "log" : "logs"} for: ${day} ${month} ${year}`
@@ -99,37 +110,33 @@ const App: FC = () => {
99110

100111
// reset to original
101112
useEffect(() => {
102-
if (message && view === "Stats") {
113+
if (message && activeView === views.s) {
103114
setLogs(allLogs);
104115
setMessage(null);
105116
}
106-
}, [message, view]);
117+
}, [message, activeView]);
107118

108119
return (
109120
<Root>
110121
{message && <DailyMessage>{message}</DailyMessage>}
111122
<Header>
112-
<Button
113-
onClick={() => setView("Stats")}
114-
isActive={view === "Stats"}
115-
aria-label="Stats View"
116-
>
117-
<span>Stats</span>
118-
</Button>
119-
<Button
120-
onClick={() => setView("Logbook")}
121-
isActive={view === "Logbook"}
122-
aria-label="Logbook View"
123-
>
124-
<span>Logbook</span>
125-
</Button>
123+
{Object.values(views).map(view => (
124+
<ViewButton
125+
key={view}
126+
onClick={() => setActiveView(view)}
127+
isActive={activeView === view}
128+
aria-label={`${view} View`}
129+
>
130+
<ViewButtonText>{view}</ViewButtonText>
131+
</ViewButton>
132+
))}
126133
</Header>
127134

128135
<ViewContainer>
129-
<Views isLogbook={view === "Logbook"}>
136+
<ViewPanel isLogbook={activeView === views.l}>
130137
<Stats logs={logs} handleSingleDay={handleSingleDay} />
131138
<Logbook logs={logs} />
132-
</Views>
139+
</ViewPanel>
133140
</ViewContainer>
134141
</Root>
135142
);

src/components/logbook/Logbook.jsx renamed to src/components/logbook/Logbook.tsx

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,34 @@
1-
import React, { useState, Fragment } from "react";
1+
import React, { useState, Fragment, FC } from "react";
22
import styled from "styled-components";
33

4-
import Search from "./Search.jsx";
5-
import SearchReset from "./SearchReset.jsx";
6-
import PageNav from "./PageNav.jsx";
7-
import Results from "./Results.jsx";
4+
import { OutputObject, DefaultSearch } from "../../utils/common-types";
5+
6+
import Search from "./Search";
7+
import SearchReset from "./SearchReset";
8+
import PageNav from "./PageNav";
9+
import Results from "./Results";
810
import SingleLog from "../singleLog/SingleLog.jsx";
911

1012
const LogContainer = styled.div`
1113
width: 50%;
1214
`;
1315

14-
const defaultSearch = {
16+
const defaultSearch: DefaultSearch = {
1517
placeholder: "Search by Climb or Crag name...",
1618
searchTerm: "",
17-
results: [],
19+
results: undefined,
1820
};
1921

20-
const Logbook = ({ logs }) => {
21-
const [search, setSearch] = useState({ ...defaultSearch });
22-
const [page, setPage] = useState({ low: 0, high: 50 });
23-
const [singleLog, setSingleLog] = useState(null);
22+
interface Props {
23+
logs: OutputObject[];
24+
}
25+
const Logbook: FC<Props> = ({ logs }) => {
26+
const [search, setSearch] = useState<DefaultSearch | void>(defaultSearch);
27+
const [page, setPage] = useState<{low: number, high: number}>({ low: 0, high: 50 });
28+
const [singleLog, setSingleLog] = useState<OutputObject | undefined>(undefined);
2429

25-
const handleSearch = value => {
26-
const resultsFind = (value, logs) => {
30+
const handleSearch = (value: string): DefaultSearch | void => {
31+
const findResults = (value: string, logs: OutputObject[]): OutputObject[] | void => {
2732
if (value.length < 3) return;
2833
return logs.filter(log => {
2934
const regex = new RegExp(value, "gi");
@@ -32,11 +37,11 @@ const Logbook = ({ logs }) => {
3237
return cl.match(regex) || cr.match(regex);
3338
});
3439
};
35-
const placeholder = value.length > 0 ? "" : "Search by Climb or Crag name...";
36-
return setSearch({ placeholder, searchTerm: value, results: resultsFind(value, logs) });
40+
const placeholder: string = value.length > 0 ? "" : "Search by Climb or Crag name...";
41+
return setSearch({ placeholder, searchTerm: value, results: findResults(value, logs) });
3742
};
3843

39-
const handlePageChange = direction => {
44+
const handlePageChange = (direction: string) => {
4045
let { low: newLow, high: newHigh } = page;
4146
if (direction === "older") {
4247
setPage({ low: (newLow += 50), high: (newHigh += 50) });
@@ -46,7 +51,8 @@ const Logbook = ({ logs }) => {
4651
}
4752
};
4853

49-
const handleSingleView = index => {
54+
// TODO: stricter checking here. Should be ascent-<number>, see OutputObject
55+
const handleSingleView = (index: string): void => {
5056
return setSingleLog(logs.find(i => i.key === index));
5157
};
5258

@@ -56,9 +62,12 @@ const Logbook = ({ logs }) => {
5662
{!singleLog && (
5763
<Fragment>
5864
<Search
59-
{...search}
60-
handleSearch={e => handleSearch(e.target.value)}
65+
handleSearch={handleSearch}
6166
handleSingleView={handleSingleView}
67+
{...search}
68+
// placeholder={search.placeholder}
69+
// results={search.results}
70+
// searchTerm={search.searchTerm}
6271
/>
6372
<PageNav {...page} logs={logs} handlePageChange={handlePageChange} />
6473
<Results {...page} logs={logs} handleSingleView={handleSingleView} />
@@ -68,7 +77,7 @@ const Logbook = ({ logs }) => {
6877
</div>
6978
<SearchReset
7079
onClose={() => {
71-
setSearch({ ...defaultSearch });
80+
setSearch(defaultSearch);
7281
}}
7382
/>
7483
</LogContainer>

src/components/logbook/PageNav.jsx renamed to src/components/logbook/PageNav.tsx

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import React from "react";
1+
import React, { FC } from "react";
22
import styled, { css } from "styled-components";
33

4+
import { OutputObject } from "../../utils/common-types";
5+
46
import useIsWidth from "../common/useIsWidth.jsx";
57
import Chevron from "../common/icons/Chevron.jsx";
68
import { buttonBase } from "../common/Buttons.jsx";
@@ -45,6 +47,10 @@ const High = styled(Low)`
4547
background-color: ${colors.lightRed};
4648
`;
4749

50+
type ButtonOptions = "left" | "right"
51+
type ButtonCss = {
52+
[key in ButtonOptions]: string;
53+
}
4854
const buttonCss = {
4955
left: css`
5056
padding-right: 1rem;
@@ -55,7 +61,7 @@ const buttonCss = {
5561
`,
5662
};
5763

58-
const Button = styled.button`
64+
const Button = styled.button<{ readonly direction: keyof ButtonCss }>`
5965
${buttonBase};
6066
line-height: 1;
6167
display: flex;
@@ -81,9 +87,19 @@ const Button = styled.button`
8187
}
8288
`;
8389

84-
const PageNav = ({ logs, low, high, handlePageChange }) => {
90+
interface Buttons {
91+
[key: string]: { condition: boolean, direction: ButtonOptions }
92+
}
93+
94+
interface Props {
95+
logs: OutputObject[];
96+
low: number;
97+
high: number;
98+
handlePageChange: (direction: string) => void;
99+
}
100+
const PageNav: FC<Props> = ({ logs, low, high, handlePageChange }) => {
85101
const { isWidth: isDesktop } = useIsWidth("large");
86-
const buttons = {
102+
const buttons: Buttons = {
87103
older: {
88104
condition: high < logs.length,
89105
direction: "left",
@@ -102,7 +118,7 @@ const PageNav = ({ logs, low, high, handlePageChange }) => {
102118
<High>{`${logs.length - low}`}</High> {`of ${logs.length} logs.`}
103119
</Pagination>
104120
)}
105-
{Object.keys(buttons).map(i => {
121+
{Object.keys(buttons).map((i) => {
106122
const { condition, direction } = buttons[i];
107123
return (
108124
condition && (

src/components/logbook/Results.jsx renamed to src/components/logbook/Results.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
import React from "react";
1+
import React, { FC } from "react";
22
import styled from "styled-components";
33

4+
import { OutputObject } from "../../utils/common-types";
5+
46
import useIsWidth from "../common/useIsWidth.jsx";
57
import Chevron from "../common/icons/Chevron.jsx";
68
import { breakpoint, colors } from "../common/styleVars";
79
import { searchResultText } from "../common/Layout.jsx";
810
import { buttonBase } from "../common/Buttons.jsx";
9-
10-
import { getDate } from "../../utils/getDate";
11+
import { getDate } from "../../utils/get-date";
1112

1213
const ResultsList = styled.ul`
1314
margin: 0 0 3rem;
@@ -82,9 +83,15 @@ const Date = styled.span`
8283
align-items: center;
8384
`;
8485

85-
const Results = ({ logs, low, high, handleSingleView }) => {
86+
interface Props {
87+
logs: OutputObject[];
88+
low: number;
89+
high: number;
90+
handleSingleView: (index: string) => void;
91+
}
92+
const Results: FC<Props> = ({ logs, low, high, handleSingleView }) => {
8693
const { isWidth: isDesktop } = useIsWidth("large");
87-
94+
// TODO: run getDate in processed-data.ts
8895
return (
8996
<ResultsList>
9097
{logs

0 commit comments

Comments
 (0)