150 lines
7.3 KiB
TypeScript
150 lines
7.3 KiB
TypeScript
import { useState } from "react";
|
||
import { Button } from "react-bootstrap";
|
||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||
import { faBookOpen, faCalendarDays, faCamera, faPen, faPenToSquare } from "@fortawesome/free-solid-svg-icons";
|
||
import { MealGroup } from "../../../types";
|
||
import { MealGroupAction } from "../enums";
|
||
import { formatDateString } from "../Utils";
|
||
import MealGroupForm from "./MealGroupForm";
|
||
import MealGroupRenameForm from "./MealGroupRenameForm";
|
||
import MealGroupDays from "./MealGroupDays";
|
||
import MealGroupRecipe from "./MealGroupRecipe";
|
||
import MealGroupPhotos from "./MealGroupPhotos";
|
||
|
||
type Props = {
|
||
group: MealGroup,
|
||
/** Všechny skupiny — přejmenování podle nich pozná sloučení s jinou. */
|
||
groupList: MealGroup[],
|
||
/** Rozbalená akce; nejvýš jeden řádek seznamu má nějakou otevřenou. */
|
||
action?: MealGroupAction,
|
||
onAction: (action?: MealGroupAction) => void,
|
||
/** Zavolá se po doplnění, přejmenování nebo změně receptu — seznam se načte znovu a akce zavře. */
|
||
onChanged: () => void,
|
||
/** Obnoví seznam a otevřenou akci nechá — po změně fotek, aby šlo přidávat dál. */
|
||
onRefresh: () => void,
|
||
};
|
||
|
||
/** Kolik jídel skupiny daný údaj nemá — "u všech", nebo "3 z 5". */
|
||
const describeMissing = (missing: number, count: number): string =>
|
||
missing === count ? 'u všech' : `${missing} z ${count}`;
|
||
|
||
/**
|
||
* Řádek skupiny jídel (stejný název a zdroj) s přehledem chybějících údajů.
|
||
* Nabízí recept a fotky jídla, doplnění údajů a změnu názvu a zdroje všem
|
||
* jídlům skupiny a dny, kdy se jídlo jedlo, jako odkazy na přehled dne.
|
||
*/
|
||
const MealGroupRow = ({ group, groupList, action, onAction, onChanged, onRefresh }: Readonly<Props>) => {
|
||
const [daysOpen, setDaysOpen] = useState(false);
|
||
const toggle = (value: MealGroupAction) => onAction(action === value ? undefined : value);
|
||
return (
|
||
<div className="card-panel meal-group">
|
||
<div className="meal-group-head">
|
||
{group.thumbnail && (
|
||
<button
|
||
type="button"
|
||
className="meal-group-thumb"
|
||
title="Fotky jídla"
|
||
onClick={() => toggle(MealGroupAction.FOTKY)}
|
||
>
|
||
<img src={group.thumbnail} alt={group.name} />
|
||
</button>
|
||
)}
|
||
<div className="meal-group-info">
|
||
<div className="meal-group-name">{group.name}</div>
|
||
<div className="meal-meta">
|
||
{group.source && <span className="tag">{group.source}</span>}
|
||
{group.lastDate
|
||
? <span className="tag">{group.count}× · naposledy {formatDateString(group.lastDate)}</span>
|
||
: <span className="tag">zatím nejedeno</span>}
|
||
{group.recipe && (
|
||
<span className="tag tag-recipe"><FontAwesomeIcon icon={faBookOpen} /> recept</span>
|
||
)}
|
||
{group.missingCaloriesCount > 0 && (
|
||
<span className="tag tag-missing">
|
||
bez kalorií {describeMissing(group.missingCaloriesCount, group.count)}
|
||
</span>
|
||
)}
|
||
{group.missingWeightCount > 0 && (
|
||
<span className="tag tag-missing">
|
||
bez gramáže {describeMissing(group.missingWeightCount, group.count)}
|
||
</span>
|
||
)}
|
||
{group.missingNutrientsCount > 0 && (
|
||
<span className="tag tag-missing">
|
||
bez živin {describeMissing(group.missingNutrientsCount, group.count)}
|
||
</span>
|
||
)}
|
||
</div>
|
||
</div>
|
||
<div className="meal-group-actions">
|
||
{!action && group.count > 0 && (
|
||
<Button
|
||
variant="outline-secondary"
|
||
size="sm"
|
||
active={daysOpen}
|
||
onClick={() => setDaysOpen(open => !open)}
|
||
>
|
||
<FontAwesomeIcon icon={faCalendarDays} /> Dny
|
||
</Button>
|
||
)}
|
||
<Button
|
||
variant="outline-secondary"
|
||
size="sm"
|
||
active={action === MealGroupAction.RECEPT}
|
||
title={group.recipe ? 'Zobrazit recept' : 'Zapsat k jídlu recept'}
|
||
onClick={() => toggle(MealGroupAction.RECEPT)}
|
||
>
|
||
<FontAwesomeIcon icon={faBookOpen} /> Recept
|
||
</Button>
|
||
<Button
|
||
variant="outline-secondary"
|
||
size="sm"
|
||
active={action === MealGroupAction.FOTKY}
|
||
title="Fotky jídla"
|
||
onClick={() => toggle(MealGroupAction.FOTKY)}
|
||
>
|
||
<FontAwesomeIcon icon={faCamera} /> {group.photoCount || ''}
|
||
</Button>
|
||
<Button
|
||
variant="outline-secondary"
|
||
size="sm"
|
||
active={action === MealGroupAction.PREJMENOVAT}
|
||
title="Změnit název a zdroj u všech jídel skupiny"
|
||
onClick={() => toggle(MealGroupAction.PREJMENOVAT)}
|
||
>
|
||
<FontAwesomeIcon icon={faPen} /> Přejmenovat
|
||
</Button>
|
||
{group.count > 0 && (
|
||
<Button
|
||
variant={action === MealGroupAction.DOPLNIT ? 'outline-secondary' : 'primary'}
|
||
size="sm"
|
||
onClick={() => toggle(MealGroupAction.DOPLNIT)}
|
||
>
|
||
<FontAwesomeIcon icon={faPenToSquare} />
|
||
{' '}{action === MealGroupAction.DOPLNIT ? 'Zavřít' : 'Doplnit'}
|
||
</Button>
|
||
)}
|
||
</div>
|
||
</div>
|
||
{(action === MealGroupAction.DOPLNIT || (!action && daysOpen)) && <MealGroupDays days={group.days} />}
|
||
{action === MealGroupAction.DOPLNIT && (
|
||
<MealGroupForm group={group} onFilled={onChanged} onCancel={() => onAction(undefined)} />
|
||
)}
|
||
{action === MealGroupAction.RECEPT && (
|
||
<MealGroupRecipe group={group} onChanged={onChanged} onClose={() => onAction(undefined)} />
|
||
)}
|
||
{action === MealGroupAction.FOTKY && <MealGroupPhotos group={group} onChanged={onRefresh} />}
|
||
{action === MealGroupAction.PREJMENOVAT && (
|
||
<MealGroupRenameForm
|
||
group={group}
|
||
groupList={groupList}
|
||
onRenamed={onChanged}
|
||
onCancel={() => onAction(undefined)}
|
||
/>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default MealGroupRow;
|