QR Code Menü Raindrop ile oluşturma - no code
Merhaba, bu makalede Raindrop.io üzerinden oluşturduğumuz veriyi internet sitemize çekebiliriz, menü görünümü verebiliriz. Veritabanı kullanmadan, ek kod yazmadan hızlıca bir menü oluşturabiliriz.
Bir önceki Google Sheets ile oluşturduğumuz menüde resim ekleme özelliği bulunmuyordu. Bunda ise resim ekleme özelliği mevcut.
Raindrop.io bir yerimi yönetim uygulaması. ademilter.com/bookmarks sayfasında görüp beğendiğim bir yapıyı kendi siteme entegre ederken sadece resim ekleyebildiğimi fark ettim. Aklıma hemen menü yapısı geldi ve hızlıca bir menü oluşturup yayınlamak istedim.
Çalışma Mekanizması #
Raindrop.io üzerinden bir hesap oluşturuyoruz ve menü fotoğraflarını, açıklama ve fiyat bilgilerini ekliyoruz. Bu veriyi api ile servisten alıp JavaScript ile işleyip sayfamızda gösteriyoruz.
1) Raindrop.io Hesap Oluşturma #
- Raindrop.io > Giriş > Yeni Üyelik
2) Api linki oluşturma #
- Raindrop.io Entegrasyon > Geliştiriciler İçin > Oluştur yeni uygulama
- Uygulama adını yaz (örn: x.com) ve Sözleşmeyi kabul et
- Test token oluştur ve kopyala
Test tokenı alıyoruz ve ilgili değişkene tanımlıyoruz.
const accessToken = "TOKEN";
ilgili değişkene tanımlıyoruz.
Daha sonrasında script kodlarımızı tamamlayalım.
3) HTML Yapıyı oluşturma #
Bu kısımda tailwindcss kullanarak hızlıca bir yapı oluşturacağız.
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Restaurant Menu</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="style.css">
</head>
<body class="bg-gray-100">
<div class="overlay"></div>
<div class="flex h-screen overflow-hidden">
<!-- Drawer Menu -->
<div id="drawer" class="fixed lg:relative w-64 h-full bg-white shadow-lg transform -translate-x-full lg:translate-x-0 transition-transform duration-300 ease-in-out">
<div class="bg-gray-600 relative">
<h2 class="text-white text-xl p-4">Kategoriler</h2>
<button id="close-button" class="text-white text-2xl focus:outline-none absolute right-4 top-0 translate-y-2/4">
✕
</button>
</div>
<div class="px-4 pb-4">
<nav id="categoryList" class="space-y-2">
<!-- Kategoriler -->
</nav>
</div>
</div>
<div class="flex-1 overflow-y-auto">
<header class="bg-slate-100 shadow-md lg:flex items-center justify-between sticky top-0 z-30">
<div class="relative h-14 flex items-center justify-center">
<button id="menuToggle" class="p-4 lg:hidden absolute left-3">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
<div class="text-2xl font-semibold text-center">Restaurant Menu</div>
</div>
</header>
<main class="container mx-auto px-4">
<div id="menuContent" class="space-y-8">
<!-- Ürünler -->
</div>
</main>
</div>
</div>
<script src="main.js"></script>
</body>
</html>
4) JavaScript #
Bu kısımda chatGPT aktif rol oynamıştır :)
accessToken yerini değiştirmeyi unutmayın.
const accessToken = "TOKEN";
const collectionsUrl = "https://api.raindrop.io/rest/v1/collections";
const drawer = document.getElementById('drawer');
const menuToggle = document.getElementById('menuToggle');
const categoryList = document.getElementById('categoryList');
const menuContent = document.getElementById('menuContent');
const body = document.body;
// Toggle mobile menu
menuToggle.addEventListener('click', () => {
drawer.classList.toggle('translate-x-0');
drawer.classList.toggle('-translate-x-full');
body.classList.toggle('drawer-open');
});
// Close drawer when clicking overlay or close button
const closeDrawer = () => {
drawer.classList.remove('translate-x-0');
drawer.classList.add('-translate-x-full');
body.classList.remove('drawer-open');
};
document.querySelector('.overlay').addEventListener('click', closeDrawer);
document.getElementById('close-button').addEventListener('click', closeDrawer);
// Fetch and render categories and items
async function fetchData() {
try {
const response = await fetch(collectionsUrl, {
headers: { Authorization: `Bearer ${accessToken}` }
});
const data = await response.json();
const collections = data.items || [];
// Sort collections by title
collections.sort((a, b) => a.title.localeCompare(b.title));
// Render categories in drawer
categoryList.innerHTML = collections.map(collection => `
<a href="#${collection._id}"
class="category-link block px-4 py-2 rounded-lg hover:bg-gray-100 transition-colors"
data-category-id="${collection._id}">
${collection.title}
<span class="text-gray-500 text-sm">(${collection.count})</span>
</a>
`).join('');
// Fetch and render items for each category
for (const collection of collections) {
const itemsResponse = await fetch(
`https://api.raindrop.io/rest/v1/raindrops/${collection._id}`,
{ headers: { Authorization: `Bearer ${accessToken}` } }
);
const itemsData = await itemsResponse.json();
const items = itemsData.items || [];
console.log(items);
const categorySection = document.createElement('section');
categorySection.id = collection._id;
categorySection.innerHTML = `
<h2 class="text-2xl font-bold mb-4 pt-4">${collection.title}</h2>
<div class="space-y-4">
${items.map(item => `
<article class="menu-item">
<div class="menu-item-image ${!item.cover ? 'placeholder-image' : ''}">
${item.cover
? `<img src="${item.cover}" alt="${item.title}" class="menu-item-image">`
: `<svg class="w-12 h-12 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
</svg>`
}
</div>
<div class="menu-item-content">
<div class="menu-item-header">
<h3 class="menu-item-title">${item.title}</h3>
<span class="menu-item-price">${item.excerpt ? item.excerpt : ''}₺</span>
</div>
<p class="menu-item-desc">${item.note ? item.note : ''}</p>
</div>
</article>
`).join('')}
</div>
`;
menuContent.appendChild(categorySection);
}
// Category highlighting and smooth scroll
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
document.querySelectorAll('.category-link').forEach(link => {
link.classList.toggle('active',
link.getAttribute('data-category-id') === entry.target.id);
});
}
});
}, {
root: null,
rootMargin: '0px',
threshold: 0.3
});
document.querySelectorAll('section').forEach(section => {
observer.observe(section);
});
// Smooth scroll to category
document.querySelectorAll('.category-link').forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const targetId = link.getAttribute('href').substring(1);
const targetSection = document.getElementById(targetId);
targetSection.scrollIntoView({ behavior: 'smooth' });
if (window.innerWidth < 1024) {
drawer.classList.remove('translate-x-0');
drawer.classList.add('-translate-x-full');
body.classList.remove('drawer-open');
}
});
});
} catch (error) {
console.error('Error fetching data:', error);
}
}
fetchData();
Ekran Görüntüsü #
Örnek Link #
QR Code Menü Raindrop ile - Örnek
Bonus: Yayınlamak #
Eğer bir hostinginiz yoksa ve hızlıca yayınlamak istiyorsanız aşağıdaki servislerden birini kullanabilirsiniz.
GitHub > Yeni Repo Oluştur > Kodları Yükle > GitHub Pages ile yayınla
Netlify > Yeni Site Oluştur > GitHub > Deploy
Vercel > Yeni Site Oluştur > GitHub > Deploy
GitHub Repo #
github/google-sheets-qrcode-menu (★++)
LinkedIn Paylaşımı #
Bonus: QR Code Menü Google Sheets ile oluşturma - no code #
github/google-sheets-qrcode-menu (★++)
İyi çalışmalar. 🚀