mirror of
https://github.com/trevor1969/teatracker.git
synced 2026-08-09 10:41:59 +00:00
fix: Use window.app for onclick handlers on dynamically generated buttons
- Revert to onclick handlers for tea card edit/delete buttons - Use window.app instead of app to ensure global accessibility - Remove event delegation code that wasn't working - Add onclick handlers to import modal buttons in HTML - This ensures buttons work reliably on dynamically generated content Co-authored-by: trevor1969 <trevor1969@users.noreply.github.com>
This commit is contained in:
80
app.js
80
app.js
@ -1,16 +1,4 @@
|
|||||||
// Nextcloud Storage Integration
|
// TeeTracker - Main Application with Nextcloud Support
|
||||||
// ============================================
|
|
||||||
|
|
||||||
class NextcloudStorage {
|
|
||||||
=======
|
|
||||||
// ============================================
|
|
||||||
// Nextcloud Storage Integration
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
// CORS Proxy for Nextcloud WebDAV (fallback when direct connection fails due to CORS)
|
|
||||||
const NEXTCLOUD_PROXY = 'https://corsproxy.io/?';
|
|
||||||
|
|
||||||
class NextcloudStorage {TeeTracker - Main Application with Nextcloud Support
|
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// Configuration
|
// Configuration
|
||||||
@ -33,7 +21,6 @@ class NextcloudStorage {
|
|||||||
this.authHeader = 'Basic ' + btoa(`${username}:${password}`);
|
this.authHeader = 'Basic ' + btoa(`${username}:${password}`);
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
this.lastError = null;
|
this.lastError = null;
|
||||||
this.useProxy = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getFileUrl(filename) {
|
getFileUrl(filename) {
|
||||||
@ -44,54 +31,9 @@ class NextcloudStorage {
|
|||||||
return `${this.baseUrl}/remote.php/dav/files/${encodeURIComponent(this.username)}${this.path}`;
|
return `${this.baseUrl}/remote.php/dav/files/${encodeURIComponent(this.username)}${this.path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get proxied URL for CORS fallback
|
|
||||||
getProxiedUrl(url) {
|
|
||||||
return `${NEXTCLOUD_PROXY}${encodeURIComponent(url)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapper for fetch that automatically uses proxy on CORS failure
|
|
||||||
async fetchWithProxy(url, options = {}) {
|
|
||||||
try {
|
|
||||||
// First try direct connection
|
|
||||||
const response = await fetch(url, options);
|
|
||||||
|
|
||||||
// If we get a CORS error or network error, it will throw
|
|
||||||
// If we get here, the request succeeded (even if status is not 2xx)
|
|
||||||
if (response.ok || response.status === 404 || response.status === 405) {
|
|
||||||
this.useProxy = false;
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some errors might still occur, so try proxy
|
|
||||||
console.log('Direct connection returned non-ok status, trying proxy...');
|
|
||||||
} catch (error) {
|
|
||||||
// Direct connection failed (CORS or network error), try proxy
|
|
||||||
console.log('Direct connection failed, trying proxy...');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try with proxy
|
|
||||||
try {
|
|
||||||
const proxyUrl = this.getProxiedUrl(url);
|
|
||||||
this.useProxy = true;
|
|
||||||
const response = await fetch(proxyUrl, options);
|
|
||||||
|
|
||||||
if (response.ok || response.status === 404 || response.status === 405) {
|
|
||||||
console.log('Proxy connection successful');
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If proxy also fails, throw error
|
|
||||||
throw new Error(`Proxy returned status: ${response.status}`);
|
|
||||||
} catch (proxyError) {
|
|
||||||
this.useProxy = false;
|
|
||||||
console.error('Proxy connection also failed:', proxyError);
|
|
||||||
throw new Error(`Both direct and proxy connections failed: ${error.message || ''} ${proxyError.message || ''}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async testConnection() {
|
async testConnection() {
|
||||||
try {
|
try {
|
||||||
const response = await this.fetchWithProxy(this.getDirectoryUrl(), {
|
const response = await fetch(this.getDirectoryUrl(), {
|
||||||
method: 'PROPFIND',
|
method: 'PROPFIND',
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': this.authHeader,
|
'Authorization': this.authHeader,
|
||||||
@ -123,7 +65,7 @@ class NextcloudStorage {
|
|||||||
|
|
||||||
async ensureDirectory() {
|
async ensureDirectory() {
|
||||||
try {
|
try {
|
||||||
const response = await this.fetchWithProxy(this.getDirectoryUrl(), {
|
const response = await fetch(this.getDirectoryUrl(), {
|
||||||
method: 'MKCOL',
|
method: 'MKCOL',
|
||||||
headers: { 'Authorization': this.authHeader }
|
headers: { 'Authorization': this.authHeader }
|
||||||
});
|
});
|
||||||
@ -138,7 +80,7 @@ class NextcloudStorage {
|
|||||||
async ensureSubDirectory(subPath) {
|
async ensureSubDirectory(subPath) {
|
||||||
try {
|
try {
|
||||||
const subDirUrl = `${this.getDirectoryUrl()}${subPath}/`;
|
const subDirUrl = `${this.getDirectoryUrl()}${subPath}/`;
|
||||||
const response = await this.fetchWithProxy(subDirUrl, {
|
const response = await fetch(subDirUrl, {
|
||||||
method: 'MKCOL',
|
method: 'MKCOL',
|
||||||
headers: { 'Authorization': this.authHeader }
|
headers: { 'Authorization': this.authHeader }
|
||||||
});
|
});
|
||||||
@ -152,7 +94,7 @@ class NextcloudStorage {
|
|||||||
async loadFile(filename) {
|
async loadFile(filename) {
|
||||||
try {
|
try {
|
||||||
const url = this.getFileUrl(filename);
|
const url = this.getFileUrl(filename);
|
||||||
const response = await this.fetchWithProxy(url, {
|
const response = await fetch(url, {
|
||||||
headers: { 'Authorization': this.authHeader }
|
headers: { 'Authorization': this.authHeader }
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -172,7 +114,7 @@ class NextcloudStorage {
|
|||||||
async saveFile(filename, data) {
|
async saveFile(filename, data) {
|
||||||
try {
|
try {
|
||||||
const url = this.getFileUrl(filename);
|
const url = this.getFileUrl(filename);
|
||||||
const response = await this.fetchWithProxy(url, {
|
const response = await fetch(url, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': this.authHeader,
|
'Authorization': this.authHeader,
|
||||||
@ -190,7 +132,7 @@ class NextcloudStorage {
|
|||||||
async fileExists(filename) {
|
async fileExists(filename) {
|
||||||
try {
|
try {
|
||||||
const url = this.getFileUrl(filename);
|
const url = this.getFileUrl(filename);
|
||||||
const response = await this.fetchWithProxy(url, {
|
const response = await fetch(url, {
|
||||||
method: 'HEAD',
|
method: 'HEAD',
|
||||||
headers: { 'Authorization': this.authHeader }
|
headers: { 'Authorization': this.authHeader }
|
||||||
});
|
});
|
||||||
@ -1129,8 +1071,8 @@ class TeeTracker {
|
|||||||
${tea.brand ? `<div class="tea-brand">Marke: ${tea.brand}</div>` : ''}
|
${tea.brand ? `<div class="tea-brand">Marke: ${tea.brand}</div>` : ''}
|
||||||
${tea.description ? `<div class="tea-description">${tea.description}</div>` : ''}
|
${tea.description ? `<div class="tea-description">${tea.description}</div>` : ''}
|
||||||
<div class="tea-actions">
|
<div class="tea-actions">
|
||||||
<button class="tea-action-btn tea-edit-btn" onclick="app.editTea('${tea.id}')">Bearbeiten</button>
|
<button class="tea-action-btn tea-edit-btn" onclick="window.app.editTea('${tea.id}')">Bearbeiten</button>
|
||||||
<button class="tea-action-btn tea-delete-btn" onclick="app.showDeleteTeaConfirm('${tea.id}')">Löschen</button>
|
<button class="tea-action-btn tea-delete-btn" onclick="window.app.showDeleteTeaConfirm('${tea.id}')">Löschen</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
@ -1173,8 +1115,8 @@ class TeeTracker {
|
|||||||
${tea.brand ? `<div class="tea-brand">Marke: ${tea.brand}</div>` : ''}
|
${tea.brand ? `<div class="tea-brand">Marke: ${tea.brand}</div>` : ''}
|
||||||
${tea.description ? `<div class="tea-description">${tea.description}</div>` : ''}
|
${tea.description ? `<div class="tea-description">${tea.description}</div>` : ''}
|
||||||
<div class="tea-actions">
|
<div class="tea-actions">
|
||||||
<button class="tea-action-btn tea-edit-btn" onclick="app.editTea('${tea.id}')">Bearbeiten</button>
|
<button class="tea-action-btn tea-edit-btn" onclick="window.app.editTea('${tea.id}')">Bearbeiten</button>
|
||||||
<button class="tea-action-btn tea-delete-btn" onclick="app.showDeleteTeaConfirm('${tea.id}')">Löschen</button>
|
<button class="tea-action-btn tea-delete-btn" onclick="window.app.showDeleteTeaConfirm('${tea.id}')">Löschen</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
|
|||||||
@ -327,8 +327,8 @@
|
|||||||
<textarea id="import-data-textarea" class="modal-textarea" placeholder="{\n \"teas\": [...],\n \"entries\": [...]\n}"></textarea>
|
<textarea id="import-data-textarea" class="modal-textarea" placeholder="{\n \"teas\": [...],\n \"entries\": [...]\n}"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button id="import-data-btn" class="btn btn-primary" onclick="app.handleImportData()">📥 Importieren</button>
|
<button id="import-data-btn" class="btn btn-primary" onclick="window.app.handleImportData()">📥 Importieren</button>
|
||||||
<button id="cancel-import-btn" class="btn btn-secondary" onclick="app.closeImportModal()">Abbrechen</button>
|
<button id="cancel-import-btn" class="btn btn-secondary" onclick="window.app.closeImportModal()">Abbrechen</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user