You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
381 lines
14 KiB
381 lines
14 KiB
// IndexedDB Funktionen
|
|
const dbName = 'myDB';
|
|
const dbVersion = 2;
|
|
let db;
|
|
|
|
|
|
|
|
//
|
|
// GRUND FUNKTIONEN FÜR DIE DATENBANK
|
|
//
|
|
|
|
|
|
// IndexedDB initialisieren
|
|
function initDB() {
|
|
return new Promise((resolve, reject) => {
|
|
const request = indexedDB.open(dbName, dbVersion);
|
|
|
|
request.onupgradeneeded = function (event) {
|
|
db = event.target.result;
|
|
if (!db.objectStoreNames.contains('hours')) {
|
|
const store = db.createObjectStore('hours', {
|
|
keyPath: 'id',
|
|
autoIncrement: true,
|
|
});
|
|
|
|
// Spalten definieren
|
|
store.createIndex('createdate', 'createdate', { unique: false });
|
|
store.createIndex('date', 'date', { unique: false });
|
|
store.createIndex('duration', 'duration', { unique: false });
|
|
store.createIndex('type', 'type', { unique: false });
|
|
store.createIndex('status', 'status', { unique: false });
|
|
store.createIndex('sondertext', 'sondertext', { unique: false });
|
|
}
|
|
if (!db.objectStoreNames.contains('settings')) {
|
|
const settingsT = db.createObjectStore('settings', {
|
|
keyPath: 'id',
|
|
autoIncrement: true,
|
|
})
|
|
|
|
settingsT.createIndex('SettingsID', 'SettingsID', { unique: false });
|
|
settingsT.createIndex('SettingsValue', 'SettingsValue', { unique: false });
|
|
}
|
|
};
|
|
|
|
request.onsuccess = function (event) {
|
|
db = event.target.result;
|
|
resolve(db);
|
|
};
|
|
|
|
request.onerror = function (event) {
|
|
console.error('IndexedDB Fehler:', event.target.errorCode);
|
|
reject(event.target.errorCode);
|
|
};
|
|
});
|
|
}
|
|
|
|
// Neuer Eintrag in die IndexedDB
|
|
function addEntry(data, table) {
|
|
return new Promise((resolve, reject) => {
|
|
const transaction = db.transaction([table], 'readwrite');
|
|
const store = transaction.objectStore(table);
|
|
|
|
data.createdate = new Date();
|
|
const request = store.add(data);
|
|
|
|
request.onsuccess = function () {
|
|
resolve(request.result);
|
|
};
|
|
|
|
request.onerror = function (event) {
|
|
reject('Fehler beim Hinzufügen des Eintrags:', event.target.error);
|
|
};
|
|
});
|
|
}
|
|
|
|
// Eintrag aktualisieren
|
|
function updateEntry(id, table, updatedData) {
|
|
return new Promise((resolve, reject) => {
|
|
const transaction = db.transaction([table], 'readwrite');
|
|
const store = transaction.objectStore(table);
|
|
|
|
const getRequest = store.get(id);
|
|
|
|
getRequest.onsuccess = function (event) {
|
|
const data = event.target.result;
|
|
Object.assign(data, updatedData);
|
|
data.createdate = new Date(); // Aktualisiere das Änderungsdatum
|
|
|
|
const updateRequest = store.put(data);
|
|
|
|
updateRequest.onsuccess = function () {
|
|
resolve(data);
|
|
};
|
|
|
|
updateRequest.onerror = function (event) {
|
|
reject('Fehler beim Aktualisieren des Eintrags:', event.target.error);
|
|
};
|
|
};
|
|
|
|
getRequest.onerror = function (event) {
|
|
reject('Fehler beim Abrufen des Eintrags:', event.target.error);
|
|
};
|
|
});
|
|
}
|
|
|
|
// Eintrag löschen
|
|
function deleteEntry(id) {
|
|
return new Promise((resolve, reject) => {
|
|
const transaction = db.transaction(['hours'], 'readwrite');
|
|
const store = transaction.objectStore('hours');
|
|
|
|
const request = store.delete(id);
|
|
|
|
request.onsuccess = function () {
|
|
resolve(`Eintrag mit ID ${id} gelöscht.`);
|
|
};
|
|
|
|
request.onerror = function (event) {
|
|
reject('Fehler beim Löschen des Eintrags:', event.target.error);
|
|
};
|
|
});
|
|
}
|
|
|
|
// Einträge abrufen
|
|
function getAllEntries(table) {
|
|
return new Promise((resolve, reject) => {
|
|
const transaction = db.transaction([table], 'readonly');
|
|
const store = transaction.objectStore(table);
|
|
|
|
const request = store.getAll();
|
|
|
|
request.onsuccess = function () {
|
|
resolve(request.result);
|
|
};
|
|
|
|
request.onerror = function (event) {
|
|
reject('Fehler beim Abrufen der Einträge:', event.target.error);
|
|
};
|
|
});
|
|
}
|
|
|
|
// Neuen Eintrag verschlüsselt speichern
|
|
async function addEncryptedEntry(data) {
|
|
const key = await generateKey();
|
|
const { iv, encryptedData } = await encryptData(key, JSON.stringify(data));
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const transaction = db.transaction(['hours'], 'readwrite');
|
|
const store = transaction.objectStore('hours');
|
|
|
|
const request = store.add({
|
|
iv: Array.from(iv), // IV muss auch gespeichert werden
|
|
encryptedData: Array.from(new Uint8Array(encryptedData)), // Encrypted data als Uint8Array speichern
|
|
});
|
|
|
|
request.onsuccess = function () {
|
|
resolve(request.result);
|
|
};
|
|
|
|
request.onerror = function (event) {
|
|
reject('Fehler beim Hinzufügen des verschlüsselten Eintrags:', event.target.error);
|
|
};
|
|
});
|
|
}
|
|
|
|
// Verschlüsselten Eintrag entschlüsseln
|
|
async function getDecryptedEntry(id) {
|
|
const key = await generateKey();
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const transaction = db.transaction(['hours'], 'readonly');
|
|
const store = transaction.objectStore('hours');
|
|
|
|
const request = store.get(id);
|
|
|
|
request.onsuccess = async function () {
|
|
const entry = request.result;
|
|
if (entry) {
|
|
const iv = new Uint8Array(entry.iv);
|
|
const encryptedData = new Uint8Array(entry.encryptedData);
|
|
const decryptedData = await decryptData(key, iv, encryptedData);
|
|
resolve(JSON.parse(decryptedData));
|
|
} else {
|
|
reject('Eintrag nicht gefunden');
|
|
}
|
|
};
|
|
|
|
request.onerror = function (event) {
|
|
reject('Fehler beim Abrufen des verschlüsselten Eintrags:', event.target.error);
|
|
};
|
|
});
|
|
}
|
|
|
|
function durationDBtoForm(durationInMin) {
|
|
const hours = Math.floor(durationInMin / 60); // Ganze Stunden berechnen
|
|
const mins = durationInMin % 60; // Verbleibende Minuten berechnen
|
|
|
|
return [hours, mins]
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// KOMBI FUNKTIONEN
|
|
// SIND VOR ALLEM HIER DAMIT SIE ZEITGLEICH GERENDERT WERDEN
|
|
//
|
|
|
|
// Die abgefragte Dauer in Minuten umrechnen und in hh:mm formatieren
|
|
function formatDuration(minutes, shortOutput = false) {
|
|
const hours = Math.floor(minutes / 60); // Ganze Stunden berechnen
|
|
const mins = minutes % 60; // Verbleibende Minuten berechnen
|
|
|
|
if (!shortOutput){
|
|
return `${String(hours).padStart(2, '0')}:${String(mins).padStart(2, '0')}`; // Stunden und Minuten formatieren
|
|
} else {
|
|
return `${String(hours)}:${String(mins)}`.replace(':30', ',5').replace(':0', ''); // Stunden und Minuten formatieren
|
|
}
|
|
}
|
|
|
|
// Datum für die gewünschte Ansicht formatieren
|
|
function formatDate(dateString) {
|
|
const date = new Date(dateString); // Konvertiere in ein Date-Objekt
|
|
const day = String(date.getDate()).padStart(2, '0'); // Tag
|
|
const month = String(date.getMonth() + 1).padStart(2, '0'); // Monat (getMonth() ist 0-basiert)
|
|
const year = String(date.getFullYear()).slice(-2); // Die letzten 2 Ziffern des Jahres
|
|
return `${day}.${month}.${year}`; // Format tt.mm.yy
|
|
}
|
|
|
|
// Einträge nach Monat und/oder Jahr filtern
|
|
function filterEntriesByMonthYear(month, year) {
|
|
return new Promise((resolve, reject) => {
|
|
const transaction = db.transaction(['hours'], 'readonly');
|
|
const store = transaction.objectStore('hours');
|
|
|
|
const request = store.getAll();
|
|
|
|
request.onsuccess = function () {
|
|
const allEntries = request.result;
|
|
|
|
// Filter nach Monat und Jahr
|
|
const filteredEntries = allEntries.filter(entry => {
|
|
const entryDate = new Date(entry.date); // Konvertiere das date-Feld in ein Date-Objekt
|
|
var actualYear = year;
|
|
|
|
const matchesMonth = month ? entryDate.getMonth() + 1 === month : true; // +1 da getMonth() 0-basiert ist
|
|
if (entryDate.getMonth() + 1 >= 9){
|
|
actualYear = actualYear - 1;
|
|
} else {
|
|
actualYear = actualYear;
|
|
};
|
|
const matchesYear = actualYear ? entryDate.getFullYear() === actualYear : true;
|
|
|
|
return matchesMonth && matchesYear;
|
|
});
|
|
|
|
resolve(filteredEntries);
|
|
};
|
|
|
|
request.onerror = function (event) {
|
|
reject('Fehler beim Abrufen der Einträge:', event.target.error);
|
|
};
|
|
});
|
|
}
|
|
|
|
// Tabelle auf dem Dashboard mit Infos füllen
|
|
function fillTableWithEntries(entries) {
|
|
const tableBody = document.getElementById('entriesTableBody');
|
|
|
|
entries.sort((a, b) => new Date(a.date) - new Date(b.date));
|
|
|
|
// Zuerst den Inhalt der Tabelle löschen (falls schon Daten da sind)
|
|
tableBody.innerHTML = '';
|
|
|
|
// Iteriere durch die Einträge und füge Zeilen hinzu
|
|
entries.forEach(entry => {
|
|
const row = document.createElement('tr');
|
|
|
|
//EVENTUELL MAL IN DER ZUKUNFT - UM EINTRÄGE ZU SEITE WISCHEN
|
|
// let startX = 0;
|
|
// let currentRow;
|
|
|
|
// row.addEventListener('touchstart', (e) => {
|
|
// startX = e.touches[0].clientX;
|
|
// currentRow = e.currentTarget;
|
|
// });
|
|
|
|
// row.addEventListener('touchmove', (e) => {
|
|
// const moveX = e.touches[0].clientX;
|
|
// if (startX - moveX > 50) {
|
|
// currentRow.classList.add('swipe-left');
|
|
// }
|
|
// });
|
|
|
|
// // Am Ende des Swipe, zeige den Löschen-Button
|
|
// row.addEventListener('touchend', () => {
|
|
// if (currentRow.classList.contains('swipe-left')) {
|
|
// currentRow.querySelector('.editCell').classList.remove('d-none')
|
|
// currentRow.querySelector('.delCell').classList.remove('d-none')
|
|
// currentRow.querySelector('.duration').classList.add('d-none')
|
|
// currentRow.querySelector('.date').classList.add('d-none')
|
|
// }
|
|
// });
|
|
|
|
// Spalte für die Dauer (duration)
|
|
const durationCell = document.createElement('td');
|
|
durationCell.classList.add('duration')
|
|
durationCell.textContent = formatDuration(entry.duration); // Dauer im hh:mm-Format
|
|
row.appendChild(durationCell);
|
|
|
|
// Spalte für das Datum (date)
|
|
const dateCell = document.createElement('td');
|
|
const entryDate = new Date(entry.date); // Konvertiere in ein Date-Objekt
|
|
dateCell.classList.add('date')
|
|
dateCell.textContent = formatDate(entry.date); // Datum im tt.mm.yy-Format
|
|
row.appendChild(dateCell);
|
|
|
|
// Spalte für den Typ (type) - wenn 3, dann zeige "Beschreibung" an
|
|
const typeCell = document.createElement('td');
|
|
if (entry.type === 3) {
|
|
typeCell.textContent = entry.sondertext; // Bei Type 3 zeige "Beschreibung" an
|
|
} else if (entry.type === 2) {
|
|
typeCell.textContent = 'LDC'; // Ansonsten zeige den Type-Wert
|
|
} else if (entry.type === 1) {
|
|
typeCell.textContent = 'Dienst'; // Ansonsten zeige den Type-Wert
|
|
}
|
|
row.appendChild(typeCell);
|
|
|
|
const editCell = document.createElement('td');
|
|
// editCell.classList.add('editCell')
|
|
// editCell.style = 'background-color: rgb(120,170,86); color: white;'
|
|
editCell.addEventListener('click', ()=>{
|
|
document.getElementById('editEntryID').value = entry.id;
|
|
document.getElementById('editStunden').value = durationDBtoForm(entry.duration)[0];
|
|
document.getElementById('editMinuten').value = durationDBtoForm(entry.duration)[1];
|
|
document.getElementById('editDatum').value = entry.date;
|
|
if (entry.type === 2) {
|
|
document.getElementById('editldcCheck').checked = true;
|
|
} else {
|
|
document.getElementById('editldcCheck').checked = false;
|
|
}
|
|
|
|
if (entry.type === 3) {
|
|
document.getElementById('editsonstigesCheck').checked = true;
|
|
document.getElementById('editsonstigesInput').value = entry.sondertext;
|
|
document.getElementById('edit-sonstiges-text').style.display = 'block';
|
|
} else {
|
|
document.getElementById('editsonstigesCheck').checked = false;
|
|
document.getElementById('editsonstigesInput').value = null;
|
|
document.getElementById('edit-sonstiges-text').style.display = 'none';
|
|
}
|
|
})
|
|
editCell.innerHTML = '<span class="btn btn-sm btn-secondary" data-bs-toggle="modal" data-bs-target="#editEntryModel" style="width: 40px; border-radius: 1rem; transform: rotate(180deg);">✐</span>';
|
|
row.appendChild(editCell)
|
|
|
|
// const delCell = document.createElement('td');
|
|
// delCell.classList.add('d-none')
|
|
// delCell.classList.add('delCell')
|
|
// delCell.style = 'background-color: red; color: white;'
|
|
// delCell.innerHTML = '<p style="margin: 0; padding: 0;">Delete</p>'
|
|
// row.appendChild(delCell)
|
|
|
|
// Füge die erstellte Zeile dem Tabellenkörper hinzu
|
|
tableBody.appendChild(row);
|
|
});
|
|
}
|
|
|
|
// Abgefragte Daten Zeiten summieren und filtern können
|
|
function sumDurations(data, type = null, roundToHalfHour = false) {
|
|
// Optionaler Filter nach dem Typ, falls ein Typ angegeben wurde
|
|
const filteredData = type !== null ? data.filter(item => item.type === type) : data;
|
|
|
|
// Berechnung der Summe der "duration"-Werte
|
|
let totalDuration = filteredData.reduce((sum, item) => sum + item.duration, 0);
|
|
|
|
// Optionales Runden auf halbe Stunden
|
|
if (roundToHalfHour) {
|
|
totalDuration = Math.round(totalDuration / 30) * 30;
|
|
}
|
|
|
|
return totalDuration;
|
|
} |