const PRODUCTS = {
base: [
{ id: "espresso", label: "Эспрессо", price: 900, hasMilk: false, drink: true },
{ id: "americano", label: "Американо", price: 1100, hasMilk: false, drink: true },
{ id: "latte", label: "Латте (база)", price: 900, hasMilk: true, drink: true },
{ id: "mocha", label: "Мока", price: 1100, hasMilk: true, drink: true },
{ id: "herb", label: "Травяной чай", price: 800, hasMilk: false, drink: true },
],
milk: [
{ id: "none", label: "Без молока", price: 0 },
{ id: "milk", label: "Коровье", price: 100 },
{ id: "oat", label: "Овсяное", price: 300 },
{ id: "almond", label: "Миндальное", price: 300 },
{ id: "coconut", label: "Кокосовое", price: 350 },
],
syrup: [
{ id: "none", label: "Без сиропа", price: 0 },
{ id: "vanilla", label: "Ваниль", price: 300 },
{ id: "caramel", label: "Карамель", price: 300 },
{ id: "lavender", label: "Лаванда", price: 350 },
{ id: "honey", label: "Мёд", price: 350 },
],
size: [
{ id: "s", label: "250 мл", price: 0 },
{ id: "m", label: "350 мл", price: 300 },
{ id: "l", label: "450 мл", price: 500 },
],
temp: [
{ id: "hot", label: "Горячий", price: 0 },
{ id: "iced", label: "На льду", price: 200 },
],
};
const BASE_COLORS = {
espresso: "var(--kt-ai-fg)",
americano: "var(--kt-ai-fg-muted)",
latte: "var(--kt-ai-primary-subtle)",
mocha: "var(--kt-ai-border-strong)",
herb: "var(--kt-ai-chart-green)",
};
const MILK_COLORS = {
none: "transparent",
milk: "var(--kt-ai-bg)",
oat: "var(--kt-ai-bg-soft)",
almond: "var(--kt-ai-bg-hover)",
coconut: "var(--kt-ai-bg-elevated)",
};
const SYRUP_COLORS = {
none: "transparent",
vanilla: "var(--kt-ai-chip-orange-bg)",
caramel: "var(--kt-ai-chip-orange-fg)",
lavender: "var(--kt-ai-chip-purple-bg)",
honey: "var(--kt-ai-chip-orange-bg)",
};
const MOODS = {
wake: { base: "espresso", milk: "none", syrup: "none", size: "s", temp: "hot" },
soft: { base: "latte", milk: "milk", syrup: "vanilla", size: "m", temp: "hot" },
light: { base: "herb", milk: "none", syrup: "honey", size: "m", temp: "hot" },
};
const state = { base: null, milk: "none", syrup: "none", size: "m", temp: "hot" };
const $ = (id) => document.getElementById(id);
const byId = (group, id) => (PRODUCTS[group] || []).find((p) => p.id === id);
const fmt = (price) => price.toLocaleString("ru-RU") + " \u20B8";
function renderGroup(group) {
const wrap = $("group-" + group);
wrap.innerHTML = "";
PRODUCTS[group].forEach((opt) => {
const label = document.createElement("label");
label.className = "opt";
const input = document.createElement("input");
input.type = "radio";
input.name = "opt-" + group;
input.value = opt.id;
input.checked = state[group] === opt.id;
input.addEventListener("change", () => {
state[group] = opt.id;
update();
});
const name = document.createElement("span");
name.className = "opt-label";
name.textContent = opt.label;
label.appendChild(input);
label.appendChild(name);
if (opt.price > 0) {
const price = document.createElement("span");
price.className = "opt-price";
price.textContent = "+" + fmt(opt.price);
label.appendChild(price);
}
wrap.appendChild(label);
});
}
function selected(group) {
return byId(group, state[group]);
}
function priceTotal() {
if (!state.base) return 0;
const b = selected("base");
const m = selected("milk");
const s = selected("syrup");
const z = selected("size");
const t = selected("temp");
return (
b.price +
(b.hasMilk ? m.price : 0) +
(b.drink ? s.price : 0) +
z.price +
t.price
);
}
function paintCup() {
const layers = $("layers");
const ice = $("iceGroup");
const steam = $("steam");
layers.innerHTML = "";
ice.innerHTML = "";
if (!state.base) return;
const b = selected("base");
const m = selected("milk");
const s = selected("syrup");
const z = selected("size");
const t = selected("temp");
const fillFactor = z.id === "s" ? 0.45 : z.id === "m" ? 0.72 : 0.9;
const innerTop = 52;
const innerH = 158;
const fillH = Math.round(innerH * fillFactor);
const fillY = innerTop + (innerH - fillH);
const widthBySize = { s: 88, m: 124, l: 148 };
const width = widthBySize[z.id];
const cupX = 100 - width / 2;
[ $("cupInner"), $("cupBody") ].forEach((el) => {
el.setAttribute("width", width);
el.setAttribute("x", cupX);
});
const NS = "http://www.w3.org/2000/svg";
const addRect = (x, width, y, h, color, rx) => {
const rect = document.createElementNS(NS, "rect");
rect.setAttribute("x", x);
rect.setAttribute("width", width);
rect.setAttribute("y", Math.max(innerTop, y));
rect.setAttribute("height", h);
rect.setAttribute("fill", color);
rect.setAttribute("class", "part");
if (rx) rect.setAttribute("rx", rx);
layers.appendChild(rect);
return rect;
};
const innerX = cupX + 5;
const innerW = width - 10;
let y = fillY + fillH;
if (t.id === "iced") {
if (b.hasMilk && m.id !== "none") { y -= fillH * 0.35; addRect(innerX, innerW, y, fillH * 0.35, MILK_COLORS[m.id]); }
y -= fillH * 0.45; addRect(innerX, innerW, y, fillH * 0.45, BASE_COLORS[b.id]);
if (b.drink && s.id !== "none") { y -= fillH * 0.2; addRect(innerX, innerW, y, fillH * 0.2, SYRUP_COLORS[s.id]); }
for (let i = 0; i < 3; i++) {
const cube = document.createElementNS(NS, "rect");
cube.setAttribute("x", cupX + 10 + i * (innerW - 20) / 3);
cube.setAttribute("y", fillY + 6);
cube.setAttribute("width", Math.round((innerW - 20) / 3 - 8));
cube.setAttribute("height", "22");
cube.setAttribute("rx", "4");
cube.setAttribute("fill", "rgba(190,222,240,0.9)");
cube.setAttribute("stroke", "rgba(255,255,255,0.7)");
cube.setAttribute("stroke-width", "2");
cube.setAttribute("class", "part");
ice.appendChild(cube);
}
} else {
addRect(innerX, innerW, fillY, fillH * 0.45, BASE_COLORS[b.id]);
if (b.hasMilk && m.id !== "none") addRect(innerX, innerW, fillY + fillH * 0.45, fillH * 0.3, MILK_COLORS[m.id]);
if (b.drink && s.id !== "none") addRect(innerX, innerW, fillY + fillH * 0.75, fillH * 0.25, SYRUP_COLORS[s.id]);
}
steam.setAttribute("opacity", t.id === "hot" ? "1" : "0");
}
function buildName(b, m, s, z, t) {
const tempWord = t.id === "iced" ? "Ледяной" : "Горячий";
const sizeWord = z.id === "s" ? "маленький" : z.id === "m" ? "средний" : "большой";
const bits = [tempWord, sizeWord.toLowerCase()];
if (b.hasMilk && m.id !== "none") bits.push(m.label.toLowerCase());
if (b.drink && s.id !== "none") bits.push("сироп " + s.label.toLowerCase());
bits.push(b.label.toLowerCase());
return bits.join(", ");
}
function update() {
paintCup();
if (!state.base) return;
const b = selected("base");
const m = selected("milk");
const s = selected("syrup");
const z = selected("size");
const t = selected("temp");
$("buildName").textContent = buildName(b, m, s, z, t);
const lines = ["Основа: " + b.label, "Размер: " + z.label, "Подача: " + t.label];
if (b.hasMilk) lines.splice(1, 0, "Молоко: " + m.label);
if (b.drink) lines.splice(2, 0, "Сироп: " + s.label);
$("buildSummary").innerHTML = lines.map((l) => "" + l + "").join("");
const total = priceTotal();
const base = b.price;
$("buildPrice").innerHTML = "Итого " + fmt(total) +
(total > base ? " (база " + fmt(base) + " + " + fmt(total - base) + ")" : "");
}
function renderAll() {
Object.keys(PRODUCTS).forEach(renderGroup);
}
function applyMood(moodId) {
const preset = MOODS[moodId];
if (!preset) return;
Object.assign(state, preset);
renderAll();
update();
document.querySelectorAll(".mood-btn").forEach((btn) => {
const active = btn.dataset.mood === moodId;
btn.style.borderColor = active ? "var(--kt-ai-primary)" : "";
btn.style.background = active ? "var(--kt-ai-primary-subtle)" : "";
});
$("builderCard").hidden = false;
$("builderCard").scrollIntoView({ behavior: "smooth", block: "nearest" });
}
document.querySelectorAll(".mood-btn").forEach((btn) => {
btn.addEventListener("click", () => applyMood(btn.dataset.mood));
});
renderAll();