feat(auth-firebase): implement Phase 2 integration - Firebase Auth, Session Sync, Favorites Grid, and Firestore Newsletter
This commit is contained in:
+63
-2
@@ -7,6 +7,11 @@ $copy = [
|
||||
'not_found' => 'Recipe not found',
|
||||
'not_found_sub' => 'Try searching for something else.',
|
||||
'back_home' => '← Back to all recipes',
|
||||
'nutrition' => 'Nutrition',
|
||||
'calories' => 'Calories',
|
||||
'protein' => 'Protein',
|
||||
'carbs' => 'Carbs',
|
||||
'fat' => 'Fat',
|
||||
'ingredients' => 'Ingredients',
|
||||
'utensils' => 'Utensils',
|
||||
'steps' => 'Steps',
|
||||
@@ -22,6 +27,11 @@ $copy = [
|
||||
'not_found' => 'Rezept nicht gefunden',
|
||||
'not_found_sub' => 'Versuche eine andere Suche.',
|
||||
'back_home' => '← Zurück zu allen Rezepten',
|
||||
'nutrition' => 'Nährwerte',
|
||||
'calories' => 'Kalorien',
|
||||
'protein' => 'Protein',
|
||||
'carbs' => 'Kohlenhydrate',
|
||||
'fat' => 'Fett',
|
||||
'ingredients' => 'Zutaten',
|
||||
'utensils' => 'Küchenwerkzeug',
|
||||
'steps' => 'Schritte',
|
||||
@@ -78,7 +88,14 @@ include __DIR__ . '/partials/header.php';
|
||||
<?php if (!empty($recipe['category'])): ?>
|
||||
<p class="pill pill--gold"><?php echo e($recipe['category']); ?></p>
|
||||
<?php endif; ?>
|
||||
<h1><?php echo e($recipe['title']); ?></h1>
|
||||
<div class="recipe__title-row">
|
||||
<h1><?php echo e($recipe['title']); ?></h1>
|
||||
<button class="btn-favorite btn-favorite--large" data-slug="<?php echo e($recipe['slug']); ?>" aria-label="Save to favorites">
|
||||
<svg viewBox="0 0 24 24" stroke="currentColor" fill="none">
|
||||
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<p class="lede"><?php echo e($recipe['description']); ?></p>
|
||||
<div class="meta meta--row">
|
||||
<span><?php echo e($t['prep']); ?>: <?php echo format_minutes((int)$recipe['prep_time']); ?></span>
|
||||
@@ -95,6 +112,35 @@ include __DIR__ . '/partials/header.php';
|
||||
</div>
|
||||
|
||||
<div class="recipe__body">
|
||||
|
||||
<?php if (isset($recipe['nutrition']) && ($recipe['nutrition']['calories'] > 0 || $recipe['nutrition']['protein'] > 0 || $recipe['nutrition']['carbs'] > 0 || $recipe['nutrition']['fat'] > 0)): ?>
|
||||
<section class="panel reveal-target" id="nutrition-panel">
|
||||
<h2><?php echo e($t['nutrition']); ?></h2>
|
||||
<div class="g-row nutrition-widget">
|
||||
<?php
|
||||
$nutri = $recipe['nutrition'];
|
||||
$macros = [
|
||||
['label' => $t['calories'], 'value' => $nutri['calories'], 'unit' => 'kcal', 'pct' => min(100, round(($nutri['calories']/2000)*100))],
|
||||
['label' => $t['protein'], 'value' => $nutri['protein'], 'unit' => 'g', 'pct' => min(100, round(($nutri['protein']/50)*100))],
|
||||
['label' => $t['carbs'], 'value' => $nutri['carbs'], 'unit' => 'g', 'pct' => min(100, round(($nutri['carbs']/260)*100))],
|
||||
['label' => $t['fat'], 'value' => $nutri['fat'], 'unit' => 'g', 'pct' => min(100, round(($nutri['fat']/70)*100))]
|
||||
];
|
||||
?>
|
||||
<?php foreach ($macros as $m): ?>
|
||||
<div class="g-col nutrition-item">
|
||||
<div class="nutrition-item__header">
|
||||
<span class="nutrition-label"><?php echo e($m['label']); ?></span>
|
||||
<span class="nutrition-value"><?php echo e($m['value']); ?><?php echo $m['unit']; ?></span>
|
||||
</div>
|
||||
<div class="nutrition-bar">
|
||||
<div class="nutrition-fill" data-width="<?php echo $m['pct']; ?>%" style="width: 0;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<section class="panel">
|
||||
<h2><?php echo e($t['ingredients']); ?></h2>
|
||||
<ul class="ingredients">
|
||||
@@ -136,6 +182,11 @@ include __DIR__ . '/partials/header.php';
|
||||
<article class="card card--bare reveal-target">
|
||||
<a href="/recipe.php?<?php echo http_build_query(['slug' => $other['slug'], 'lang' => $lang]); ?>" class="card__image">
|
||||
<img src="<?php echo e($other['hero']); ?>" alt="<?php echo e($other['title']); ?>" loading="lazy">
|
||||
<button class="btn-favorite" data-slug="<?php echo e($other['slug']); ?>" aria-label="Save to favorites" onclick="event.preventDefault(); event.stopPropagation();">
|
||||
<svg viewBox="0 0 24 24" stroke="currentColor" fill="none">
|
||||
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</a>
|
||||
<div class="card__body">
|
||||
<h4><a href="/recipe.php?<?php echo http_build_query(['slug' => $other['slug'], 'lang' => $lang]); ?>"><?php echo e($other['title']); ?></a></h4>
|
||||
@@ -158,7 +209,17 @@ include __DIR__ . '/partials/header.php';
|
||||
if ('IntersectionObserver' in window) {
|
||||
var obs = new IntersectionObserver(function(entries) {
|
||||
entries.forEach(function(e) {
|
||||
if (e.isIntersecting) { e.target.classList.add('revealed'); obs.unobserve(e.target); }
|
||||
if (e.isIntersecting) {
|
||||
e.target.classList.add('revealed');
|
||||
obs.unobserve(e.target);
|
||||
|
||||
// Trigger nutrition bar animations if this is the nutrition panel
|
||||
if (e.target.id === 'nutrition-panel') {
|
||||
e.target.querySelectorAll('.nutrition-fill').forEach(function(fill) {
|
||||
fill.style.width = fill.getAttribute('data-width');
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.08 });
|
||||
document.querySelectorAll('.reveal-target').forEach(function(el) { obs.observe(el); });
|
||||
|
||||
Reference in New Issue
Block a user