all repos — cameraman @ 64c0d4a54d3bd2993ef397be3d2e54270a71c590

templates/index.html (view raw)

  1<!DOCTYPE html>
  2<html>
  3
  4<head>
  5    <title>Ricorrenze</title>
  6    <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%2210 0 100 100%22><text y=%22.90em%22 font-size=%2290%22>📅</text></svg>"></link>
  7    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
  8    <style>
  9        :root {
 10            color-scheme: light dark;
 11            --text: #000;
 12            --bg: #fff;
 13            --green: #4CAF50;
 14            --hover-green: #45a049;
 15            --red: #af4c4c;
 16            --hover-red: #a14545;
 17        }
 18
 19        @media (prefers-color-scheme: dark) {
 20            :root {
 21                color-scheme: dark light;
 22                --text: #fff;
 23                --bg: #121212;
 24                --green: #265929;
 25                --hover-green: #214d23;
 26                --red: #592626;
 27                --hover-red: #4d2121;
 28            }
 29        }
 30
 31        body {
 32            font-family: Arial, sans-serif;
 33            background-color: var(--bg);
 34            color: var(--text);
 35        }
 36
 37        table {
 38            width: 100%;
 39            border-collapse: collapse;
 40        }
 41
 42        table,
 43        th,
 44        td {
 45            border: 1px solid var(--text);
 46        }
 47
 48        th,
 49        td {
 50            padding: 10px;
 51            text-align: left;
 52        }
 53
 54        th {
 55            background-color: var(--bg);
 56        }
 57
 58        .next {
 59            background-color: var(--red);
 60            color: white;
 61        }
 62
 63        .date-inputs {
 64            display: flex;
 65            align-items: center;
 66            border: none;
 67            gap: 2px;
 68        }
 69        .small-input {
 70            width: 32px;
 71        }
 72        .big-input {
 73            width: 100%;
 74        }
 75
 76        .actions i {
 77            cursor: pointer;
 78            padding-right: 5px;
 79        }
 80
 81        .cute-button {
 82            margin-top: 20px;
 83            background-color: var(--green);
 84            border: none;
 85            color: white;
 86            padding: 10px 20px;
 87            text-align: center;
 88            text-decoration: none;
 89            display: inline-block;
 90            font-size: 16px;
 91            margin: 4px 2px;
 92            transition-duration: 0.4s;
 93            cursor: pointer;
 94            border-radius: 12px;
 95        }
 96
 97        .cute-button:hover {
 98            background-color: var(--hover-green);
 99        }
100
101        .cute-button-red {
102            background-color: var(--red);
103        }
104
105        .cute-button-red:hover {
106            background-color: var(--hover-red);
107        }
108
109        .hidden {
110            display: none;
111        }
112    </style>
113</head>
114
115<body>
116    <h1>Ricorrenze</h1>
117    <table id="main-table">
118        <tr>
119            <th data-field="name">Nome</th>
120            <th data-field="description">Descrizione</th>
121            <th data-field="date">Data (gg/mm)</th>
122            <th data-field="notify">Notifica</th>
123            <th data-field="notified">Inviata</th>
124            <th data-field="actions">Azioni</th>
125        </tr>
126        {{ range .Occurrences }}
127        <tr id="occurrence-{{ .ID }}">
128            <td data-field="name">{{ .Name }}</td>
129            <td data-field="description">{{ .Description }}</td>
130            <td data-field="date">{{ padZero .Day }}/{{ padZero .Month }}</td>
131            <td data-field="notify"><input type="checkbox" {{if .Notify}}checked{{end}} disabled></td>
132            <td data-field="notified"><input type="checkbox" {{if .Notified}}checked{{end}} disabled></td>
133            <td data-field="actions" class="actions">
134                <i class="fas fa-edit" title="Modifica" onclick="editOccurrence('{{ .ID }}')"></i>
135                <i class="fas fa-trash-alt" title="Elimina" onclick="deleteOccurrence('{{ .ID }}')"></i>
136            </td>
137        </tr>
138        {{ end }}
139        <tr id="occurrence-none" class="hidden">
140            <td colspan="6">Nessuna ricorrenza.</td>
141        </tr>
142    </table>
143    <div style="margin-top: 10px; text-align: center;">
144        <button id="add-row-button" class="cute-button" onclick="addNewOccurrenceRow()">
145            <i class="fas fa-plus"></i> Aggiungi</button>
146        <button id="save-row-button" class="cute-button hidden" onclick="saveOccurrence('0')">
147            <i class="fas fa-save"></i> Salva
148        </button>
149        <button id="cancel-row-button" class="cute-button cute-button-red hidden" onclick="cancelNewOccurrence()">
150            <i class="fas fa-times"></i> Annulla
151        </button>
152    </div>
153
154    <script>
155        const hiddenClass = 'hidden';
156        const addButton = document.getElementById('add-row-button');
157        const saveButton = document.getElementById('save-row-button');
158        const cancelButton = document.getElementById('cancel-row-button');
159        const mainTable = document.getElementById('main-table');
160        const noneRow = document.getElementById('occurrence-none');
161        
162        const dataError = 'Controlla che i dati (e le date) siano corretti.';
163
164        let currentNext = null;
165
166        function updateRowDisplay() {
167            const rows = mainTable.getElementsByTagName('tr');
168            const l = rows.length
169            if (l === 2) {
170                noneRow.classList.remove(hiddenClass);
171                return;
172            }
173            noneRow.classList.add(hiddenClass);
174            findNextOccurrence();
175        }
176
177        function deleteOccurrence(id) {
178            if (confirm('Sei sicuro di voler eliminare questa ricorrenza?')) {
179                fetch(`/occurrences/${id}`, {
180                    method: 'DELETE'
181                })
182                    .then(response => {
183                        if (!response.ok) {
184                            console.error('Error:', response.status);
185                            alert('Eliminazione fallita.');
186                            return;
187                        }
188                        const deletedRow = document.getElementById(`occurrence-${id}`);
189                        deletedRow.parentElement.removeChild(deletedRow);
190                        updateRowDisplay();
191                    })
192                    .catch(error => {
193                        console.error('Error:', error);
194                        alert(dataError);
195                    });
196            }
197        }
198
199        function padNumber(input, n=2) {
200            return Math.max(1, Math.min(input, 12)).toString().padStart(n, '0');
201        }
202
203        function createRow(id, name, description, day, month, notify, notified) {
204            return `
205                <td data-field="name">${name}</td>
206                <td data-field="description">${description}</td>
207                <td data-field="date">${padNumber(day)}/${padNumber(month)}</td>
208                <td data-field="notify"><input type="checkbox" ${notify ? 'checked' : ''} disabled></td>
209                <td data-field="notified"><input type="checkbox" ${notified ? 'checked' : ''} disabled></td>
210                <td data-field="actions" class="actions">
211                    <i class="fas fa-edit" title="Edit" onclick="editOccurrence(${id})"></i>
212                    <i class="fas fa-trash-alt" title="Delete" onclick="deleteOccurrence(${id})"></i>
213                </td>
214            `;
215        }
216
217        function createInputFields(id, name, description, day, month, notify, notified, isNew) {
218            return `
219                <td><input class="big-input" type="text" value="${name || ''}" id="name-${id}"></td>
220                <td><input class="big-input" type="text" value="${description || ''}" id="description-${id}"></td>
221                <td class="date-inputs"><input type="number" value="${day || ''}" id="day-${id}" class="small-input" min="1" max="31" onchange="this.value = padNumber(this.value);"> /<input type="number" value="${month || ''}" id="month-${id}" class="small-input" min="1" max="12" onchange="this.value = padNumber(this.value);"></td>
222                <td><input type="checkbox" id="notify-${id}" ${notify ? 'checked' : ''}></td>
223                <td><input type="checkbox" id="notified-${id}" ${notified ? 'checked' : 'disabled'}></td>
224                <td class="actions">
225                    ${isNew ? '' : `
226                    <i class="fas fa-save" title="Save" onclick="saveOccurrence(${id})"></i>
227                    <i class="fas fa-times" title="Cancel" onclick="cancelEdit(${id}, '${name}', '${description}', ${day}, ${month}, ${notify})"></i>
228                    `}
229                </td>
230            `;
231        }
232
233        function editOccurrence(id) {
234            const row = document.getElementById(`occurrence-${id}`);
235            const cells = row.getElementsByTagName('td');
236
237            const name = cells[0].innerText;
238            const description = cells[1].innerText;
239            const [day, month] = cells[2].innerText.split('/');
240            const notify = cells[3].getElementsByTagName('input')[0].checked;
241            const notified = cells[4].getElementsByTagName('input')[0].checked;
242
243            row.innerHTML = createInputFields(id, name, description, day, month, notify, notified, false);
244        }
245
246        function cancelEdit(id, name, description, day, month, notify) {
247            const row = document.getElementById(`occurrence-${id}`);
248            row.innerHTML = createRow(id, name, description, day, month, notify);
249        }
250
251        function saveOccurrence(id) {
252            const name = document.getElementById(`name-${id}`).value;
253            const description = document.getElementById(`description-${id}`).value;
254            const day = parseInt(document.getElementById(`day-${id}`).value);
255            const month = parseInt(document.getElementById(`month-${id}`).value);
256            const notify = document.getElementById(`notify-${id}`).checked;
257            const notified = document.getElementById(`notified-${id}`).checked;
258
259            const isNew = id === '0';
260            const updatedData = {
261                id: isNew ? undefined : id,
262                name: name,
263                description: description,
264                month: month,
265                day: day,
266                notify: notify,
267                notified: notified
268            };
269
270            fetch('/occurrences', {
271                method: 'POST',
272                headers: {
273                    'Content-Type': 'application/json'
274                },
275                body: JSON.stringify(updatedData)
276            })
277                .then(response => {
278                    if (!response.ok) {
279                        console.error('Error:', response.status);
280                        alert('Controlla che i campi siano validi.');
281                        return;
282                    }
283                    if (isNew) {
284                        cancelNewOccurrence();
285                        mainTable.insertRow(-1).id = `occurrence-${id}`
286                    }
287                    updateRow(`occurrence-${id}`, response);
288                })
289                .catch(error => {
290                    console.error('Error:', error);
291                    alert(dataError);
292                });
293        }
294
295        function addNewOccurrenceRow() {
296            const newRow = mainTable.insertRow(-1);
297            newRow.id = 'new-occurrence';
298            newRow.innerHTML = createInputFields('0', '', '', '', '', true, false, true);
299
300            hideAddButton();
301            updateRowDisplay();
302        }
303        function hideAddButton() {
304            addButton.classList.add(hiddenClass);
305            saveButton.classList.remove(hiddenClass);
306            cancelButton.classList.remove(hiddenClass);
307        }
308
309        function showAddButton() {
310            addButton.classList.remove(hiddenClass);
311            saveButton.classList.add(hiddenClass);
312            cancelButton.classList.add(hiddenClass);
313        }
314        
315        function updateRow(rowElementId, response) {
316            const newRow = document.getElementById(rowElementId);
317            response.json().then((res) => {
318                newRow.id = `occurrence-${res.id}`;
319                newRow.innerHTML = createRow(res.id, res.name, res.description, res.day, res.month, res.notify, res.notified);
320                updateRowDisplay()
321            });
322        }
323
324        function cancelNewOccurrence() {
325            const newRow = document.getElementById('new-occurrence');
326            newRow.parentNode.removeChild(newRow);
327            showAddButton();
328            updateRowDisplay();
329        }
330
331        function findNextOccurrence() {
332            if (currentNext !== null) {
333                currentNext.classList.remove('next');
334            }
335            const now = new Date();
336            const occurrenceRows = Array.from(mainTable.querySelectorAll("tr[id]:not(#occurrence-none):not(#new-occurrence)"));
337            const occurrences = occurrenceRows.map((row) => {
338                const tds = Array.from(row.getElementsByTagName('td'));
339                
340                const id = row.id.split('-')[1];
341
342                const dateString = tds.find((td) => td.getAttribute('data-field') === 'date').innerText;
343                const [day, month] = dateString.split('/').map((x) => Number(x));
344                const date = new Date(now.getFullYear(), month - 1, day, 23, 59, 59);
345
346                return {id, date}
347            });
348
349            const deltas = occurrences.map((x) => ({...x, distance: x.date - now})).filter((x) => x.distance > 0);
350            if (deltas.length == 0) return;
351
352            const distances = deltas.map((x) => x.distance);
353            const minDistance = Math.min(...distances);
354            const minDelta = deltas.find((x) => x.distance == minDistance);
355            
356            currentNext = occurrenceRows.find((row) => row.id === `occurrence-${minDelta.id}`);
357            currentNext.classList.add('next');
358        }
359
360        updateRowDisplay();
361    </script>
362
363</body>
364
365</html>