Fix: Statistiken werden nicht angezeigt, Tee-Sorten Editieren funktioniert nicht

- Fix updateStats() um renderDetailedStats() korrekt aufzurufen
- Fix Stats-Period-Filter Event-Listener hinzugefügt
- Verbesserte LocalFileStorage-Klasse mit besserer Fehlerbehandlung
- .htaccess-Datei für Apache-Server hinzugefügt
- SERVER_SETUP.md mit Anleitungen für Server-Konfiguration
- data/-Verzeichnis mit .gitkeep hinzugefügt
- app-Variable als window.app global verfügbar gemacht

Co-authored-by: trevor1969 <trevor1969@users.noreply.github.com>
This commit is contained in:
Vibe Nuage Agent
2026-06-06 11:15:48 +00:00
parent b47e2bd7d0
commit 2544a102d9
4 changed files with 489 additions and 6 deletions

262
app.js
View File

@ -1,4 +1,239 @@
// TeeTracker - Main Application
// 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;
}
}
}
=======
// ============================================
// Local File Storage
// ============================================
class LocalFileStorage {
constructor() {
this.basePath = DATA_DIRECTORY;
this.connected = false;
this.lastError = null;
this.tested = false;
}
async testConnection() {
if (this.tested) return this.connected;
this.tested = true;
try {
// First, check if we can read from the data directory
const testFile = this.basePath + 'teas.json';
const readResponse = await fetch(testFile, {
method: 'GET'
});
if (readResponse.ok) {
// If we can read, try to write
const testWriteFile = this.basePath + '.write_test_' + Date.now() + '.tmp';
const writeResponse = await fetch(testWriteFile, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ test: true })
});
if (writeResponse.ok) {
// Clean up test file
await fetch(testWriteFile, { method: 'DELETE' }).catch(() => {});
this.connected = true;
this.lastError = null;
return true;
}
}
this.connected = false;
this.lastError = 'Server unterstützt Dateispeicherung nicht (PUT/DELETE nicht verfügbar)';
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',
cache: 'no-store'
});
if (response.ok) {
const text = await response.text();
if (!text || text.trim() === '') {
return [];
}
return JSON.parse(text);
} 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 {
// 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;
// Try with PUT first (standard for creating/updating files)
let response = await fetch(url, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data, null, 2)
});
if (response.ok) {
return true;
}
// If PUT fails, try POST (some servers use POST for file creation)
response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data, null, 2)
});
if (response.ok) {
return true;
}
console.log(`Failed to save ${filename}: PUT and POST both failed`);
return false;
} 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;
}
}
async ensureDirectory() {
// Try to create a placeholder file to ensure directory exists
// This works on servers that support PUT for file creation
try {
const placeholder = this.basePath + '.gitkeep';
const response = await fetch(placeholder, {
method: 'PUT',
headers: {
'Content-Type': 'text/plain'
},
body: 'This file ensures the data directory exists'
});
return response.ok;
} catch (error) {
return false;
}
}
}TeeTracker - Main Application
// Version 4.0: Local file storage in data/ directory
// ============================================
@ -150,6 +385,9 @@ class TeeTracker {
try {
if (this.useFileStorage) {
// Try to ensure data directory exists
await this.fileStorage.ensureDirectory();
// Try to load from file storage first
const teas = await this.fileStorage.loadFile('teas.json');
const entries = await this.fileStorage.loadFile('entries.json');
@ -159,7 +397,7 @@ class TeeTracker {
const localEntries = localStorage.getItem(STORAGE_KEY_ENTRIES);
// Load from file storage if available, otherwise use local data
if (teas) {
if (teas && Array.isArray(teas)) {
this.teas = teas.map(tea => ({
...tea,
organic: tea.organic !== undefined ? tea.organic : false,
@ -180,7 +418,7 @@ class TeeTracker {
this.teas = [];
}
if (entries) {
if (entries && Array.isArray(entries)) {
this.entries = entries.map(entry => ({
...entry,
teaspoons: entry.teaspoons !== undefined ? entry.teaspoons : 1
@ -198,6 +436,11 @@ class TeeTracker {
} else {
this.entries = [];
}
// If we loaded from file storage, save to localStorage as backup
if ((teas || entries) && (localTeas === null || localEntries === null)) {
this.saveToLocalStorage();
}
} else {
// Load from localStorage only
this.loadFromLocalStorage();
@ -548,6 +791,12 @@ class TeeTracker {
if (trackForm) {
trackForm.addEventListener('submit', (e) => this.handleTrackFormSubmit(e));
}
// Stats period filter
const statsPeriod = document.getElementById('stats-period');
if (statsPeriod) {
statsPeriod.addEventListener('change', () => this.updateStats());
}
// Export/Import/Clear data buttons
const exportBtn = document.getElementById('export-data');
@ -1579,7 +1828,8 @@ class TeeTracker {
// ============================================
updateStats() {
this.updateDetailedStats('all');
const period = document.getElementById('stats-period')?.value || 'all';
this.renderDetailedStats(period);
// Update charts
this.createTeaTypeChart();
@ -1666,7 +1916,7 @@ class TeeTracker {
}
// Initialize application
let app;
window.app = new TeeTracker();
document.addEventListener('DOMContentLoaded', () => {
app = new TeeTracker();
window.app = new TeeTracker();
});