131 lines
6.4 KiB
TypeScript
131 lines
6.4 KiB
TypeScript
import React, { useState } from 'react';
|
||
import { INITIAL_RECIPES, RecipeItem } from '../../mock/mockData';
|
||
import { Badge } from '../../shared/components/Badge';
|
||
import { Button } from '../../shared/components/Button';
|
||
import { formatCurrency } from '../../shared/utils/formatters';
|
||
import { ChefHat } from 'lucide-react';
|
||
import { Modal } from '../../shared/components/Modal';
|
||
|
||
export const RecipesView: React.FC = () => {
|
||
const [recipes] = useState<RecipeItem[]>(INITIAL_RECIPES);
|
||
const [selectedRecipe, setSelectedRecipe] = useState<RecipeItem | null>(null);
|
||
|
||
return (
|
||
<div className="space-y-4 animate-fade-in">
|
||
<div className="flex items-center justify-between bg-krona-surface p-3.5 rounded-xl border border-krona-border shadow-krona-soft">
|
||
<div>
|
||
<h2 className="text-sm font-bold text-krona-primary">
|
||
Технологические карты & Калькуляция (BOM)
|
||
</h2>
|
||
<p className="text-xs text-krona-secondary">
|
||
Нормы расхода сырья, расчет food cost %, плановая себестоимость и маржинальность
|
||
</p>
|
||
</div>
|
||
|
||
<Button variant="primary" size="sm" onClick={() => setSelectedRecipe(recipes[0])}>
|
||
<ChefHat className="w-3.5 h-3.5 mr-1.5" />
|
||
Посмотреть калькуляцию
|
||
</Button>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
{recipes.map((rec) => (
|
||
<div
|
||
key={rec.id}
|
||
onClick={() => setSelectedRecipe(rec)}
|
||
className="p-4 rounded-xl border border-krona-border bg-krona-surface hover:border-krona-border-strong shadow-krona-soft cursor-pointer transition-all space-y-3"
|
||
>
|
||
<div className="flex items-center justify-between">
|
||
<div>
|
||
<h4 className="text-sm font-bold text-krona-primary">{rec.name}</h4>
|
||
<span className="text-xs text-krona-secondary">Выход: {rec.yieldWeight} • {rec.category}</span>
|
||
</div>
|
||
<Badge variant="green" size="sm">Food Cost: {rec.foodCostPercent}%</Badge>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-3 gap-2 p-2.5 rounded-lg bg-krona-workspace text-xs">
|
||
<div>
|
||
<span className="text-krona-secondary block text-[10px]">Себестоимость:</span>
|
||
<span className="font-bold text-krona-primary">{formatCurrency(rec.primeCost)}</span>
|
||
</div>
|
||
<div>
|
||
<span className="text-krona-secondary block text-[10px]">Цена продажи:</span>
|
||
<span className="font-bold text-krona-green">{formatCurrency(rec.sellingPrice)}</span>
|
||
</div>
|
||
<div>
|
||
<span className="text-krona-secondary block text-[10px]">Маржа (руб):</span>
|
||
<span className="font-bold text-krona-gold">{formatCurrency(rec.marginRub)}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="text-xs text-krona-secondary">
|
||
<span className="font-medium text-krona-primary">Ингредиенты ({rec.ingredients.length}): </span>
|
||
{rec.ingredients.map((i) => i.name).join(', ')}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* Recipe Detail Modal */}
|
||
{selectedRecipe && (
|
||
<Modal
|
||
isOpen={Boolean(selectedRecipe)}
|
||
onClose={() => setSelectedRecipe(null)}
|
||
title={`Техкарта: ${selectedRecipe.name}`}
|
||
subtitle={`Выход готового блюда: ${selectedRecipe.yieldWeight}`}
|
||
maxWidth="lg"
|
||
footer={
|
||
<Button variant="secondary" onClick={() => setSelectedRecipe(null)}>
|
||
Закрыть
|
||
</Button>
|
||
}
|
||
>
|
||
<div className="space-y-4 text-xs">
|
||
<div className="grid grid-cols-4 gap-2 p-3 bg-krona-workspace rounded-lg text-center">
|
||
<div>
|
||
<span className="text-krona-secondary block text-[10px]">Цена в меню:</span>
|
||
<span className="font-bold text-sm text-krona-primary">{formatCurrency(selectedRecipe.sellingPrice)}</span>
|
||
</div>
|
||
<div>
|
||
<span className="text-krona-secondary block text-[10px]">Себестоимость:</span>
|
||
<span className="font-bold text-sm text-krona-danger">{formatCurrency(selectedRecipe.primeCost)}</span>
|
||
</div>
|
||
<div>
|
||
<span className="text-krona-secondary block text-[10px]">Food Cost %:</span>
|
||
<span className="font-bold text-sm text-krona-green">{selectedRecipe.foodCostPercent}%</span>
|
||
</div>
|
||
<div>
|
||
<span className="text-krona-secondary block text-[10px]">Маржинальный доход:</span>
|
||
<span className="font-bold text-sm text-krona-gold">{formatCurrency(selectedRecipe.marginRub)}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<h5 className="font-semibold text-krona-primary mb-2">Расход сырья и калькуляция:</h5>
|
||
<table className="w-full text-left border-collapse">
|
||
<thead>
|
||
<tr className="border-b border-krona-border text-krona-secondary text-[11px]">
|
||
<th className="pb-1.5 font-medium">Ингредиент</th>
|
||
<th className="pb-1.5 font-medium">Брутто</th>
|
||
<th className="pb-1.5 font-medium">Нетто</th>
|
||
<th className="pb-1.5 font-medium text-right">Стоимость</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody className="divide-y divide-krona-border/40">
|
||
{selectedRecipe.ingredients.map((ing, i) => (
|
||
<tr key={i} className="py-1.5">
|
||
<td className="py-1.5 font-medium text-krona-primary">{ing.name}</td>
|
||
<td className="py-1.5 text-krona-secondary">{ing.gross}</td>
|
||
<td className="py-1.5 text-krona-secondary">{ing.net}</td>
|
||
<td className="py-1.5 font-semibold text-right text-krona-primary">{formatCurrency(ing.cost)}</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</Modal>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|