Improve horoscope widget with rotation and modal selection

Update the horoscope widget to rotate through zodiac signs automatically every 6 seconds. Implement a modal that appears on click, allowing users to select a specific sign to view its daily horoscope.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 413891e8-d784-4bea-b9f5-91a5a68316b4
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: b7a5caf6-b4da-4196-a78e-1607379f364a
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
This commit is contained in:
sebastjanartic 2026-02-28 18:28:38 +00:00
parent c73e035b3d
commit 149f32da6a

View File

@ -1,4 +1,4 @@
import { useState } from "react";
import { useState, useEffect, useCallback } from "react";
import { Star, ChevronLeft, ChevronRight, X } from "lucide-react";
const SIGNS = [
@ -31,31 +31,13 @@ const DAILY_TEXTS = [
"Einfühlungsvermögen und Mitgefühl stärken heute Ihre Beziehungen.",
];
function getCurrentSignIndex(): number {
const now = new Date();
const month = now.getMonth() + 1;
const day = now.getDate();
if ((month === 3 && day >= 21) || (month === 4 && day <= 19)) return 0;
if ((month === 4 && day >= 20) || (month === 5 && day <= 20)) return 1;
if ((month === 5 && day >= 21) || (month === 6 && day <= 20)) return 2;
if ((month === 6 && day >= 21) || (month === 7 && day <= 22)) return 3;
if ((month === 7 && day >= 23) || (month === 8 && day <= 22)) return 4;
if ((month === 8 && day >= 23) || (month === 9 && day <= 22)) return 5;
if ((month === 9 && day >= 23) || (month === 10 && day <= 22)) return 6;
if ((month === 10 && day >= 23) || (month === 11 && day <= 21)) return 7;
if ((month === 11 && day >= 22) || (month === 12 && day <= 21)) return 8;
if ((month === 12 && day >= 22) || (month === 1 && day <= 19)) return 9;
if ((month === 1 && day >= 20) || (month === 2 && day <= 18)) return 10;
return 11;
}
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 }: { onClose: () => void }) {
const [selected, setSelected] = useState(getCurrentSignIndex());
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">
@ -108,31 +90,55 @@ function HoroscopeModal({ onClose }: { onClose: () => void }) {
export function HoroscopeWidget() {
const [showModal, setShowModal] = useState(false);
const currentIndex = getCurrentSignIndex();
const sign = SIGNS[currentIndex];
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 (
<>
<button
<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"
onMouseEnter={() => setPaused(true)}
onMouseLeave={() => setPaused(false)}
onClick={() => setShowModal(true)}
className="bg-card rounded-lg border border-card-border overflow-hidden h-full w-full text-left cursor-pointer group hover:border-primary/50 transition-colors"
data-testid="widget-horoscope"
>
<div className="p-3 flex items-center gap-2 border-b border-card-border">
<Star className="w-4 h-4 text-primary" />
<h3 className="font-bold text-card-foreground text-sm">Horoskop</h3>
</div>
<div className="p-4 flex items-center gap-3">
<span className="text-4xl">{sign.symbol}</span>
<div>
<p className="font-semibold text-card-foreground text-sm">{sign.name}</p>
<p className="text-[10px] text-muted-foreground">{sign.date}</p>
<p className="text-xs text-muted-foreground mt-1 line-clamp-2">{getDailyText(currentIndex)}</p>
<div className="p-4">
<div className="flex items-center gap-3 mb-2">
<span className="text-4xl transition-all duration-500">{sign.symbol}</span>
<div className="flex-1 min-w-0">
<p className="font-semibold text-card-foreground text-sm">{sign.name}</p>
<p className="text-[10px] text-muted-foreground">{sign.date}</p>
</div>
</div>
<p className="text-xs text-muted-foreground mt-1 line-clamp-2 leading-relaxed">{getDailyText(index)}</p>
<div className="flex justify-center gap-1 mt-3">
{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>
</button>
</div>
{showModal && <HoroscopeModal onClose={() => setShowModal(false)} />}
{showModal && <HoroscopeModal initialSign={index} onClose={() => setShowModal(false)} />}
</>
);
}