143 lines
5.4 KiB
JavaScript
143 lines
5.4 KiB
JavaScript
import React from "react";
|
|
import { Badge } from "../UI/Badge";
|
|
import { Button } from "../UI/Button";
|
|
import { Panel } from "../UI/Panel";
|
|
import { getInvitationReferenceLabel } from "./invitationReference";
|
|
import { supabase } from "../../supabaseClient";
|
|
import { matchesStopWord } from "../../hooks/useStopWords";
|
|
|
|
const flattenOrderProducts = (rawItems) => {
|
|
if (!Array.isArray(rawItems) || rawItems.length === 0) return [];
|
|
|
|
const products = [];
|
|
|
|
for (const item of rawItems) {
|
|
if (!item || typeof item !== "object") continue;
|
|
|
|
const subItems = Array.isArray(item.items) ? item.items : [];
|
|
|
|
if (subItems.length > 0) {
|
|
const hasSubOrders = subItems.some(
|
|
(s) => typeof s === "object" && ("nom" in s || ("items" in s && Array.isArray(s.items))),
|
|
);
|
|
|
|
if (hasSubOrders) {
|
|
for (const sub of subItems) {
|
|
if (!sub || typeof sub !== "object") continue;
|
|
const productsList = Array.isArray(sub.items) ? sub.items : [];
|
|
for (const p of productsList) {
|
|
if (!p || typeof p !== "object") continue;
|
|
const pName = String(p.product_name || p.name || "").trim();
|
|
if (!pName) continue;
|
|
products.push({
|
|
name: pName,
|
|
quantity: String(p.product_quantity || p.quantity || p.count || p.amount || "").trim(),
|
|
unit: String(p.product_ed || p.unit || "").trim(),
|
|
_sourceOrder: sub,
|
|
});
|
|
}
|
|
}
|
|
} else {
|
|
for (const p of subItems) {
|
|
if (!p || typeof p !== "object") continue;
|
|
const pName = String(p.product_name || p.name || "").trim();
|
|
if (!pName) continue;
|
|
products.push({
|
|
name: pName,
|
|
quantity: String(p.product_quantity || p.quantity || p.count || p.amount || "").trim(),
|
|
unit: String(p.product_ed || p.unit || "").trim(),
|
|
_sourceOrder: item,
|
|
});
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
const name = String(item.product_name || item.name || item.nom || "").trim();
|
|
if (!name) continue;
|
|
products.push({
|
|
name,
|
|
quantity: String(item.product_quantity || item.quantity || item.count || item.amount || "").trim(),
|
|
unit: String(item.product_ed || item.unit || "").trim(),
|
|
_sourceOrder: item,
|
|
});
|
|
}
|
|
|
|
return products;
|
|
};
|
|
|
|
export const OrderCompositionPanel = ({ invitation = {} }) => {
|
|
const [stopWords, setStopWords] = React.useState([]);
|
|
const [stopWordsLoaded, setStopWordsLoaded] = React.useState(false);
|
|
const [scopeActive, setScopeActive] = React.useState(true);
|
|
|
|
React.useEffect(() => {
|
|
if (!supabase) { setStopWordsLoaded(true); return; }
|
|
Promise.all([
|
|
supabase.from("stop_words").select("word"),
|
|
supabase.from("stop_words_scope").select("scope").eq("id", 1).single(),
|
|
]).then(([{ data: wordsData }, { data: scopeData }]) => {
|
|
if (wordsData) setStopWords(wordsData.map((d) => d.word));
|
|
setScopeActive(scopeData?.scope === "everywhere" || scopeData?.scope === "client_only");
|
|
setStopWordsLoaded(true);
|
|
})
|
|
.catch(() => setStopWordsLoaded(true));
|
|
}, []);
|
|
|
|
const rawItems = invitation.orderItems || invitation.items || [];
|
|
const allProducts = flattenOrderProducts(rawItems);
|
|
const products = (stopWords.length && scopeActive)
|
|
? allProducts.filter((p) => !matchesStopWord(p.name, stopWords))
|
|
: allProducts;
|
|
|
|
const filteredCount = allProducts.length - products.length;
|
|
const reference = getInvitationReferenceLabel(invitation);
|
|
|
|
const [isExpanded, setIsExpanded] = React.useState(false);
|
|
|
|
// Hide the entire panel if there are no products to show and some were filtered
|
|
if (products.length === 0 && filteredCount > 0) return null;
|
|
if (products.length === 0 && filteredCount === 0) return null;
|
|
|
|
return (
|
|
<Panel className="space-y-3 border shadow-soft p-5 sm:p-6">
|
|
<Button
|
|
variant="ghost"
|
|
className="flex w-full items-center justify-between text-left"
|
|
onClick={() => setIsExpanded(!isExpanded)}
|
|
>
|
|
<p className="text-sm uppercase tracking-[0.18em] text-[var(--color-text-muted)]">
|
|
Состав заказа{reference && reference !== "Счет —" ? ` ${reference}` : ""}
|
|
</p>
|
|
<span className="flex items-center gap-2 text-sm text-[var(--color-text-muted)]">
|
|
{products.length} поз.
|
|
<svg
|
|
className="h-4 w-4 transition-transform"
|
|
style={{ transform: isExpanded ? "rotate(180deg)" : "rotate(0deg)" }}
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
strokeWidth={2}
|
|
>
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
|
|
</svg>
|
|
</span>
|
|
</Button>
|
|
{isExpanded && (
|
|
<div className="space-y-2">
|
|
{products.map((product, idx) => (
|
|
<div
|
|
key={`${product.name}-${idx}`}
|
|
className="flex items-center justify-between gap-3 rounded-[18px] border border-[var(--color-border)] bg-[var(--color-surface-strong)] px-4 py-3 text-sm"
|
|
>
|
|
<span className="leading-6">{product.name}</span>
|
|
{(product.quantity || product.unit) ? (
|
|
<Badge tone="neutral">{[product.quantity, product.unit].filter(Boolean).join(" ")}</Badge>
|
|
) : null}
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</Panel>
|
|
);
|
|
}; |