nils althaus

This commit is contained in:
nora 2021-09-27 11:07:24 +02:00
parent 2e5473f2be
commit 223f11750d
9 changed files with 207 additions and 3293 deletions

View file

@ -1,5 +1,5 @@
import axiosInstance from "./AxiosInstance";
import { Band } from "./Types";
import { Band, Response } from "./Types";
export class ApiClient {
private readonly _cache: { [route: string]: any };
@ -8,12 +8,12 @@ export class ApiClient {
this._cache = {};
}
public async get<T>(route: string, force = false): Promise<T> {
public async get<T, Name extends string>(route: string, force = false): Promise<Response<T, Name>> {
if (!force && this._cache[route]) {
return this._cache[route];
}
const res = await axiosInstance.get(route);
const data = res.data.response;
const data: Response<T, Name> = res.data.response;
if (res.status === 200) {
this._cache[route] = data;
}
@ -21,7 +21,7 @@ export class ApiClient {
}
public async searchBand(name: string): Promise<Band[]> {
const res = await this.get<any>(`/bands?query=${encodeURIComponent(name)}`);
const res = await this.get<Band[], "bands">(`/bands?query=${encodeURIComponent(name)}`);
return res.bands;
}
}

View file

@ -1,19 +1,33 @@
import React, { useRef, useState } from "react";
import { Button, Card, Container, FormControl, FormGroup, FormLabel, Row } from "react-bootstrap";
import React, { useEffect, useRef, useState } from "react";
import { Button, Card, Container, FormControl, FormGroup, FormLabel, Row, Spinner } from "react-bootstrap";
import client from "./ApiClient";
import { Band } from "./Types";
import { Band, Option } from "./Types";
import ModalBand from "./ModalBand";
import { useHistory, useParams } from "react-router-dom";
function App() {
const searchRef = useRef<HTMLInputElement>(null);
const [bands, setBands] = useState<Band[]>([]);
const [bands, setBands] = useState<Option<Band[]>>(null);
const [selectedBand, setSelectedBand] = useState<Option<Band>>(null);
const params = useParams<any>();
const bandQuery = decodeURIComponent(params.query);
const history = useHistory();
useEffect(() => {
if (bandQuery) {
client.searchBand(bandQuery).then((res) => setBands(res));
}
}, [bandQuery]);
const search = () => {
const input = searchRef.current?.value;
if (!input) {
return;
}
client.searchBand(input).then((res) => setBands(res));
history.push(`/${encodeURIComponent(input)}`);
setBands(null);
};
return (
@ -26,22 +40,35 @@ function App() {
<Button onClick={search}>Search</Button>
</Row>
<Row>
{bands &&
{selectedBand && <ModalBand onClose={() => setSelectedBand(null)} band={selectedBand} />}
{bands ? (
bands.map((band) => (
<Card style={{ width: "18rem" }} key={band.uid}>
<Card onClick={() => setSelectedBand(band)} style={{ width: "18rem" }} key={band.uid}>
<Card.Img variant="top" src={band.url_for_image_original} />
<Card.Title>{band.name}</Card.Title>
<Card.Body>{getBioText(band)}</Card.Body>
<Card.Body>{getBioText(band, 200)}</Card.Body>
</Card>
))}
))
) : bandQuery ? (
<></>
) : (
<Spinner animation="border" />
)}
</Row>
</Container>
);
}
function getBioText(band: Band): string {
export function getBioText(band: Band, maxLen: number): string {
const bio = band.biographies.find((bio) => bio.lang === "de") || band.biographies[0];
return bio?.description || "Keine Biographie angegeben.";
return limit(bio?.description || "Keine Biographie angegeben.", maxLen);
}
function limit(str: string, max: number): string {
if (str.length > max) {
return str.substr(0, max - 3) + "...";
}
return str;
}
export default App;

View file

@ -1,15 +0,0 @@
import React, { useEffect, useState } from "react";
import client from "./ApiClient";
import { BandById, Option } from "./Types";
const Band = ({ id }: { id: number }) => {
const [band, setBand] = useState<Option<BandById>>(null);
useEffect(() => {
client.get<BandById>(`/bands/${id}`).then((data) => setBand(data));
}, [id]);
return <div></div>;
};
export default Band;

28
src/ModalBand.tsx Normal file
View file

@ -0,0 +1,28 @@
import React from "react";
import { Band } from "./Types";
import { Button, Modal, Spinner } from "react-bootstrap";
import { getBioText } from "./App";
interface Props {
onClose: () => void;
band: Band;
}
const ModalBand = ({ band, onClose }: Props) => {
return (
<div>
<Modal show={true}>
<Modal.Footer>
<Button onClick={onClose}>Close</Button>
</Modal.Footer>
<Modal.Header>{band.name}</Modal.Header>
<Modal.Body>
<img width="400" src={band.url_for_image_original} alt={band.name} />
<div>{getBioText(band, 10000)}</div>
</Modal.Body>
</Modal>
</div>
);
};
export default ModalBand;

View file

@ -1,5 +1,3 @@
import band from "./bandByIdRes.json";
export type Option<T> = T | null;
export interface Band {
@ -34,4 +32,8 @@ export interface Band {
singles_count: number;
performances: Array<any>;
}
export type BandById = typeof band;
export type Response<T, Name extends string> = {
status: string;
code: string;
} & { [P in Name]: T };

File diff suppressed because it is too large Load diff

View file

@ -2,10 +2,18 @@ import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import "bootstrap/dist/css/bootstrap.min.css";
import { BrowserRouter, Route, Switch } from "react-router-dom";
ReactDOM.render(
<React.StrictMode>
<App />
<BrowserRouter>
<Switch>
<Route path="/:query">
<App />
</Route>
<Route path="/" component={App} />
</Switch>
</BrowserRouter>
</React.StrictMode>,
document.getElementById("root")
);