feat: založení základní stránky příjem/výdej + import a statistiky
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
import { useState } from "react";
|
||||
import { Button, Spinner } from "react-bootstrap";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faMagnifyingGlass, faArrowUpRightFromSquare, faBookmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { CalorieSearchResult, searchCalories } from "../../../types";
|
||||
|
||||
type Props = {
|
||||
/** Název jídla, ke kterému se energie hledá. */
|
||||
name: string,
|
||||
/** Zavolá se po výběru návrhu s energií na 100 g v kcal. */
|
||||
onPick: (caloriesPer100g: number) => void,
|
||||
};
|
||||
|
||||
/** Návrhy pocházející z vlastní knihovny se odlišují ikonou a popiskem. */
|
||||
const LIBRARY_ORIGIN = 'library';
|
||||
|
||||
/**
|
||||
* Vyhledání energetické hodnoty k názvu jídla.
|
||||
*
|
||||
* Návrhy chodí z vlastní knihovny dřívějších zadání a od externího poskytovatele.
|
||||
* Když je poskytovatel nedostupný (což se stává), zůstane aspoň knihovna a odkaz
|
||||
* na KalorickéTabulky.cz, odkud si hodnotu uživatel opíše ručně — automaticky se
|
||||
* odtud nic nestahuje, veřejné API nemají.
|
||||
*/
|
||||
export default function CalorieLookup({ name, onPick }: Readonly<Props>) {
|
||||
const [result, setResult] = useState<CalorieSearchResult | undefined>();
|
||||
const [searching, setSearching] = useState(false);
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const canSearch = name.trim().length > 0 && !searching;
|
||||
|
||||
/**
|
||||
* Zkopíruje název jídla do schránky při otevření Kalorických tabulek.
|
||||
* Jejich vyhledávání nejde předvyplnit z URL, takže aspoň stačí vložit.
|
||||
*/
|
||||
const copyName = () => {
|
||||
navigator.clipboard?.writeText(name.trim())
|
||||
.then(() => setCopied(true))
|
||||
.catch(() => { /* schránka není dostupná, odkaz se stejně otevře */ });
|
||||
};
|
||||
|
||||
const search = async () => {
|
||||
if (!canSearch) return;
|
||||
setSearching(true);
|
||||
const response = await searchCalories({ query: { q: name.trim() } });
|
||||
setResult(response.data);
|
||||
setCopied(false);
|
||||
setSearching(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="calorie-lookup">
|
||||
<Button variant="outline-secondary" size="sm" onClick={search} disabled={!canSearch}>
|
||||
{searching
|
||||
? <Spinner animation="border" size="sm" />
|
||||
: <FontAwesomeIcon icon={faMagnifyingGlass} />}
|
||||
{' '}Najít kalorie
|
||||
</Button>
|
||||
|
||||
{!canSearch && !name.trim().length && (
|
||||
<span className="calorie-lookup-hint">nejdřív vyplňte název jídla</span>
|
||||
)}
|
||||
|
||||
{result && !searching && (
|
||||
<div className="calorie-lookup-results">
|
||||
{result.suggestions.map(suggestion => (
|
||||
<button
|
||||
type="button"
|
||||
className="calorie-suggestion"
|
||||
key={`${suggestion.origin}-${suggestion.name}-${suggestion.caloriesPer100g}`}
|
||||
onClick={() => onPick(suggestion.caloriesPer100g)}
|
||||
title="Použít tuto hodnotu"
|
||||
>
|
||||
{suggestion.origin === LIBRARY_ORIGIN && (
|
||||
<FontAwesomeIcon icon={faBookmark} className="calorie-suggestion-icon" />
|
||||
)}
|
||||
<span className="calorie-suggestion-name">{suggestion.name}</span>
|
||||
{suggestion.brand && (
|
||||
<span className="calorie-suggestion-brand">{suggestion.brand}</span>
|
||||
)}
|
||||
<span className="calorie-suggestion-value">{suggestion.caloriesPer100g} kcal/100 g</span>
|
||||
</button>
|
||||
))}
|
||||
|
||||
{!result.suggestions.length && (
|
||||
<div className="calorie-lookup-hint">
|
||||
Nic se nenašlo{!result.providerAvailable && ' a externí databáze je nedostupná'}.
|
||||
</div>
|
||||
)}
|
||||
|
||||
{result.providerAvailable === false && !!result.suggestions.length && (
|
||||
<div className="calorie-lookup-hint">
|
||||
Externí databáze je nedostupná, návrhy jsou jen z vašich dřívějších zadání.
|
||||
</div>
|
||||
)}
|
||||
|
||||
<a
|
||||
className="calorie-lookup-external"
|
||||
href={result.externalSearchUrl}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
onClick={copyName}
|
||||
>
|
||||
{copied ? 'Název zkopírován, vložte ho do jejich hledání' : 'Otevřít KalorickéTabulky.cz'}
|
||||
{' '}<FontAwesomeIcon icon={faArrowUpRightFromSquare} />
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user