Horoskop in kozmicni dogodki: dnevno AI generiranje + scheduler
- Nov dnevni scheduler (server/scheduler.ts): vsak dan ob zagonu in vsakih 6h preveri/generira horoskope in kozmicne dogodke (prej samo enkrat ob zagonu) - Kozmicni dogodki so zdaj AI-generirani in dnevni (nova tabela cosmic_events + /api/cosmic-events), namesto hardcoded fiksnih datumov iz feb/mar 2026 - Naslovni horoskop widget bere pravi AI horoskop za danes (prej staticni tekst) - Frontend: staleTime 30min + refetchOnWindowFocus za dnevno osvezevanje
This commit is contained in:
@@ -1,8 +1,15 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { useLocation } from "wouter";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Star, Heart, Briefcase, TrendingUp, ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { SIGNS, ELEMENT_COLORS, getHoroscope, getRating } from "@/lib/horoscope-data";
|
||||
|
||||
interface AIHoroscope {
|
||||
signIndex: number;
|
||||
signName: string;
|
||||
general: string;
|
||||
}
|
||||
|
||||
function MiniStars({ count, max = 5 }: { count: number; max?: number }) {
|
||||
return (
|
||||
<div className="flex gap-px">
|
||||
@@ -18,6 +25,12 @@ export function HoroscopeWidget() {
|
||||
const [index, setIndex] = useState(0);
|
||||
const [paused, setPaused] = useState(false);
|
||||
|
||||
const { data: aiHoroscopes = [] } = useQuery<AIHoroscope[]>({
|
||||
queryKey: ["/api/horoscopes/today"],
|
||||
staleTime: 1000 * 60 * 30,
|
||||
refetchOnWindowFocus: true,
|
||||
});
|
||||
|
||||
const next = useCallback(() => setIndex((i) => (i + 1) % SIGNS.length), []);
|
||||
const prev = useCallback(() => setIndex((i) => (i - 1 + SIGNS.length) % SIGNS.length), []);
|
||||
|
||||
@@ -28,7 +41,9 @@ export function HoroscopeWidget() {
|
||||
}, [paused, next]);
|
||||
|
||||
const sign = SIGNS[index];
|
||||
const horoscope = getHoroscope(index);
|
||||
// Prefer the real AI-generated horoscope for today; fall back to static text until loaded.
|
||||
const aiForSign = aiHoroscopes.find((h) => h.signIndex === index);
|
||||
const generalText = aiForSign?.general || getHoroscope(index).general;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -88,7 +103,7 @@ export function HoroscopeWidget() {
|
||||
</div>
|
||||
|
||||
<div className="px-3 pb-3 pt-2 flex-1 flex flex-col justify-between">
|
||||
<p className="text-xs text-white/60 leading-relaxed line-clamp-5">{horoscope.general}</p>
|
||||
<p className="text-xs text-white/60 leading-relaxed line-clamp-5">{generalText}</p>
|
||||
<p className="text-[10px] text-amber-400 mt-2 group-hover:underline">Mehr lesen</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -77,7 +77,25 @@ function getEventColor(type: string) {
|
||||
}
|
||||
}
|
||||
|
||||
interface CosmicEvent {
|
||||
title: string;
|
||||
description: string;
|
||||
dateRange: string;
|
||||
icon: string;
|
||||
type: string;
|
||||
affectedSigns: string[];
|
||||
}
|
||||
|
||||
function AstroEventsSection() {
|
||||
const { data: apiEvents = [] } = useQuery<CosmicEvent[]>({
|
||||
queryKey: ["/api/cosmic-events"],
|
||||
staleTime: 1000 * 60 * 30,
|
||||
refetchOnWindowFocus: true,
|
||||
});
|
||||
|
||||
// Use live AI-generated events when available, otherwise fall back to static list.
|
||||
const events = apiEvents.length > 0 ? apiEvents : ASTRO_EVENTS;
|
||||
|
||||
return (
|
||||
<section className="mb-10" data-testid="section-astro-events">
|
||||
<h2 className="text-lg font-bold text-foreground flex items-center gap-2 mb-4">
|
||||
@@ -85,7 +103,7 @@ function AstroEventsSection() {
|
||||
Aktuelle kosmische Ereignisse
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{ASTRO_EVENTS.map((event, i) => {
|
||||
{events.map((event, i) => {
|
||||
const ec = getEventColor(event.type);
|
||||
return (
|
||||
<div
|
||||
@@ -378,6 +396,8 @@ export default function HoroscopePage() {
|
||||
|
||||
const { data: aiHoroscopes = [], isLoading: aiLoading } = useQuery<AIHoroscope[]>({
|
||||
queryKey: ["/api/horoscopes/today"],
|
||||
staleTime: 1000 * 60 * 30,
|
||||
refetchOnWindowFocus: true,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user