Refactor database handling to require PostgreSQL connection, removing fallback to JSON. Implement error handling for database unavailability in key files. Update .env.example to reflect mandatory DATABASE_URL for local development. Remove Firebase configuration and related code from the project.

This commit is contained in:
2026-05-23 10:23:28 +02:00
parent 547778bba5
commit 6577db475a
20 changed files with 823 additions and 789 deletions
+16 -92
View File
@@ -61,37 +61,23 @@
})();
// ── Newsletter subscription logic ──────────────────────────
// ── Newsletter (mailto) ────────────────────────────────────
function handleSubscribe(e) {
e.preventDefault();
var emailInput = document.getElementById('newsletterEmail');
var feedback = document.getElementById('newsletterFeedback');
var form = document.getElementById('newsletterForm');
var btn = form.querySelector('button');
var email = emailInput.value.trim();
if (!email) return;
btn.disabled = true;
feedback.className = 'newsletter-feedback';
feedback.textContent = '';
var subject = encodeURIComponent('FlixCooks Newsletter');
var body = encodeURIComponent('Please add me to the newsletter: ' + email);
window.location.href = 'mailto:<?php echo e($contactEmail); ?>?subject=' + subject + '&body=' + body;
window.db.collection('subscribers').doc(email.toLowerCase()).set({
email: email.toLowerCase(),
subscribedAt: firebase.firestore.FieldValue.serverTimestamp(),
status: 'active'
})
.then(function() {
form.classList.add('success');
emailInput.disabled = true;
feedback.className = 'newsletter-feedback success';
feedback.textContent = '<?php echo $langCode === "de" ? "Erfolgreich abonniert!" : "Successfully subscribed!"; ?>';
})
.catch(function(error) {
btn.disabled = false;
feedback.className = 'newsletter-feedback error';
feedback.textContent = '<?php echo $langCode === "de" ? "Fehler: " : "Error: "; ?>' + error.message;
});
form.classList.add('success');
feedback.className = 'newsletter-feedback success';
feedback.textContent = '<?php echo $langCode === "de" ? "E-Mail-Programm geöffnet bitte absenden." : "Email client opened — please send."; ?>';
}
// ── Guest Modal Logic ──────────────────────────────────────
@@ -121,33 +107,12 @@ document.addEventListener('click', function(e) {
}
});
// ── Favorites Toggle & Management ───────────────────────────
var currentUser = null;
var userFavSlugs = new Set();
function syncFavorites(uid) {
window.db.collection('user_favorites').where('userId', '==', uid).get()
.then(function(snapshot) {
userFavSlugs.clear();
snapshot.forEach(function(doc) {
userFavSlugs.add(doc.data().recipeSlug);
});
updateHeartStates();
})
.catch(function(err) {
console.error("Error syncing favorites: ", err);
});
}
function clearFavorites() {
userFavSlugs.clear();
updateHeartStates();
}
// ── Favorites (localStorage) ───────────────────────────────
function updateHeartStates() {
if (!window.fcLocal) return;
document.querySelectorAll('.btn-favorite').forEach(function(btn) {
var slug = btn.getAttribute('data-slug');
if (userFavSlugs.has(slug)) {
if (slug && window.fcLocal.hasFavorite(slug)) {
btn.classList.add('active');
} else {
btn.classList.remove('active');
@@ -156,43 +121,12 @@ function updateHeartStates() {
}
function toggleFavorite(slug, btn) {
if (!currentUser) {
if (!window.fcLocal) {
openGuestModal();
return;
}
var docId = currentUser.uid + '_' + slug;
var docRef = window.db.collection('user_favorites').doc(docId);
if (userFavSlugs.has(slug)) {
// Optimistic UI update
userFavSlugs.delete(slug);
btn.classList.remove('active');
docRef.delete()
.catch(function(err) {
console.error("Error removing favorite: ", err);
// Rollback
userFavSlugs.add(slug);
btn.classList.add('active');
});
} else {
// Optimistic UI update
userFavSlugs.add(slug);
btn.classList.add('active');
docRef.set({
userId: currentUser.uid,
recipeSlug: slug,
createdAt: firebase.firestore.FieldValue.serverTimestamp()
})
.catch(function(err) {
console.error("Error adding favorite: ", err);
// Rollback
userFavSlugs.delete(slug);
btn.classList.remove('active');
});
}
window.fcLocal.toggleFavorite(slug);
updateHeartStates();
}
// Hook up event listeners to all favorite buttons dynamically
@@ -206,18 +140,8 @@ document.addEventListener('click', function(e) {
}
});
// Setup Auth state observer for favorites
window.addEventListener('DOMContentLoaded', function() {
if (window.auth) {
window.auth.onAuthStateChanged(function(user) {
currentUser = user;
if (user) {
syncFavorites(user.uid);
} else {
clearFavorites();
}
});
}
updateHeartStates();
});
})();
</script>
@@ -228,10 +152,10 @@ window.addEventListener('DOMContentLoaded', function() {
<button class="guest-modal-close" onclick="closeGuestModal()" aria-label="Close modal">×</button>
<div class="guest-modal-icon">❤️</div>
<h3><?php echo $langCode === 'de' ? 'Lieblingsrezept speichern' : 'Save your favorite recipe'; ?></h3>
<p><?php echo $langCode === 'de' ? 'Erstelle ein kostenloses Konto oder melde dich an, um Rezepte in deinen Favoriten zu speichern und deine Ernährungsziele zu verwalten.' : 'Create a free account or login to save recipes to your favorites and manage your dietary goals.'; ?></p>
<p><?php echo $langCode === 'de' ? 'Speichere Rezepte lokal in deinem Browser und verwalte dein Ernährungsziel im Profil.' : 'Save recipes locally in your browser and manage your dietary goal in your profile.'; ?></p>
<div class="guest-modal-actions">
<a class="btn-reveal" href="/login.php<?php echo $langSuffix; ?>">
<span class="btn-reveal-text"><?php echo $langCode === 'de' ? 'Jetzt Anmelden' : 'Login / Register'; ?></span>
<span class="btn-reveal-text"><?php echo $langCode === 'de' ? 'Zum Profil' : 'Go to profile'; ?></span>
<span class="btn-reveal-arrow">→</span>
</a>
<button class="underline-link" onclick="closeGuestModal()"><?php echo $langCode === 'de' ? 'Später' : 'Maybe later'; ?></button>
+1 -43
View File
@@ -19,49 +19,7 @@ $description = $description ?? 'Seasonal recipes, tested tips, and approachable
<!-- Main stylesheet -->
<link rel="stylesheet" href="/assets/style.css">
<!-- Firebase v10 compat SDKs -->
<script src="https://www.gstatic.com/firebasejs/10.8.0/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.8.0/firebase-auth-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/10.8.0/firebase-firestore-compat.js"></script>
<script>
(function() {
var config = <?php echo json_encode(get_firebase_config()); ?>;
firebase.initializeApp(config);
window.auth = firebase.auth();
window.db = firebase.firestore();
// Auto-sync PHP Session with Firebase Auth State
window.auth.onAuthStateChanged(function(user) {
var phpUser = <?php echo isset($_SESSION['fc_user']) ? json_encode($_SESSION['fc_user']) : 'null'; ?>;
if (user) {
if (!phpUser || phpUser.uid !== user.uid) {
user.getIdToken().then(function(token) {
var xhr = new XMLHttpRequest();
xhr.open('POST', '/api/session.php', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('action=login&uid=' + encodeURIComponent(user.uid) + '&email=' + encodeURIComponent(user.email) + '&token=' + encodeURIComponent(token));
xhr.onload = function() {
if (xhr.status === 200) {
if (!phpUser) { window.location.reload(); }
}
};
});
}
} else {
if (phpUser) {
var xhr = new XMLHttpRequest();
xhr.open('POST', '/api/session.php', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('action=logout');
xhr.onload = function() {
window.location.reload();
};
}
}
});
})();
</script>
<script src="/assets/fc-local.js" defer></script>
<!-- GSAP + ScrollTrigger (CDN) -->
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
+2 -2
View File
@@ -58,8 +58,8 @@ $langParamAmp = isset($lang) && $lang === 'de' ? '&lang=de' : '';
<li>
<a href="/login.php<?php echo $langParam; ?>" class="slide-hover-link" <?php echo $currentPage === 'login.php' ? 'aria-current="page"' : ''; ?>>
<span class="slide-hover-wrapper">
<span class="slide-text-main"><?php echo isset($_SESSION['fc_user']) ? (isset($lang) && $lang === 'de' ? 'Profil' : 'Profile') : (isset($lang) && $lang === 'de' ? 'Anmelden' : 'Login'); ?></span>
<span class="slide-text-clone" aria-hidden="true"><?php echo isset($_SESSION['fc_user']) ? (isset($lang) && $lang === 'de' ? 'Profil' : 'Profile') : (isset($lang) && $lang === 'de' ? 'Anmelden' : 'Login'); ?></span>
<span class="slide-text-main"><?php echo isset($lang) && $lang === 'de' ? 'Profil' : 'Profile'; ?></span>
<span class="slide-text-clone" aria-hidden="true"><?php echo isset($lang) && $lang === 'de' ? 'Profil' : 'Profile'; ?></span>
</span>
</a>
</li>