mirror of
https://github.com/trevor1969/teatracker.git
synced 2026-08-09 10:41:59 +00:00
Clean app.js: Remove all merge conflicts and duplicate classes
Co-authored-by: trevor1969 <trevor1969@users.noreply.github.com>
This commit is contained in:
317
app.js
317
app.js
@ -1,91 +1,14 @@
|
|||||||
// Local File Storage
|
// TeeTracker - Main Application
|
||||||
|
// Version 4.0: Local file storage in data/ directory
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// Configuration
|
||||||
// ============================================
|
// ============================================
|
||||||
|
|
||||||
class LocalFileStorage {
|
const STORAGE_KEY_TEAS = 'teatracker_teas';
|
||||||
constructor() {
|
const STORAGE_KEY_ENTRIES = 'teatracker_entries';
|
||||||
this.basePath = DATA_DIRECTORY;
|
const DATA_DIRECTORY = 'data/';
|
||||||
this.connected = false;
|
|
||||||
this.lastError = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async testConnection() {
|
|
||||||
try {
|
|
||||||
// Try to create a test file
|
|
||||||
const testFile = this.basePath + 'test_connection.txt';
|
|
||||||
const response = await fetch(testFile, {
|
|
||||||
method: 'PUT',
|
|
||||||
body: 'test'
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
// Clean up test file
|
|
||||||
await fetch(testFile, { method: 'DELETE' });
|
|
||||||
this.connected = true;
|
|
||||||
this.lastError = null;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
this.connected = false;
|
|
||||||
this.lastError = `HTTP Error: ${response.status}`;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.connected = false;
|
|
||||||
this.lastError = error.message;
|
|
||||||
console.log('Local file storage not available:', error.message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadFile(filename) {
|
|
||||||
try {
|
|
||||||
const url = this.basePath + filename;
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: 'GET'
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
const text = await response.text();
|
|
||||||
return text.trim() === '' ? [] : (text ? JSON.parse(text) : null);
|
|
||||||
} else if (response.status === 404) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
} catch (error) {
|
|
||||||
console.log(`Error loading ${filename}:`, error.message);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveFile(filename, data) {
|
|
||||||
try {
|
|
||||||
const url = this.basePath + filename;
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: 'PUT',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify(data, null, 2)
|
|
||||||
});
|
|
||||||
return response.ok;
|
|
||||||
} catch (error) {
|
|
||||||
console.log(`Error saving ${filename}:`, error.message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fileExists(filename) {
|
|
||||||
try {
|
|
||||||
const url = this.basePath + filename;
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: 'HEAD'
|
|
||||||
});
|
|
||||||
return response.ok;
|
|
||||||
} catch (error) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
=======
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// Local File Storage
|
// Local File Storage
|
||||||
// ============================================
|
// ============================================
|
||||||
@ -166,8 +89,6 @@ class LocalFileStorage {
|
|||||||
|
|
||||||
async saveFile(filename, data) {
|
async saveFile(filename, data) {
|
||||||
try {
|
try {
|
||||||
// First ensure the data directory exists by trying to create it
|
|
||||||
// Note: We can't actually create directories via fetch, but we can try to save the file
|
|
||||||
const url = this.basePath + filename;
|
const url = this.basePath + filename;
|
||||||
|
|
||||||
// Try with PUT first (standard for creating/updating files)
|
// Try with PUT first (standard for creating/updating files)
|
||||||
@ -217,8 +138,6 @@ class LocalFileStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async ensureDirectory() {
|
async ensureDirectory() {
|
||||||
// Try to create a placeholder file to ensure directory exists
|
|
||||||
// This works on servers that support PUT for file creation
|
|
||||||
try {
|
try {
|
||||||
const placeholder = this.basePath + '.gitkeep';
|
const placeholder = this.basePath + '.gitkeep';
|
||||||
const response = await fetch(placeholder, {
|
const response = await fetch(placeholder, {
|
||||||
@ -233,104 +152,6 @@ class LocalFileStorage {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}TeeTracker - Main Application
|
|
||||||
// Version 4.0: Local file storage in data/ directory
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Configuration
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
const STORAGE_KEY_TEAS = 'teatracker_teas';
|
|
||||||
const STORAGE_KEY_ENTRIES = 'teatracker_entries';
|
|
||||||
const DATA_DIRECTORY = 'data/';
|
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Local File Storage
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
class LocalFileStorage {
|
|
||||||
constructor() {
|
|
||||||
this.basePath = DATA_DIRECTORY;
|
|
||||||
this.connected = false;
|
|
||||||
this.lastError = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async testConnection() {
|
|
||||||
try {
|
|
||||||
// Try to create a test file
|
|
||||||
const testFile = this.basePath + 'test_connection.txt';
|
|
||||||
const response = await fetch(testFile, {
|
|
||||||
method: 'PUT',
|
|
||||||
body: 'test'
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
// Clean up test file
|
|
||||||
await fetch(testFile, { method: 'DELETE' });
|
|
||||||
this.connected = true;
|
|
||||||
this.lastError = null;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
this.connected = false;
|
|
||||||
this.lastError = `HTTP Error: ${response.status}`;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.connected = false;
|
|
||||||
this.lastError = error.message;
|
|
||||||
console.log('Local file storage not available:', error.message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadFile(filename) {
|
|
||||||
try {
|
|
||||||
const url = this.basePath + filename;
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: 'GET'
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
const text = await response.text();
|
|
||||||
return text.trim() === '' ? [] : (text ? JSON.parse(text) : null);
|
|
||||||
} else if (response.status === 404) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
} catch (error) {
|
|
||||||
console.log(`Error loading ${filename}:`, error.message);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveFile(filename, data) {
|
|
||||||
try {
|
|
||||||
const url = this.basePath + filename;
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: 'PUT',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify(data, null, 2)
|
|
||||||
});
|
|
||||||
return response.ok;
|
|
||||||
} catch (error) {
|
|
||||||
console.log(`Error saving ${filename}:`, error.message);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fileExists(filename) {
|
|
||||||
try {
|
|
||||||
const url = this.basePath + filename;
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: 'HEAD'
|
|
||||||
});
|
|
||||||
return response.ok;
|
|
||||||
} catch (error) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
@ -374,7 +195,6 @@ class TeeTracker {
|
|||||||
// ============================================
|
// ============================================
|
||||||
|
|
||||||
async initStorage() {
|
async initStorage() {
|
||||||
// Test if file storage is available
|
|
||||||
this.useFileStorage = await this.fileStorage.testConnection();
|
this.useFileStorage = await this.fileStorage.testConnection();
|
||||||
this.updateSyncStatus();
|
this.updateSyncStatus();
|
||||||
}
|
}
|
||||||
@ -385,18 +205,14 @@ class TeeTracker {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (this.useFileStorage) {
|
if (this.useFileStorage) {
|
||||||
// Try to ensure data directory exists
|
|
||||||
await this.fileStorage.ensureDirectory();
|
await this.fileStorage.ensureDirectory();
|
||||||
|
|
||||||
// Try to load from file storage first
|
|
||||||
const teas = await this.fileStorage.loadFile('teas.json');
|
const teas = await this.fileStorage.loadFile('teas.json');
|
||||||
const entries = await this.fileStorage.loadFile('entries.json');
|
const entries = await this.fileStorage.loadFile('entries.json');
|
||||||
|
|
||||||
// Always load from localStorage as backup
|
|
||||||
const localTeas = localStorage.getItem(STORAGE_KEY_TEAS);
|
const localTeas = localStorage.getItem(STORAGE_KEY_TEAS);
|
||||||
const localEntries = localStorage.getItem(STORAGE_KEY_ENTRIES);
|
const localEntries = localStorage.getItem(STORAGE_KEY_ENTRIES);
|
||||||
|
|
||||||
// Load from file storage if available, otherwise use local data
|
|
||||||
if (teas && Array.isArray(teas)) {
|
if (teas && Array.isArray(teas)) {
|
||||||
this.teas = teas.map(tea => ({
|
this.teas = teas.map(tea => ({
|
||||||
...tea,
|
...tea,
|
||||||
@ -437,12 +253,10 @@ class TeeTracker {
|
|||||||
this.entries = [];
|
this.entries = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we loaded from file storage, save to localStorage as backup
|
|
||||||
if ((teas || entries) && (localTeas === null || localEntries === null)) {
|
if ((teas || entries) && (localTeas === null || localEntries === null)) {
|
||||||
this.saveToLocalStorage();
|
this.saveToLocalStorage();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Load from localStorage only
|
|
||||||
this.loadFromLocalStorage();
|
this.loadFromLocalStorage();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -487,11 +301,9 @@ class TeeTracker {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (this.useFileStorage) {
|
if (this.useFileStorage) {
|
||||||
// Save to file storage
|
|
||||||
const teasSaved = await this.fileStorage.saveFile('teas.json', this.teas);
|
const teasSaved = await this.fileStorage.saveFile('teas.json', this.teas);
|
||||||
const entriesSaved = await this.fileStorage.saveFile('entries.json', this.entries);
|
const entriesSaved = await this.fileStorage.saveFile('entries.json', this.entries);
|
||||||
|
|
||||||
// Also save locally as backup
|
|
||||||
this.saveToLocalStorage();
|
this.saveToLocalStorage();
|
||||||
|
|
||||||
if (teasSaved && entriesSaved) {
|
if (teasSaved && entriesSaved) {
|
||||||
@ -500,7 +312,6 @@ class TeeTracker {
|
|||||||
this.showToast('Lokale Speicherung erfolgreich, Dateispeicherung fehlgeschlagen', 'warning');
|
this.showToast('Lokale Speicherung erfolgreich, Dateispeicherung fehlgeschlagen', 'warning');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Save only locally
|
|
||||||
this.saveToLocalStorage();
|
this.saveToLocalStorage();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -529,18 +340,10 @@ class TeeTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Generate unique ID
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
generateId() {
|
generateId() {
|
||||||
return Date.now().toString(36) + Math.random().toString(36).substr(2);
|
return Date.now().toString(36) + Math.random().toString(36).substr(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Get data by ID
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
getTeaById(id) {
|
getTeaById(id) {
|
||||||
return this.teas.find(tea => tea.id === id);
|
return this.teas.find(tea => tea.id === id);
|
||||||
}
|
}
|
||||||
@ -549,10 +352,6 @@ class TeeTracker {
|
|||||||
return this.teas.findIndex(tea => tea.id === id);
|
return this.teas.findIndex(tea => tea.id === id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Tea Management
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
addTea(tea) {
|
addTea(tea) {
|
||||||
const newTea = {
|
const newTea = {
|
||||||
id: this.generateId(),
|
id: this.generateId(),
|
||||||
@ -592,10 +391,6 @@ class TeeTracker {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Entry Management
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
addEntry(entry) {
|
addEntry(entry) {
|
||||||
const newEntry = {
|
const newEntry = {
|
||||||
id: this.generateId(),
|
id: this.generateId(),
|
||||||
@ -622,10 +417,6 @@ class TeeTracker {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Statistics
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
getTotalCups() {
|
getTotalCups() {
|
||||||
return this.entries.reduce((sum, entry) => sum + entry.amount, 0);
|
return this.entries.reduce((sum, entry) => sum + entry.amount, 0);
|
||||||
}
|
}
|
||||||
@ -743,28 +534,21 @@ class TeeTracker {
|
|||||||
return days.size;
|
return days.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Initialization
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
// Ensure data directory exists by attempting to save
|
|
||||||
this.saveData().catch(() => {});
|
this.saveData().catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
setupEventListeners() {
|
setupEventListeners() {
|
||||||
// Tab navigation
|
|
||||||
document.querySelectorAll('.tab-button').forEach(button => {
|
document.querySelectorAll('.tab-button').forEach(button => {
|
||||||
button.addEventListener('click', () => {
|
button.addEventListener('click', () => {
|
||||||
this.switchTab(button.dataset.tab);
|
this.switchTab(button.dataset.tab);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Event delegation for tea card buttons (edit and delete)
|
// Event delegation for tea card buttons
|
||||||
const teasList = document.getElementById('teas-list');
|
const teasList = document.getElementById('teas-list');
|
||||||
if (teasList) {
|
if (teasList) {
|
||||||
teasList.addEventListener('click', (e) => {
|
teasList.addEventListener('click', (e) => {
|
||||||
// Edit button
|
|
||||||
const editBtn = e.target.closest('.tea-edit-btn');
|
const editBtn = e.target.closest('.tea-edit-btn');
|
||||||
if (editBtn) {
|
if (editBtn) {
|
||||||
const teaId = editBtn.dataset.teaId;
|
const teaId = editBtn.dataset.teaId;
|
||||||
@ -772,7 +556,6 @@ class TeeTracker {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete button
|
|
||||||
const deleteBtn = e.target.closest('.tea-delete-btn');
|
const deleteBtn = e.target.closest('.tea-delete-btn');
|
||||||
if (deleteBtn) {
|
if (deleteBtn) {
|
||||||
const teaId = deleteBtn.dataset.teaId;
|
const teaId = deleteBtn.dataset.teaId;
|
||||||
@ -782,13 +565,11 @@ class TeeTracker {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add tea button
|
|
||||||
const addTeaBtn = document.getElementById('add-tea-btn');
|
const addTeaBtn = document.getElementById('add-tea-btn');
|
||||||
if (addTeaBtn) {
|
if (addTeaBtn) {
|
||||||
addTeaBtn.addEventListener('click', () => this.openTeaModal());
|
addTeaBtn.addEventListener('click', () => this.openTeaModal());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tea modal
|
|
||||||
const teaModal = document.getElementById('tea-modal');
|
const teaModal = document.getElementById('tea-modal');
|
||||||
if (teaModal) {
|
if (teaModal) {
|
||||||
teaModal.addEventListener('click', (e) => {
|
teaModal.addEventListener('click', (e) => {
|
||||||
@ -796,31 +577,26 @@ class TeeTracker {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tea form
|
|
||||||
const teaForm = document.getElementById('tea-form');
|
const teaForm = document.getElementById('tea-form');
|
||||||
if (teaForm) {
|
if (teaForm) {
|
||||||
teaForm.addEventListener('submit', (e) => this.handleTeaFormSubmit(e));
|
teaForm.addEventListener('submit', (e) => this.handleTeaFormSubmit(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancel tea button
|
|
||||||
const cancelTeaBtn = document.getElementById('cancel-tea-btn');
|
const cancelTeaBtn = document.getElementById('cancel-tea-btn');
|
||||||
if (cancelTeaBtn) {
|
if (cancelTeaBtn) {
|
||||||
cancelTeaBtn.addEventListener('click', () => this.closeTeaModal());
|
cancelTeaBtn.addEventListener('click', () => this.closeTeaModal());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track form
|
|
||||||
const trackForm = document.getElementById('track-form');
|
const trackForm = document.getElementById('track-form');
|
||||||
if (trackForm) {
|
if (trackForm) {
|
||||||
trackForm.addEventListener('submit', (e) => this.handleTrackFormSubmit(e));
|
trackForm.addEventListener('submit', (e) => this.handleTrackFormSubmit(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stats period filter
|
|
||||||
const statsPeriod = document.getElementById('stats-period');
|
const statsPeriod = document.getElementById('stats-period');
|
||||||
if (statsPeriod) {
|
if (statsPeriod) {
|
||||||
statsPeriod.addEventListener('change', () => this.updateStats());
|
statsPeriod.addEventListener('change', () => this.updateStats());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export/Import/Clear data buttons
|
|
||||||
const exportBtn = document.getElementById('export-data');
|
const exportBtn = document.getElementById('export-data');
|
||||||
if (exportBtn) {
|
if (exportBtn) {
|
||||||
exportBtn.addEventListener('click', () => this.exportData());
|
exportBtn.addEventListener('click', () => this.exportData());
|
||||||
@ -836,7 +612,6 @@ class TeeTracker {
|
|||||||
clearBtn.addEventListener('click', () => this.showClearDataConfirm());
|
clearBtn.addEventListener('click', () => this.showClearDataConfirm());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import modal
|
|
||||||
const importModal = document.getElementById('import-modal');
|
const importModal = document.getElementById('import-modal');
|
||||||
if (importModal) {
|
if (importModal) {
|
||||||
importModal.addEventListener('click', (e) => {
|
importModal.addEventListener('click', (e) => {
|
||||||
@ -859,7 +634,6 @@ class TeeTracker {
|
|||||||
importDataBtn.addEventListener('click', () => this.handleImportData());
|
importDataBtn.addEventListener('click', () => this.handleImportData());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Confirm modal
|
|
||||||
const confirmModal = document.getElementById('confirm-modal');
|
const confirmModal = document.getElementById('confirm-modal');
|
||||||
if (confirmModal) {
|
if (confirmModal) {
|
||||||
confirmModal.addEventListener('click', (e) => {
|
confirmModal.addEventListener('click', (e) => {
|
||||||
@ -877,12 +651,10 @@ class TeeTracker {
|
|||||||
confirmBtn.addEventListener('click', () => this.confirmAction());
|
confirmBtn.addEventListener('click', () => this.confirmAction());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Background settings
|
|
||||||
this.setupBackgroundListeners();
|
this.setupBackgroundListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
setupTimerListeners() {
|
setupTimerListeners() {
|
||||||
// Timer controls
|
|
||||||
const startTimerBtn = document.getElementById('start-timer');
|
const startTimerBtn = document.getElementById('start-timer');
|
||||||
const pauseTimerBtn = document.getElementById('pause-timer');
|
const pauseTimerBtn = document.getElementById('pause-timer');
|
||||||
const resetTimerBtn = document.getElementById('reset-timer');
|
const resetTimerBtn = document.getElementById('reset-timer');
|
||||||
@ -907,38 +679,29 @@ class TeeTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setupBackgroundListeners() {
|
setupBackgroundListeners() {
|
||||||
// Background image upload
|
|
||||||
const bgUpload = document.getElementById('bg-image-upload');
|
const bgUpload = document.getElementById('bg-image-upload');
|
||||||
if (bgUpload) {
|
if (bgUpload) {
|
||||||
bgUpload.addEventListener('change', (e) => this.handleBackgroundUpload(e));
|
bgUpload.addEventListener('change', (e) => this.handleBackgroundUpload(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Background enable toggle
|
|
||||||
const bgEnabled = document.getElementById('bg-enabled');
|
const bgEnabled = document.getElementById('bg-enabled');
|
||||||
if (bgEnabled) {
|
if (bgEnabled) {
|
||||||
bgEnabled.addEventListener('change', () => this.toggleBackground());
|
bgEnabled.addEventListener('change', () => this.toggleBackground());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save background button
|
|
||||||
const saveBgBtn = document.getElementById('save-bg');
|
const saveBgBtn = document.getElementById('save-bg');
|
||||||
if (saveBgBtn) {
|
if (saveBgBtn) {
|
||||||
saveBgBtn.addEventListener('click', () => this.saveBackgroundSettings());
|
saveBgBtn.addEventListener('click', () => this.saveBackgroundSettings());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove background button
|
|
||||||
const removeBgBtn = document.getElementById('remove-bg');
|
const removeBgBtn = document.getElementById('remove-bg');
|
||||||
if (removeBgBtn) {
|
if (removeBgBtn) {
|
||||||
removeBgBtn.addEventListener('click', () => this.removeBackground());
|
removeBgBtn.addEventListener('click', () => this.removeBackground());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load saved background settings
|
|
||||||
this.loadBackgroundSettings();
|
this.loadBackgroundSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Background Functions
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
handleBackgroundUpload(e) {
|
handleBackgroundUpload(e) {
|
||||||
const file = e.target.files[0];
|
const file = e.target.files[0];
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
@ -1008,10 +771,6 @@ class TeeTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Timer Functions
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
startTimer() {
|
startTimer() {
|
||||||
if (this.timerRunning) return;
|
if (this.timerRunning) return;
|
||||||
|
|
||||||
@ -1045,7 +804,6 @@ class TeeTracker {
|
|||||||
|
|
||||||
pauseTimer() {
|
pauseTimer() {
|
||||||
if (!this.timerRunning) return;
|
if (!this.timerRunning) return;
|
||||||
|
|
||||||
clearInterval(this.timer);
|
clearInterval(this.timer);
|
||||||
this.timerRunning = false;
|
this.timerRunning = false;
|
||||||
this.timerPaused = true;
|
this.timerPaused = true;
|
||||||
@ -1094,10 +852,6 @@ class TeeTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Data Export/Import
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
exportData() {
|
exportData() {
|
||||||
const data = {
|
const data = {
|
||||||
teas: this.teas,
|
teas: this.teas,
|
||||||
@ -1137,7 +891,6 @@ class TeeTracker {
|
|||||||
const data = JSON.parse(textarea.value);
|
const data = JSON.parse(textarea.value);
|
||||||
|
|
||||||
if (data.teas && data.entries) {
|
if (data.teas && data.entries) {
|
||||||
// Merge or replace data
|
|
||||||
if (confirm('Sollen die importierten Daten die bestehenden ersetzen?')) {
|
if (confirm('Sollen die importierten Daten die bestehenden ersetzen?')) {
|
||||||
this.teas = data.teas.map(tea => ({
|
this.teas = data.teas.map(tea => ({
|
||||||
...tea,
|
...tea,
|
||||||
@ -1145,7 +898,6 @@ class TeeTracker {
|
|||||||
}));
|
}));
|
||||||
this.entries = data.entries;
|
this.entries = data.entries;
|
||||||
} else {
|
} else {
|
||||||
// Merge data
|
|
||||||
this.teas = [...this.teas, ...data.teas.map(tea => ({
|
this.teas = [...this.teas, ...data.teas.map(tea => ({
|
||||||
...tea,
|
...tea,
|
||||||
organic: tea.organic !== undefined ? tea.organic : false
|
organic: tea.organic !== undefined ? tea.organic : false
|
||||||
@ -1177,8 +929,6 @@ class TeeTracker {
|
|||||||
const message = 'Möchtest du wirklich ALLE Daten löschen? Dieser Vorgang kann nicht rückgängig gemacht werden!';
|
const message = 'Möchtest du wirklich ALLE Daten löschen? Dieser Vorgang kann nicht rückgängig gemacht werden!';
|
||||||
document.getElementById('confirm-message').textContent = message;
|
document.getElementById('confirm-message').textContent = message;
|
||||||
document.getElementById('confirm-modal').classList.add('active');
|
document.getElementById('confirm-modal').classList.add('active');
|
||||||
|
|
||||||
// Set action type
|
|
||||||
this.tempConfirmAction = 'clearData';
|
this.tempConfirmAction = 'clearData';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1195,11 +945,9 @@ class TeeTracker {
|
|||||||
this.teas = [];
|
this.teas = [];
|
||||||
this.entries = [];
|
this.entries = [];
|
||||||
|
|
||||||
// Clear localStorage
|
|
||||||
localStorage.removeItem(STORAGE_KEY_TEAS);
|
localStorage.removeItem(STORAGE_KEY_TEAS);
|
||||||
localStorage.removeItem(STORAGE_KEY_ENTRIES);
|
localStorage.removeItem(STORAGE_KEY_ENTRIES);
|
||||||
|
|
||||||
// Clear file storage if available
|
|
||||||
if (this.useFileStorage) {
|
if (this.useFileStorage) {
|
||||||
await this.fileStorage.saveFile('teas.json', []);
|
await this.fileStorage.saveFile('teas.json', []);
|
||||||
await this.fileStorage.saveFile('entries.json', []);
|
await this.fileStorage.saveFile('entries.json', []);
|
||||||
@ -1211,10 +959,6 @@ class TeeTracker {
|
|||||||
this.updateSettingsStats();
|
this.updateSettingsStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Tab Navigation
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
switchTab(tabId) {
|
switchTab(tabId) {
|
||||||
document.querySelectorAll('.tab-button').forEach(button => {
|
document.querySelectorAll('.tab-button').forEach(button => {
|
||||||
button.classList.toggle('active', button.dataset.tab === tabId);
|
button.classList.toggle('active', button.dataset.tab === tabId);
|
||||||
@ -1273,10 +1017,6 @@ class TeeTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Dashboard
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
updateDashboard() {
|
updateDashboard() {
|
||||||
const totalCups = document.getElementById('total-cups');
|
const totalCups = document.getElementById('total-cups');
|
||||||
const todayCups = document.getElementById('today-cups');
|
const todayCups = document.getElementById('today-cups');
|
||||||
@ -1329,10 +1069,6 @@ class TeeTracker {
|
|||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Tea List
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
renderTeasList() {
|
renderTeasList() {
|
||||||
const container = document.getElementById('teas-list');
|
const container = document.getElementById('teas-list');
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
@ -1346,7 +1082,6 @@ class TeeTracker {
|
|||||||
<button class="btn btn-primary add-first-tea-btn">➕ Tee hinzufügen</button>
|
<button class="btn btn-primary add-first-tea-btn">➕ Tee hinzufügen</button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
// Add event listener for the add button
|
|
||||||
const addBtn = container.querySelector('.add-first-tea-btn');
|
const addBtn = container.querySelector('.add-first-tea-btn');
|
||||||
if (addBtn) {
|
if (addBtn) {
|
||||||
addBtn.addEventListener('click', () => this.openTeaModal());
|
addBtn.addEventListener('click', () => this.openTeaModal());
|
||||||
@ -1418,10 +1153,6 @@ class TeeTracker {
|
|||||||
`).join('');
|
`).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Tea Modal
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
openTeaModal(teaId = null) {
|
openTeaModal(teaId = null) {
|
||||||
const modal = document.getElementById('tea-modal');
|
const modal = document.getElementById('tea-modal');
|
||||||
const form = document.getElementById('tea-form');
|
const form = document.getElementById('tea-form');
|
||||||
@ -1439,12 +1170,10 @@ class TeeTracker {
|
|||||||
document.getElementById('tea-organic').checked = tea.organic || false;
|
document.getElementById('tea-organic').checked = tea.organic || false;
|
||||||
document.getElementById('tea-image-preview-img').src = tea.image || '';
|
document.getElementById('tea-image-preview-img').src = tea.image || '';
|
||||||
document.getElementById('tea-image-preview').style.display = tea.image ? 'block' : 'none';
|
document.getElementById('tea-image-preview').style.display = tea.image ? 'block' : 'none';
|
||||||
|
|
||||||
this.renderStarRating(tea.rating || 3, 'tea-rating');
|
this.renderStarRating(tea.rating || 3, 'tea-rating');
|
||||||
this.currentTeaId = tea.id;
|
this.currentTeaId = tea.id;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// New tea
|
|
||||||
form.reset();
|
form.reset();
|
||||||
document.getElementById('tea-color').value = '#8B4513';
|
document.getElementById('tea-color').value = '#8B4513';
|
||||||
document.getElementById('tea-organic').checked = false;
|
document.getElementById('tea-organic').checked = false;
|
||||||
@ -1477,8 +1206,6 @@ class TeeTracker {
|
|||||||
const color = document.getElementById('tea-color').value;
|
const color = document.getElementById('tea-color').value;
|
||||||
const organic = document.getElementById('tea-organic').checked;
|
const organic = document.getElementById('tea-organic').checked;
|
||||||
const image = document.getElementById('tea-image-preview').src || '';
|
const image = document.getElementById('tea-image-preview').src || '';
|
||||||
|
|
||||||
// Get rating from star rating
|
|
||||||
const rating = this.getStarRating('tea-rating');
|
const rating = this.getStarRating('tea-rating');
|
||||||
|
|
||||||
if (!name || !type) {
|
if (!name || !type) {
|
||||||
@ -1489,11 +1216,9 @@ class TeeTracker {
|
|||||||
const teaData = { name, type, brand, description, color, organic, rating, image };
|
const teaData = { name, type, brand, description, color, organic, rating, image };
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
// Update existing tea
|
|
||||||
this.updateTea(id, teaData);
|
this.updateTea(id, teaData);
|
||||||
this.showToast('Tee-Sorte erfolgreich aktualisiert!', 'success');
|
this.showToast('Tee-Sorte erfolgreich aktualisiert!', 'success');
|
||||||
} else {
|
} else {
|
||||||
// Add new tea
|
|
||||||
this.addTea(teaData);
|
this.addTea(teaData);
|
||||||
this.showToast('Tee-Sorte erfolgreich hinzugefügt!', 'success');
|
this.showToast('Tee-Sorte erfolgreich hinzugefügt!', 'success');
|
||||||
}
|
}
|
||||||
@ -1578,7 +1303,6 @@ class TeeTracker {
|
|||||||
const message = `Möchtest du wirklich die Tee-Sorte "${tea ? tea.name : 'Unbekannt'}" löschen? Alle damit verbundenen Einträge werden ebenfalls gelöscht!`;
|
const message = `Möchtest du wirklich die Tee-Sorte "${tea ? tea.name : 'Unbekannt'}" löschen? Alle damit verbundenen Einträge werden ebenfalls gelöscht!`;
|
||||||
document.getElementById('confirm-message').textContent = message;
|
document.getElementById('confirm-message').textContent = message;
|
||||||
document.getElementById('confirm-modal').classList.add('active');
|
document.getElementById('confirm-modal').classList.add('active');
|
||||||
|
|
||||||
this.tempConfirmAction = 'deleteTea';
|
this.tempConfirmAction = 'deleteTea';
|
||||||
this.currentTeaId = teaId;
|
this.currentTeaId = teaId;
|
||||||
}
|
}
|
||||||
@ -1610,10 +1334,6 @@ class TeeTracker {
|
|||||||
this.currentTeaId = null;
|
this.currentTeaId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Track Form
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
renderTrackForm() {
|
renderTrackForm() {
|
||||||
const teaSelect = document.getElementById('track-tea');
|
const teaSelect = document.getElementById('track-tea');
|
||||||
const today = new Date().toISOString().split('T')[0];
|
const today = new Date().toISOString().split('T')[0];
|
||||||
@ -1662,7 +1382,6 @@ class TeeTracker {
|
|||||||
this.showToast('Tee-Konsum erfolgreich getrackt!', 'success');
|
this.showToast('Tee-Konsum erfolgreich getrackt!', 'success');
|
||||||
|
|
||||||
this.resetTrackForm();
|
this.resetTrackForm();
|
||||||
|
|
||||||
this.updateDashboard();
|
this.updateDashboard();
|
||||||
this.updateStats();
|
this.updateStats();
|
||||||
this.renderTrackRecentEntries();
|
this.renderTrackRecentEntries();
|
||||||
@ -1716,12 +1435,7 @@ class TeeTracker {
|
|||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Charts
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
initCharts() {
|
initCharts() {
|
||||||
// Check if Chart.js is loaded
|
|
||||||
if (typeof Chart === 'undefined') {
|
if (typeof Chart === 'undefined') {
|
||||||
console.log('Chart.js not loaded, skipping charts initialization');
|
console.log('Chart.js not loaded, skipping charts initialization');
|
||||||
return;
|
return;
|
||||||
@ -1739,7 +1453,6 @@ class TeeTracker {
|
|||||||
const labels = Object.keys(typeCounts);
|
const labels = Object.keys(typeCounts);
|
||||||
const data = Object.values(typeCounts);
|
const data = Object.values(typeCounts);
|
||||||
|
|
||||||
// Define colors for each tea type
|
|
||||||
const typeColors = {
|
const typeColors = {
|
||||||
'Schwarztee': '#8B4513',
|
'Schwarztee': '#8B4513',
|
||||||
'Grüntee': '#228B22',
|
'Grüntee': '#228B22',
|
||||||
@ -1800,7 +1513,6 @@ class TeeTracker {
|
|||||||
const labels = Object.keys(dailyCounts).sort();
|
const labels = Object.keys(dailyCounts).sort();
|
||||||
const data = labels.map(date => dailyCounts[date] || 0);
|
const data = labels.map(date => dailyCounts[date] || 0);
|
||||||
|
|
||||||
// Format dates for display
|
|
||||||
const formattedLabels = labels.map(date => {
|
const formattedLabels = labels.map(date => {
|
||||||
const d = new Date(date);
|
const d = new Date(date);
|
||||||
return d.toLocaleDateString('de-DE', { weekday: 'short', day: 'numeric' });
|
return d.toLocaleDateString('de-DE', { weekday: 'short', day: 'numeric' });
|
||||||
@ -1850,15 +1562,9 @@ class TeeTracker {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Statistics
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
updateStats() {
|
updateStats() {
|
||||||
const period = document.getElementById('stats-period')?.value || 'all';
|
const period = document.getElementById('stats-period')?.value || 'all';
|
||||||
this.renderDetailedStats(period);
|
this.renderDetailedStats(period);
|
||||||
|
|
||||||
// Update charts
|
|
||||||
this.createTeaTypeChart();
|
this.createTeaTypeChart();
|
||||||
this.createDailyChart();
|
this.createDailyChart();
|
||||||
}
|
}
|
||||||
@ -1900,7 +1606,6 @@ class TeeTracker {
|
|||||||
calculateAveragePerDay(period) {
|
calculateAveragePerDay(period) {
|
||||||
const entries = this.getEntriesByPeriod(period);
|
const entries = this.getEntriesByPeriod(period);
|
||||||
if (entries.length === 0) return 0;
|
if (entries.length === 0) return 0;
|
||||||
|
|
||||||
const totalCups = this.getCupsByPeriod(period);
|
const totalCups = this.getCupsByPeriod(period);
|
||||||
const days = this.countUniqueDays(entries);
|
const days = this.countUniqueDays(entries);
|
||||||
return days > 0 ? totalCups / days : 0;
|
return days > 0 ? totalCups / days : 0;
|
||||||
@ -1918,10 +1623,6 @@ class TeeTracker {
|
|||||||
return teaIds.size;
|
return teaIds.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Toast Notifications
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
showToast(message, type = 'info') {
|
showToast(message, type = 'info') {
|
||||||
const toastContainer = document.getElementById('toast-container');
|
const toastContainer = document.getElementById('toast-container');
|
||||||
if (!toastContainer) return;
|
if (!toastContainer) return;
|
||||||
|
|||||||
Reference in New Issue
Block a user