fix: recepty přesnuty k jídlům

feat: přidána podpora fotek
This commit is contained in:
Ondřej Anděl committed 2026-10-12 10:20:12 +02:00
1 parent efed5e6e86
commit e13f7ce8e5
39 files changed
+1266 -511

No files matched your search

+58 -14
View File
@@ -1,13 +1,15 @@
import { useState } from "react";
import { Button } from "react-bootstrap";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCalendarDays, faPen, faPenToSquare } from "@fortawesome/free-solid-svg-icons";
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,
@@ -16,8 +18,10 @@ type Props = {
/** Rozbalená akce; nejvýš jeden řádek seznamu má nějakou otevřenou. */
action?: MealGroupAction,
onAction: (action?: MealGroupAction) => void,
/** Zavolá se po doplnění nebo přejmenování, aby se seznam načetl znovu. */
/** 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". */
@@ -26,20 +30,35 @@ const describeMissing = (missing: number, count: number): string =>
/**
* Řádek skupiny jídel (stejný název a zdroj) s přehledem chybějících údajů.
* Nabízí 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.
* 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 }: Readonly<Props>) => {
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>}
<span className="tag">{group.count}× · naposledy {formatDateString(group.lastDate)}</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)}
@@ -58,7 +77,7 @@ const MealGroupRow = ({ group, groupList, action, onAction, onChanged }: Readonl
</div>
</div>
<div className="meal-group-actions">
{!action && (
{!action && group.count > 0 && (
<Button
variant="outline-secondary"
size="sm"
@@ -68,6 +87,24 @@ const MealGroupRow = ({ group, groupList, action, onAction, onChanged }: Readonl
<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"
@@ -77,19 +114,26 @@ const MealGroupRow = ({ group, groupList, action, onAction, onChanged }: Readonl
>
<FontAwesomeIcon icon={faPen} /> Přejmenovat
</Button>
<Button
variant={action === MealGroupAction.DOPLNIT ? 'outline-secondary' : 'primary'}
size="sm"
onClick={() => toggle(MealGroupAction.DOPLNIT)}
>
<FontAwesomeIcon icon={faPenToSquare} /> {action === MealGroupAction.DOPLNIT ? 'Zavřít' : 'Doplnit'}
</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}