app.js (4753B)
1 'use strict'; 2 3 let daysOffset = 0; 4 let weekOffset = 0; 5 let monthOffset = 0; 6 7 // Labels 8 9 function updateDaysLabel() { 10 const el = document.getElementById('days-offset-label'); 11 const fwd = document.getElementById('days-forward-btn'); 12 el.textContent = daysOffset === 0 ? 'Today ±2' : daysOffset + 'd back'; 13 fwd.disabled = daysOffset <= 0; 14 } 15 16 function updateWeekLabel() { 17 const el = document.getElementById('week-offset-label'); 18 const fwd = document.getElementById('week-forward-btn'); 19 if (weekOffset === 0) el.textContent = 'This week'; 20 else if (weekOffset === 1) el.textContent = 'Last week'; 21 else el.textContent = weekOffset + ' weeks ago'; 22 fwd.disabled = weekOffset <= 0; 23 } 24 25 function updateMonthLabel() { 26 const el = document.getElementById('month-offset-label'); 27 const fwd = document.getElementById('month-forward-btn'); 28 if (monthOffset === 0) el.textContent = 'This month'; 29 else if (monthOffset === 1) el.textContent = 'Last month'; 30 else el.textContent = monthOffset + ' months ago'; 31 fwd.disabled = monthOffset <= 0; 32 } 33 34 // Tiles 35 36 async function fetchHTML(url, containerId) { 37 const res = await fetch(url); 38 const html = await res.text(); 39 document.getElementById(containerId).innerHTML = html; 40 } 41 42 async function refreshAllTiles() { 43 await Promise.all([ 44 fetchHTML('/tiles/days?offset=' + daysOffset, 'days-row'), 45 fetchHTML('/tiles/week?offset=' + weekOffset, 'week-row'), 46 fetchHTML('/tiles/month?offset=' + monthOffset, 'month-grid'), 47 ]); 48 } 49 50 // Navigation 51 52 async function shiftDays(dir) { 53 const next = daysOffset + dir; 54 if (next < 0) return; 55 daysOffset = next; 56 updateDaysLabel(); 57 await fetchHTML('/tiles/days?offset=' + daysOffset, 'days-row'); 58 } 59 60 async function shiftWeek(dir) { 61 const next = weekOffset + dir; 62 if (next < 0) return; 63 weekOffset = next; 64 updateWeekLabel(); 65 await fetchHTML('/tiles/week?offset=' + weekOffset, 'week-row'); 66 document.getElementById('week-label').textContent = 67 await (await fetch('/label/week?offset=' + weekOffset)).text(); 68 } 69 70 async function shiftMonth(dir) { 71 const next = monthOffset + dir; 72 if (next < 0) return; 73 monthOffset = next; 74 updateMonthLabel(); 75 await fetchHTML('/tiles/month?offset=' + monthOffset, 'month-grid'); 76 document.getElementById('month-label').textContent = 77 await (await fetch('/label/month?offset=' + monthOffset)).text(); 78 } 79 80 // Days 81 82 function openDay(date) { 83 fetch('/modal?date=' + date) 84 .then(r => r.text()) 85 .then(html => { 86 document.body.insertAdjacentHTML('beforeend', html); 87 updatePreview(); 88 document.getElementById('drink-volume').addEventListener('change', updatePreview); 89 document.getElementById('drink-abv').addEventListener('input', updatePreview); 90 }); 91 } 92 93 function closeModal(e) { 94 if (e.target.id === 'day-modal') { 95 document.getElementById('day-modal').remove(); 96 } 97 } 98 99 function refreshModal(date) { 100 const modal = document.getElementById('day-modal'); 101 if (!modal) return; 102 fetch('/modal?date=' + date) 103 .then(r => r.text()) 104 .then(html => { 105 modal.remove(); 106 document.body.insertAdjacentHTML('beforeend', html); 107 updatePreview(); 108 document.getElementById('drink-volume').addEventListener('change', updatePreview); 109 document.getElementById('drink-abv').addEventListener('input', updatePreview); 110 }); 111 } 112 113 function updatePreview() { 114 const volEl = document.getElementById('drink-volume'); 115 const abvEl = document.getElementById('drink-abv'); 116 const pre = document.getElementById('abv-preview'); 117 if (!volEl || !abvEl || !pre) return; 118 const ml = parseInt(volEl.value, 10); 119 const abv = parseFloat(abvEl.value); 120 if (ml > 0 && abv > 0) { 121 const u = (ml * abv / 1000).toFixed(2); 122 pre.textContent = `= ${u} unit${u === '1.00' ? '' : 's'}`; 123 } else { 124 pre.textContent = ''; 125 } 126 } 127 128 // Drinks 129 130 function post(url, params) { 131 return fetch(url, { 132 method: 'POST', 133 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 134 body: new URLSearchParams(params).toString(), 135 }); 136 } 137 138 async function addDrink(date) { 139 const ml = document.getElementById('drink-volume').value; 140 const abv = document.getElementById('drink-abv').value; 141 if (!ml || !abv || parseFloat(abv) <= 0) return; 142 await post('/drink/add', { date, volume_ml: ml, abv }); 143 refreshModal(date); 144 refreshAllTiles(); 145 } 146 147 async function removeDrink(date, key) { 148 await post('/drink/remove', { date, key }); 149 refreshModal(date); 150 refreshAllTiles(); 151 } 152 153 async function adjustDrink(date, key, delta) { 154 await post('/drink/adjust', { date, key, delta }); 155 refreshModal(date); 156 refreshAllTiles(); 157 } 158 159 // Init 160 updateDaysLabel(); 161 updateWeekLabel(); 162 updateMonthLabel();