/
rootPolzovatel
/
WishBot
Обзор
Документация
Войти
/
rootPolzovatel
/
WishBot
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
script.js
256 строк
11 KB
rootPolzovatel
Create: photo_2026-07-23_22-50-38.jpg, Update: script.js
24 июл 2026, 09:39
Верифицирован
24 июл 2026, 09:39
ecd9086
Код
Авторство
О чём код?
(function () { const giftsData = [ { id: 1, name: "Kilian Princess", description: "KILIAN PARIS princess eau fraiche", image: "IMG_2855.PNG", link: "https://goldapple.ru/19000402299-princess-eau-fraiche" }, { id: 2, name: "Mango skin", description: "VILHELM PARFUMERIE mango skin", image: "IMG_2856.PNG", link: "https://goldapple.ru/19760305896-mango-skin" }, { id: 3, name: "Сертификат в золотое яблоко", description: "На любую сумму (На номер 89278480936)", image: "IMG_2857.JPG", link: "https://goldapple.ru/cards" }, { id: 4, name: "Раскраска по номерам", description: "Артикулы на вб 962789059 895986053 796760508", image: "IMG_2859.JPG", link: "None" }, { id: 5, name: "Акриловые маркеры", description: "Хотелось бы именно большой набор маркеров Артикул на вб 626871656", image: "IMG_2861.JPG", link: "None" }, { id: 6, name: "Сертификат на фотосессию", description: "К ней: @kristii_stor", image: "IMG_2862.PNG", link: "https://www.instagram.com/kristina_storonenko?igsh=MTRxeXp5ZjUxMzc0Mg==" }, { id: 7, name: "Сертификат на волосы", description: "Сюда: @averkieva_studio", image: "IMG_2863.PNG", link: "https://www.instagram.com/averkieva_studio?igsh=MWgzb3pxc2FlZG15cQ==" }, { id: 8, name: "Спа-программа", description: "Inspa zona", image: "IMG_2864.JPG", link: "https://www.instagram.com/inspa.zona?igsh=MWdjaGtnNWtqMzRkeA==" }, { id: 9, name: "Постельное белье", description: "Двуспальный комплект: Поостынь на резинке (160 х 200 х 25) Пододеяльник (180 х 220) Наволочки (70 х 70) Цвет однотонный белый / серый / бежевый", image: "IMG_2865.JPG", link: "None" }, { id: 10, name: "Сертификат на ногти", description: "Сюда: @wi_bar", image: "IMG_2866.JPG", link: "None" }, { id: 11, name: "Сертификат в Incanto", description: "В каскаде", image: "IMG_2867.JPG", link: "None" }, { id: 12, name: "Хайлайтер", description: "Rare beauty (оттенок Exhilarate) В России не продают, но можно заказать через проверенных байеров: \n1. https://vk.ru/market-209890505_9513942\n2. https://usmall.ru/product/6358340-positive-light-silky-touch-highlighter-rare-beauty-by-selena-gomez", image: "IMG_2868.JPG", link: "https://usmall.ru/product/6358340-positive-light-silky-touch-highlighter-rare-beauty-by-selena-gomez" }, { id: 13, name: "Бокалы для вина / шампанского", description: "Набор бокалов для вина или креманок для шампанского из kuchenland (в каскаде)", image: "IMG_2871.JPG", link: "None" }, { id: 14, name: "Набор из двух тарелок", description: "", image: "photo_2026-07-23_22-50-38.jpg", link: "https://www.wildberries.ru/catalog/465445228/detail.aspx" } ]; const API_URL = "https://script.google.com/macros/s/AKfycbzCqQzC5016jw5JzoEmn8NQtdYmBXMHhyMgO5aiyuqAenC-h0XsxkCegZ7oG_CBmGsn/exec"; let bookings = {}; const container = document.getElementById("giftsContainer"); const modal = document.createElement("div"); modal.className = "modal"; modal.innerHTML = ` <div class="modal-overlay"></div> <div class="modal-box"> <h3 id="modalTitle"></h3> <input id="modalInput" type="text" placeholder="Имя будет зашифровано"> <div class="modal-actions"> <button id="modalCancel">Отмена</button> <button id="modalConfirm">OK</button> </div> </div> `; document.body.appendChild(modal); const input = modal.querySelector("#modalInput"); const title = modal.querySelector("#modalTitle"); const confirmBtn = modal.querySelector("#modalConfirm"); const cancelBtn = modal.querySelector("#modalCancel"); let modalResolve = null; function openModal(text) { modal.classList.add("active"); title.textContent = text; input.value = ""; input.focus(); return new Promise(resolve => { modalResolve = resolve; }); } function closeModal(value) { modal.classList.remove("active"); if (modalResolve) modalResolve(value); modalResolve = null; } cancelBtn.onclick = () => closeModal(null); modal.querySelector(".modal-overlay").onclick = () => closeModal(null); confirmBtn.onclick = () => { const val = input.value.trim(); if (!val) return; closeModal(val); }; async function loadBookings() { try { const res = await fetch(API_URL); bookings = await res.json(); } catch { bookings = {}; } return bookings; } async function saveBookings() { try { const response = await fetch(API_URL, { method: "POST", body: JSON.stringify(bookings) }); const result = await response.json(); if (!result.success) { throw new Error(result.error || "Ошибка сохранения"); } } catch (err) { console.error(err); alert("Не удалось сохранить бронирование."); throw err; } } function isBooked(id) { return !!bookings[id]; } function book(id, name) { bookings[id] = { name }; } function unbook(id) { delete bookings[id]; } function escape(str) { const d = document.createElement("div"); d.textContent = str; return d.innerHTML; } function render() { container.innerHTML = ""; giftsData.forEach((gift) => { const booked = isBooked(gift.id); const card = document.createElement("article"); card.className = "gift-card"; const badge = document.createElement("div"); badge.className = "lot-badge"; badge.textContent = `ЛОТ ${gift.id}`; const cardInner = document.createElement("div"); cardInner.className = "card-inner"; const cardImage = document.createElement("div"); cardImage.className = "card-image"; const img = document.createElement("img"); img.src = gift.image; img.alt = gift.name; cardImage.appendChild(img); const cardDetails = document.createElement("div"); cardDetails.className = "card-details"; const giftName = document.createElement("h3"); giftName.className = "gift-name"; giftName.textContent = gift.name; const giftStatus = document.createElement("div"); giftStatus.className = `gift-status ${booked ? "booked" : "free"}`; const dot = document.createElement("span"); dot.className = "dot"; const statusText = document.createElement("span"); statusText.textContent = booked ? "Занято" : "Свободно"; giftStatus.append(dot, statusText); const descriptionText = Array.isArray(gift.description) ? gift.description.join(" ") : (gift.description || ""); const giftDescription = document.createElement("p"); giftDescription.className = "gift-description gist0description"; giftDescription.textContent = descriptionText; const cardContent = [giftName, giftStatus, giftDescription]; if (gift.link && gift.link !== "None") { const giftLink = document.createElement("a"); giftLink.className = "gift-link"; giftLink.href = gift.link; giftLink.target = "_blank"; giftLink.rel = "noopener noreferrer"; giftLink.textContent = "Открыть ссылку"; cardContent.push(giftLink); } const booking = document.createElement("div"); booking.className = "booking"; const bookBtn = document.createElement("button"); bookBtn.className = "book-btn"; bookBtn.dataset.id = gift.id; bookBtn.textContent = booked ? "Снять бронь" : "Забронировать"; booking.appendChild(bookBtn); cardDetails.append(...cardContent, booking); cardInner.append(cardImage, cardDetails); card.append(badge, cardInner); container.appendChild(card); }); attach(); observe(); } function attach() { container.querySelectorAll(".book-btn").forEach(btn => { btn.onclick = async () => { const id = +btn.dataset.id; const gift = giftsData.find(g => g.id === id); const latestBookings = await loadBookings(); bookings = latestBookings; if (isBooked(id)) { const name = await openModal("Введите имя для снятия брони"); if (!name) return; if (bookings[id]?.name !== name) { alert("Имя не совпадает с бронью. Обновите страницу и попробуйте ещё раз."); return; } unbook(id); await saveBookings(); render(); } else { if (bookings[id]) { alert("Этот подарок уже забронирован. Обновите страницу и попробуйте ещё раз."); return; } const name = await openModal("Введите имя для бронирования"); if (!name) return; book(id, name); await saveBookings(); render(); } }; }); } function observe() { const cards = document.querySelectorAll(".gift-card"); const io = new IntersectionObserver(entries => { entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add("visible"); io.unobserve(e.target); } }); }, { threshold: 0.1 }); cards.forEach(c => io.observe(c)); } async function init() { await loadBookings(); render(); } init(); })();