153 lines
4.5 KiB
HTML
153 lines
4.5 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Costco Grocery List</title>
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
margin: 0;
|
|
padding: 1em;
|
|
background: #f8f9fa;
|
|
}
|
|
|
|
.container {
|
|
max-width: 480px;
|
|
margin: auto;
|
|
background: white;
|
|
padding: 1em;
|
|
border-radius: 8px;
|
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
h1 {
|
|
text-align: center;
|
|
font-size: 1.5em;
|
|
}
|
|
|
|
input,
|
|
button,
|
|
select {
|
|
font-size: 1em;
|
|
margin: 0.3em 0;
|
|
padding: 0.5em;
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
ul {
|
|
list-style: none;
|
|
padding: 0;
|
|
}
|
|
|
|
li {
|
|
padding: 0.5em;
|
|
background: #e9ecef;
|
|
margin-bottom: 0.5em;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
li:hover {
|
|
background: #dee2e6;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="container">
|
|
<h1>Costco Grocery List</h1>
|
|
<input type="text" id="itemInput" placeholder="Item name" list="suggestions" autocomplete="off">
|
|
<!-- <ul id="suggestionList" style="display:none; background:#fff; border:1px solid #ccc; max-height:150px; overflow-y:auto; position:absolute; z-index:1000; width:calc(100% - 2em);"></ul> -->
|
|
<ul id="suggestionList"
|
|
style="display:none; background:#fff; border:1px solid #ccc; max-height:150px; overflow-y:auto; position:absolute; z-index:1000; left:1em; right:1em;">
|
|
</ul>
|
|
<input type="number" id="quantityInput" placeholder="Quantity" value="1" min="1">
|
|
<button onclick="addItem()">Add Item</button>
|
|
<ul id="groceryList"></ul>
|
|
</div>
|
|
|
|
<script>
|
|
async function loadList() {
|
|
const res = await fetch('/list');
|
|
const list = await res.json();
|
|
const ul = document.getElementById('groceryList');
|
|
ul.innerHTML = '';
|
|
list.forEach(item => {
|
|
const li = document.createElement('li');
|
|
li.textContent = `${item.item_name} (${item.quantity})`;
|
|
li.onclick = () => markBought(item.id);
|
|
ul.appendChild(li);
|
|
});
|
|
}
|
|
|
|
async function suggest(query) {
|
|
if (!query.trim()) return document.getElementById('suggestionList').style.display = 'none';
|
|
const res = await fetch('/suggest?query=' + encodeURIComponent(query));
|
|
const items = await res.json();
|
|
const suggestionBox = document.getElementById('suggestionList');
|
|
suggestionBox.innerHTML = '';
|
|
items.forEach(i => {
|
|
const li = document.createElement('li');
|
|
li.textContent = i;
|
|
li.style.padding = '0.5em';
|
|
li.onclick = () => {
|
|
document.getElementById('itemInput').value = i;
|
|
suggestionBox.style.display = 'none';
|
|
};
|
|
suggestionBox.appendChild(li);
|
|
});
|
|
suggestionBox.style.display = items.length ? 'block' : 'none';
|
|
}
|
|
|
|
document.getElementById('itemInput').addEventListener('input', (e) => suggest(e.target.value));
|
|
|
|
async function addItem() {
|
|
const item = document.getElementById('itemInput').value.trim();
|
|
const quantity = parseInt(document.getElementById('quantityInput').value, 10);
|
|
if (!item || isNaN(quantity)) return;
|
|
|
|
const check = await fetch('/list');
|
|
const existing = await check.json();
|
|
const match = existing.find(i => i.item_name.toLowerCase() === item.toLowerCase());
|
|
|
|
if (match) {
|
|
const confirmAdd = confirm("Item already exists. Add more?");
|
|
if (!confirmAdd) return;
|
|
await fetch('/add', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ item_name: item, quantity: match.quantity + quantity })
|
|
});
|
|
} else {
|
|
await fetch('/add', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ item_name: item, quantity })
|
|
});
|
|
}
|
|
|
|
document.getElementById('itemInput').value = '';
|
|
document.getElementById('quantityInput').value = 1;
|
|
document.getElementById('suggestionList').style.display = 'none';
|
|
loadList();
|
|
}
|
|
|
|
async function markBought(id) {
|
|
const confirmBuy = confirm("Mark this item as bought?");
|
|
if (!confirmBuy) return;
|
|
await fetch('/mark-bought', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ id })
|
|
});
|
|
loadList();
|
|
}
|
|
|
|
loadList();
|
|
</script>
|
|
</body>
|
|
|
|
</html> |