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:
parent
c73e035b3d
commit
149f32da6a
@ -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)} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user