q3z34
This commit is contained in:
parent
4b10b28588
commit
b486199ca2
@ -476,3 +476,64 @@
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
/* ── Mengenauswahl ───────────────────────── */
|
||||
.baz-qty-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.baz-qty-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background: linear-gradient(#3a2810, #1a0f04);
|
||||
border: 2px solid #6b4b2a;
|
||||
border-radius: 6px;
|
||||
color: #f0d9a6;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: .15s;
|
||||
line-height: 1;
|
||||
}
|
||||
.baz-qty-btn:hover {
|
||||
border-color: #f0d060;
|
||||
color: #fff;
|
||||
}
|
||||
.baz-qty-input {
|
||||
width: 52px;
|
||||
text-align: center;
|
||||
background: rgba(0,0,0,0.5);
|
||||
border: 2px solid #6b4b2a;
|
||||
border-radius: 6px;
|
||||
color: #f0d9a6;
|
||||
font-family: "Cinzel", serif;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
padding: 4px 0;
|
||||
outline: none;
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
.baz-qty-input::-webkit-inner-spin-button,
|
||||
.baz-qty-input::-webkit-outer-spin-button { -webkit-appearance: none; }
|
||||
.baz-qty-input:focus { border-color: #f0d060; }
|
||||
.baz-qty-max {
|
||||
padding: 4px 10px;
|
||||
background: linear-gradient(#4a3010, #2a1808);
|
||||
border: 2px solid #c8960c;
|
||||
border-radius: 6px;
|
||||
color: #f0d060;
|
||||
font-family: "Cinzel", serif;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
letter-spacing: 1px;
|
||||
transition: .15s;
|
||||
}
|
||||
.baz-qty-max:hover {
|
||||
border-color: #f0d060;
|
||||
color: #fff5cc;
|
||||
}
|
||||
|
||||
@ -179,18 +179,37 @@ async function loadShopCards() {
|
||||
function showBuyConfirm(card) {
|
||||
document.getElementById("baz-confirm-modal")?.remove();
|
||||
|
||||
const canAfford =
|
||||
baz_wood >= card.price_wood &&
|
||||
baz_iron >= card.price_iron &&
|
||||
baz_gold >= card.price_gold &&
|
||||
baz_gems >= card.price_gems;
|
||||
const maxAffordable = Math.max(1, Math.min(99,
|
||||
card.price_wood > 0 ? Math.floor(baz_wood / card.price_wood) : 99,
|
||||
card.price_iron > 0 ? Math.floor(baz_iron / card.price_iron) : 99,
|
||||
card.price_gold > 0 ? Math.floor(baz_gold / card.price_gold) : 99,
|
||||
card.price_gems > 0 ? Math.floor(baz_gems / card.price_gems) : 99,
|
||||
));
|
||||
|
||||
const priceStr = [
|
||||
card.price_wood > 0 ? `🪵 ${card.price_wood} Holz` : "",
|
||||
card.price_iron > 0 ? `⚙️ ${card.price_iron} Eisen` : "",
|
||||
card.price_gold > 0 ? `🪙 ${card.price_gold} Gold` : "",
|
||||
card.price_gems > 0 ? `💎 ${card.price_gems} Gems` : "",
|
||||
].filter(Boolean).join(" ");
|
||||
function calcTotal(qty) {
|
||||
return {
|
||||
wood: card.price_wood * qty,
|
||||
iron: card.price_iron * qty,
|
||||
gold: card.price_gold * qty,
|
||||
gems: card.price_gems * qty,
|
||||
};
|
||||
}
|
||||
|
||||
function priceHtml(qty) {
|
||||
const t = calcTotal(qty);
|
||||
return [
|
||||
t.wood > 0 ? `<span class="baz-price-wood">🪵 ${t.wood}</span>` : "",
|
||||
t.iron > 0 ? `<span class="baz-price-iron">⚙️ ${t.iron}</span>` : "",
|
||||
t.gold > 0 ? `<span class="baz-price-gold">🪙 ${t.gold}</span>` : "",
|
||||
t.gems > 0 ? `<span class="baz-price-gems">💎 ${t.gems}</span>` : "",
|
||||
].filter(Boolean).join(" ");
|
||||
}
|
||||
|
||||
function canAffordQty(qty) {
|
||||
const t = calcTotal(qty);
|
||||
return baz_wood >= t.wood && baz_iron >= t.iron &&
|
||||
baz_gold >= t.gold && baz_gems >= t.gems;
|
||||
}
|
||||
|
||||
const modal = document.createElement("div");
|
||||
modal.id = "baz-confirm-modal";
|
||||
@ -202,33 +221,68 @@ function showBuyConfirm(card) {
|
||||
onerror="this.src='/images/avatar_placeholder.svg'">
|
||||
</div>
|
||||
<div class="baz-confirm-name">${card.name}</div>
|
||||
<div class="baz-confirm-price">${priceStr || "Kostenlos"}</div>
|
||||
${!canAfford ? `<div class="baz-confirm-warn">⚠ Nicht genug Ressourcen</div>` : ""}
|
||||
<div class="baz-qty-wrap">
|
||||
<button class="baz-qty-btn" id="baz-qty-minus">−</button>
|
||||
<input class="baz-qty-input" id="baz-qty" type="number"
|
||||
min="1" max="99" value="1">
|
||||
<button class="baz-qty-btn" id="baz-qty-plus">+</button>
|
||||
<button class="baz-qty-max" id="baz-qty-max">MAX</button>
|
||||
</div>
|
||||
<div class="baz-confirm-price" id="baz-total-price">${priceHtml(1) || "Kostenlos"}</div>
|
||||
<div class="baz-confirm-warn" id="baz-afford-warn" style="display:none">⚠ Nicht genug Ressourcen</div>
|
||||
<div class="baz-confirm-btns">
|
||||
<button class="baz-btn-cancel" id="baz-cancel">Abbrechen</button>
|
||||
<button class="baz-btn-buy" id="baz-confirm" ${!canAfford?"disabled":""}>Kaufen</button>
|
||||
<button class="baz-btn-buy" id="baz-confirm">Kaufen</button>
|
||||
</div>
|
||||
</div>`;
|
||||
document.getElementById("bazaar-popup").appendChild(modal);
|
||||
|
||||
const qtyInput = modal.querySelector("#baz-qty");
|
||||
const priceEl = modal.querySelector("#baz-total-price");
|
||||
const warnEl = modal.querySelector("#baz-afford-warn");
|
||||
const buyBtn = modal.querySelector("#baz-confirm");
|
||||
|
||||
function updateModal() {
|
||||
const qty = Math.max(1, Math.min(99, parseInt(qtyInput.value) || 1));
|
||||
qtyInput.value = qty;
|
||||
priceEl.innerHTML = priceHtml(qty) || "Kostenlos";
|
||||
const ok = canAffordQty(qty);
|
||||
warnEl.style.display = ok ? "none" : "";
|
||||
buyBtn.disabled = !ok;
|
||||
}
|
||||
|
||||
qtyInput.addEventListener("input", updateModal);
|
||||
modal.querySelector("#baz-qty-minus").onclick = () => {
|
||||
qtyInput.value = Math.max(1, parseInt(qtyInput.value || 1) - 1); updateModal();
|
||||
};
|
||||
modal.querySelector("#baz-qty-plus").onclick = () => {
|
||||
qtyInput.value = Math.min(99, parseInt(qtyInput.value || 1) + 1); updateModal();
|
||||
};
|
||||
modal.querySelector("#baz-qty-max").onclick = () => {
|
||||
qtyInput.value = maxAffordable; updateModal();
|
||||
};
|
||||
|
||||
updateModal();
|
||||
|
||||
modal.querySelector("#baz-cancel").onclick = () => modal.remove();
|
||||
modal.querySelector(".baz-confirm-backdrop").onclick = () => modal.remove();
|
||||
modal.querySelector("#baz-confirm").onclick = async () => {
|
||||
const btn = modal.querySelector("#baz-confirm");
|
||||
btn.disabled = true; btn.textContent = "…";
|
||||
|
||||
buyBtn.onclick = async () => {
|
||||
const qty = Math.max(1, Math.min(99, parseInt(qtyInput.value) || 1));
|
||||
buyBtn.disabled = true; buyBtn.textContent = "…";
|
||||
try {
|
||||
const res = await fetch("/api/bazaar/buy", {
|
||||
method:"POST", headers:{"Content-Type":"application/json"},
|
||||
body: JSON.stringify({ card_id: card.id }),
|
||||
body: JSON.stringify({ card_id: card.id, quantity: qty }),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) { btn.textContent = data.error||"Fehler"; setTimeout(()=>modal.remove(),2000); return; }
|
||||
if (!res.ok) { buyBtn.textContent = data.error||"Fehler"; setTimeout(()=>modal.remove(),2000); return; }
|
||||
baz_wood=data.wood; baz_iron=data.iron; baz_gold=data.gold; baz_gems=data.gems;
|
||||
updateCurrencyDisplay();
|
||||
modal.remove();
|
||||
showToast(`✅ ${card.name} gekauft!`);
|
||||
showToast(`✅ ${qty}× ${card.name} gekauft!`);
|
||||
await loadShopCards();
|
||||
} catch { btn.textContent="Fehler"; setTimeout(()=>modal.remove(),2000); }
|
||||
} catch { buyBtn.textContent="Fehler"; setTimeout(()=>modal.remove(),2000); }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -76,8 +76,9 @@ router.get("/bazaar/cards", requireLogin, async (req, res) => {
|
||||
════════════════════════════════════════════ */
|
||||
router.post("/bazaar/buy", requireLogin, async (req, res) => {
|
||||
const userId = req.session.user.id;
|
||||
const { card_id } = req.body;
|
||||
const { card_id, quantity } = req.body;
|
||||
if (!card_id) return res.status(400).json({ error: "card_id fehlt." });
|
||||
const qty = Math.max(1, Math.min(99, parseInt(quantity) || 1));
|
||||
|
||||
try {
|
||||
const [[card]] = await db.query(
|
||||
@ -117,10 +118,10 @@ router.post("/bazaar/buy", requireLogin, async (req, res) => {
|
||||
{ key: "gems", label: "Gems" },
|
||||
];
|
||||
for (const { key, label } of checks) {
|
||||
const need = card[`price_${key}`];
|
||||
const need = card[`price_${key}`] * qty;
|
||||
if (need > 0 && (currency[key] || 0) < need) {
|
||||
return res.status(400).json({
|
||||
error: `Nicht genug ${label}. Benötigt: ${need}, Vorhanden: ${currency[key] || 0}`,
|
||||
error: `Nicht genug ${label}. Benötigt: ${need} (${qty}×), Vorhanden: ${currency[key] || 0}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -133,18 +134,18 @@ router.post("/bazaar/buy", requireLogin, async (req, res) => {
|
||||
gems = gems - ?
|
||||
WHERE account_id = ?`,
|
||||
[
|
||||
card.price_wood,
|
||||
card.price_iron,
|
||||
card.price_gold,
|
||||
card.price_gems,
|
||||
card.price_wood * qty,
|
||||
card.price_iron * qty,
|
||||
card.price_gold * qty,
|
||||
card.price_gems * qty,
|
||||
userId,
|
||||
],
|
||||
);
|
||||
|
||||
await db.query(
|
||||
`INSERT INTO user_cards (user_id, card_id, amount) VALUES (?, ?, 1)
|
||||
ON DUPLICATE KEY UPDATE amount = amount + 1`,
|
||||
[userId, card_id],
|
||||
`INSERT INTO user_cards (user_id, card_id, amount) VALUES (?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE amount = amount + ?`,
|
||||
[userId, card_id, qty, qty],
|
||||
);
|
||||
|
||||
const [[updated]] = await db.query(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user