limoncello

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

app.js (3862B)


      1 'use strict';
      2 
      3 let daysOffset  = 0;
      4 
      5 // Month (calendar) view
      6 let monthOffset = 0; // 0 = current month, -1 = previous month, 1 = next month, ...
      7 
      8 // Labels
      9 
     10 function updateDaysLabel() {
     11   const el  = document.getElementById('days-offset-label');
     12   const fwd = document.getElementById('days-forward-btn');
     13   el.textContent = daysOffset === 0 ? 'Today ±2' : daysOffset + 'd back';
     14   fwd.disabled   = daysOffset <= 0;
     15 }
     16 
     17 // Tiles
     18 
     19 async function fetchHTML(url, containerId) {
     20   const res  = await fetch(url);
     21   const html = await res.text();
     22   document.getElementById(containerId).innerHTML = html;
     23 }
     24 
     25 async function refreshAllTiles() {
     26   await Promise.all([
     27     fetchHTML('/tiles/days?offset=' + daysOffset, 'days-row'),
     28     loadMonth(),
     29     fetchHTML('/summary', 'summary'),
     30   ]);
     31 }
     32 
     33 // Navigation
     34 
     35 async function shiftDays(dir) {
     36   const next = daysOffset + dir;
     37   if (next < 0) return;
     38   daysOffset = next;
     39   updateDaysLabel();
     40   await fetchHTML('/tiles/days?offset=' + daysOffset, 'days-row');
     41 }
     42 
     43 // Month (calendar)
     44 
     45 // Fetches the full calendar grid + label for the currently selected
     46 // monthOffset and swaps it in, updating the Prev/Next button state.
     47 async function loadMonth() {
     48   const res  = await fetch('/tiles/month?offset=' + monthOffset);
     49   const data = await res.json();
     50   document.getElementById('month-label').textContent = data.label;
     51   document.getElementById('month-grid').innerHTML     = data.grid;
     52   document.getElementById('month-next-btn').disabled  = data.next_disabled;
     53 }
     54 
     55 async function shiftMonth(dir) {
     56   monthOffset += dir;
     57   await loadMonth();
     58 }
     59 
     60 // Days
     61 
     62 function openDay(date) {
     63   fetch('/modal?date=' + date)
     64     .then(r => r.text())
     65     .then(html => {
     66       document.body.insertAdjacentHTML('beforeend', html);
     67       updatePreview();
     68       document.getElementById('drink-volume').addEventListener('change', updatePreview);
     69       document.getElementById('drink-abv').addEventListener('input', updatePreview);
     70     });
     71 }
     72 
     73 function closeModal(e) {
     74   if (e.target.id === 'day-modal') {
     75     document.getElementById('day-modal').remove();
     76   }
     77 }
     78 
     79 function refreshModal(date) {
     80   const modal = document.getElementById('day-modal');
     81   if (!modal) return;
     82   fetch('/modal?date=' + date)
     83     .then(r => r.text())
     84     .then(html => {
     85       modal.remove();
     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 updatePreview() {
     94   const volEl = document.getElementById('drink-volume');
     95   const abvEl = document.getElementById('drink-abv');
     96   const pre   = document.getElementById('abv-preview');
     97   if (!volEl || !abvEl || !pre) return;
     98   const ml  = parseInt(volEl.value, 10);
     99   const abv = parseFloat(abvEl.value);
    100   if (ml > 0 && abv > 0) {
    101     const u = (ml * abv / 1000).toFixed(2);
    102     pre.textContent = `= ${u} unit${u === '1.00' ? '' : 's'}`;
    103   } else {
    104     pre.textContent = '';
    105   }
    106 }
    107 
    108 // Drinks
    109 
    110 function post(url, params) {
    111   return fetch(url, {
    112     method: 'POST',
    113     headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    114     body: new URLSearchParams(params).toString(),
    115   });
    116 }
    117 
    118 async function addDrink(date) {
    119   const ml  = document.getElementById('drink-volume').value;
    120   const abv = document.getElementById('drink-abv').value;
    121   if (!ml || !abv || parseFloat(abv) <= 0) return;
    122   await post('/drink/add', { date, volume_ml: ml, abv });
    123   refreshModal(date);
    124   refreshAllTiles();
    125 }
    126 
    127 async function removeDrink(date, key) {
    128   await post('/drink/remove', { date, key });
    129   refreshModal(date);
    130   refreshAllTiles();
    131 }
    132 
    133 async function adjustDrink(date, key, delta) {
    134   await post('/drink/adjust', { date, key, delta });
    135   refreshModal(date);
    136   refreshAllTiles();
    137 }
    138 
    139 // Init
    140 updateDaysLabel();