Apply `objectPosition: "center 25%"` to all relevant image elements across the client application to ensure faces are consistently visible and not cropped out by the `object-cover` CSS property. This change impacts components in `photo-gallery`, `article`, `category`, and `home` pages. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 413891e8-d784-4bea-b9f5-91a5a68316b4 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 6fbb209e-0312-4027-963a-b6aa098cc025 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/413891e8-d784-4bea-b9f5-91a5a68316b4/RVXhOPb Replit-Helium-Checkpoint-Created: true
142 lines
6.7 KiB
TypeScript
142 lines
6.7 KiB
TypeScript
import { useState, useEffect, useCallback } from "react";
|
||
import { useLocation } from "wouter";
|
||
import { Star, ChevronLeft, ChevronRight, X } from "lucide-react";
|
||
|
||
const SIGNS = [
|
||
{ name: "Widder", symbol: "♈", date: "21.03 – 19.04", element: "Feuer" },
|
||
{ name: "Stier", symbol: "♉", date: "20.04 – 20.05", element: "Erde" },
|
||
{ name: "Zwillinge", symbol: "♊", date: "21.05 – 20.06", element: "Luft" },
|
||
{ name: "Krebs", symbol: "♋", date: "21.06 – 22.07", element: "Wasser" },
|
||
{ name: "Löwe", symbol: "♌", date: "23.07 – 22.08", element: "Feuer" },
|
||
{ name: "Jungfrau", symbol: "♍", date: "23.08 – 22.09", element: "Erde" },
|
||
{ name: "Waage", symbol: "♎", date: "23.09 – 22.10", element: "Luft" },
|
||
{ name: "Skorpion", symbol: "♏", date: "23.10 – 21.11", element: "Wasser" },
|
||
{ name: "Schütze", symbol: "♐", date: "22.11 – 21.12", element: "Feuer" },
|
||
{ name: "Steinbock", symbol: "♑", date: "22.12 – 19.01", element: "Erde" },
|
||
{ name: "Wassermann", symbol: "♒", date: "20.01 – 18.02", element: "Luft" },
|
||
{ name: "Fische", symbol: "♓", date: "19.02 – 20.03", element: "Wasser" },
|
||
];
|
||
|
||
const DAILY_TEXTS = [
|
||
"Ein wunderbarer Tag für neue Begegnungen. Die Sterne stehen günstig für spontane Entscheidungen.",
|
||
"Heute ist Geduld gefragt. Lassen Sie sich Zeit und genießen Sie die kleinen Freuden des Alltags.",
|
||
"Kommunikation steht im Vordergrund. Ein gutes Gespräch kann heute Wunder wirken.",
|
||
"Vertrauen Sie auf Ihre Intuition. Ihr Bauchgefühl weist Ihnen den richtigen Weg.",
|
||
"Kreativität und Mut werden heute belohnt. Zeigen Sie, was in Ihnen steckt!",
|
||
"Ordnung und Struktur bringen heute den Erfolg. Nutzen Sie die Energie für wichtige Aufgaben.",
|
||
"Harmonie und Ausgeglichenheit bestimmen den Tag. Genießen Sie die Zeit mit Ihren Liebsten.",
|
||
"Tiefgründige Erkenntnisse warten auf Sie. Nehmen Sie sich Zeit für Reflexion.",
|
||
"Abenteuerlust liegt in der Luft! Planen Sie etwas Besonderes für heute.",
|
||
"Disziplin und Ausdauer zahlen sich aus. Bleiben Sie an Ihren Zielen dran.",
|
||
"Originelle Ideen kommen heute von ganz allein. Lassen Sie Ihrer Fantasie freien Lauf.",
|
||
"Einfühlungsvermögen und Mitgefühl stärken heute Ihre Beziehungen.",
|
||
];
|
||
|
||
function getDailyText(signIndex: number): string {
|
||
const dayOfYear = Math.floor((Date.now() - new Date(new Date().getFullYear(), 0, 0).getTime()) / 86400000);
|
||
return DAILY_TEXTS[(signIndex + dayOfYear) % DAILY_TEXTS.length];
|
||
}
|
||
|
||
function HoroscopeModal({ onClose, initialSign }: { onClose: () => void; initialSign: number }) {
|
||
const [selected, setSelected] = useState(initialSign);
|
||
|
||
return (
|
||
<div className="fixed inset-0 z-50 bg-black/80 flex items-center justify-center p-4" onClick={onClose} data-testid="modal-horoscope">
|
||
<div
|
||
className="bg-card rounded-xl border border-card-border max-w-lg w-full max-h-[80vh] overflow-y-auto p-6"
|
||
onClick={(e) => e.stopPropagation()}
|
||
>
|
||
<div className="flex items-center justify-between mb-4">
|
||
<h2 className="text-lg font-bold text-foreground flex items-center gap-2">
|
||
<Star className="w-5 h-5 text-primary" />
|
||
Tageshoroskop
|
||
</h2>
|
||
<button onClick={onClose} className="text-muted-foreground hover:text-foreground" data-testid="button-horoscope-close">
|
||
<X className="w-5 h-5" />
|
||
</button>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-4 sm:grid-cols-6 gap-2 mb-6">
|
||
{SIGNS.map((sign, i) => (
|
||
<button
|
||
key={sign.name}
|
||
onClick={() => setSelected(i)}
|
||
className={`flex flex-col items-center p-2 rounded-lg transition-colors ${
|
||
i === selected ? "bg-primary/20 border border-primary" : "hover:bg-muted border border-transparent"
|
||
}`}
|
||
data-testid={`button-sign-${sign.name}`}
|
||
>
|
||
<span className="text-2xl">{sign.symbol}</span>
|
||
<span className="text-[10px] text-muted-foreground mt-1">{sign.name}</span>
|
||
</button>
|
||
))}
|
||
</div>
|
||
|
||
<div className="bg-muted/50 rounded-lg p-4">
|
||
<div className="flex items-center gap-2 mb-2">
|
||
<span className="text-3xl">{SIGNS[selected].symbol}</span>
|
||
<div>
|
||
<h3 className="font-bold text-foreground">{SIGNS[selected].name}</h3>
|
||
<p className="text-xs text-muted-foreground">{SIGNS[selected].date} · {SIGNS[selected].element}</p>
|
||
</div>
|
||
</div>
|
||
<p className="text-sm text-foreground/80 leading-relaxed mt-3">
|
||
{getDailyText(selected)}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export function HoroscopeWidget() {
|
||
const [showModal, setShowModal] = useState(false);
|
||
const [index, setIndex] = useState(0);
|
||
const [paused, setPaused] = useState(false);
|
||
|
||
const next = useCallback(() => setIndex((i) => (i + 1) % SIGNS.length), []);
|
||
|
||
useEffect(() => {
|
||
if (paused) return;
|
||
const timer = setInterval(next, 6000);
|
||
return () => clearInterval(timer);
|
||
}, [paused, next]);
|
||
|
||
const sign = SIGNS[index];
|
||
|
||
return (
|
||
<>
|
||
<div
|
||
className="bg-card rounded-lg border border-card-border overflow-hidden h-full w-full cursor-pointer group hover:border-primary/50 transition-colors flex flex-col"
|
||
onMouseEnter={() => setPaused(true)}
|
||
onMouseLeave={() => setPaused(false)}
|
||
onClick={() => setShowModal(true)}
|
||
data-testid="widget-horoscope"
|
||
>
|
||
<div className="p-3 flex items-center gap-2 border-b border-card-border flex-shrink-0">
|
||
<Star className="w-4 h-4 text-primary" />
|
||
<h3 className="font-bold text-card-foreground text-sm">Horoskop</h3>
|
||
</div>
|
||
<div className="p-5 flex-1 flex flex-col justify-center items-center text-center">
|
||
<span className="text-7xl mb-4 transition-all duration-500 block">{sign.symbol}</span>
|
||
<p className="font-bold text-card-foreground text-lg">{sign.name}</p>
|
||
<p className="text-xs text-muted-foreground mt-1">{sign.date}</p>
|
||
<p className="text-sm text-muted-foreground mt-4 leading-relaxed">{getDailyText(index)}</p>
|
||
<div className="flex justify-center gap-1 mt-5">
|
||
{SIGNS.map((_, i) => (
|
||
<button
|
||
key={i}
|
||
onClick={(e) => { e.stopPropagation(); setIndex(i); }}
|
||
className={`w-1.5 h-1.5 rounded-full transition-all ${i === index ? "bg-primary w-3" : "bg-muted-foreground/30 hover:bg-muted-foreground/50"}`}
|
||
data-testid={`button-horoscope-dot-${i}`}
|
||
/>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{showModal && <HoroscopeModal initialSign={index} onClose={() => setShowModal(false)} />}
|
||
</>
|
||
);
|
||
}
|