Hero: ehrliche Fakten statt Zahlenleiste
Die Leiste mit Projektzähler (/api/projects), "Am Markt"-Ticker, 99,9 % Systemverfügbarkeit und Flagge ist ersetzt durch drei belegbare Fakten ohne Count-up: Falkenstein (Server in Deutschland), Live-Vorschau (im Kundenportal) und ab 20 € (Hosting im Monat). Einspaltig auf dem Handy, ab md drei Spalten. Der zweite Button "Projekte ansehen" ist entfernt, ebenso Projekt-Query, FOUNDING_DATE und src/lib/projects.ts. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
79c3551934
commit
a27b1fe255
3 files changed
+133
-144
No files matched your search
+14
-116
@@ -1,25 +1,12 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { ArrowRight } from "lucide-react";
|
||||
import React, { useState, useEffect, useRef, lazy, Suspense } from "react";
|
||||
import { lazy, Suspense } from "react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import CountUp from "@/components/CountUp";
|
||||
import { trackCtaClick } from "@/lib/analytics";
|
||||
import { fetchProjects } from "@/lib/projects";
|
||||
|
||||
// WebGL-Effekte als eigene Chunks laden, damit der Text nicht auf three.js/ogl warten muss
|
||||
const Silk = lazy(() => import("@/components/Silk"));
|
||||
|
||||
const SPARKLE_SVG = (
|
||||
<svg className="btn-svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M9.813 15.904 9 18.75l-.813-2.846a4.5 4.5 0 0 0-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 0 0 3.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 0 0 3.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 0 0-3.09 3.09ZM18.259 8.715 18 9.75l-.259-1.035a3.375 3.375 0 0 0-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 0 0 2.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 0 0 2.456 2.456L21.75 6l-1.035.259a3.375 3.375 0 0 0-2.456 2.456ZM16.894 20.567 16.5 21.75l-.394-1.183a2.25 2.25 0 0 0-1.423-1.423L13.5 18.75l1.183-.394a2.25 2.25 0 0 0 1.423-1.423l.394-1.183.394 1.183a2.25 2.25 0 0 0 1.423 1.423l1.183.394-1.183.394a2.25 2.25 0 0 0-1.423 1.423Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
function DemoButtonLetters({ text }: { text: string }) {
|
||||
const chars = text.split("");
|
||||
return (
|
||||
@@ -33,49 +20,16 @@ function DemoButtonLetters({ text }: { text: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
const FOUNDING_DATE = new Date("2026-01-25"); // Samstag, 25. Januar 2026
|
||||
// Drei belegbare Fakten statt Zahlen: Wort-Werte, deshalb kleiner gesetzt als die frühere Zahlenleiste.
|
||||
const FAKTEN = [
|
||||
{ wert: "Falkenstein", label: "Server in Deutschland" },
|
||||
{ wert: "Live-Vorschau", label: "im Kundenportal" },
|
||||
{ wert: "ab 20 €", label: "Hosting im Monat" },
|
||||
];
|
||||
|
||||
const Hero = () => {
|
||||
const navigate = useNavigate();
|
||||
const { resolvedTheme } = useTheme();
|
||||
const [companyAge, setCompanyAge] = useState("");
|
||||
const secondBtnRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
// Projektzähler kommt live aus Gitea – gleicher queryKey wie im Abschnitt
|
||||
// „Projekte", react-query teilt sich den einen Request.
|
||||
const { data: projectsSnapshot } = useQuery({
|
||||
queryKey: ["projects"],
|
||||
queryFn: ({ signal }) => fetchProjects(signal),
|
||||
staleTime: 5 * 60 * 1000,
|
||||
refetchOnWindowFocus: false,
|
||||
retry: 1,
|
||||
});
|
||||
const projectCount = projectsSnapshot?.total ?? 10;
|
||||
|
||||
useEffect(() => {
|
||||
const calculateAge = () => {
|
||||
const now = new Date();
|
||||
const diff = now.getTime() - FOUNDING_DATE.getTime();
|
||||
|
||||
const totalSeconds = Math.floor(diff / 1000);
|
||||
const days = Math.floor(totalSeconds / (60 * 60 * 24));
|
||||
const hours = Math.floor((totalSeconds % (60 * 60 * 24)) / (60 * 60));
|
||||
|
||||
const years = Math.floor(days / 365);
|
||||
const remainingDays = days % 365;
|
||||
|
||||
if (years > 0) {
|
||||
setCompanyAge(`${years}J ${remainingDays}T ${hours}h`);
|
||||
} else {
|
||||
setCompanyAge(`${days}T ${hours}h`);
|
||||
}
|
||||
};
|
||||
|
||||
calculateAge();
|
||||
const interval = setInterval(calculateAge, 60 * 60 * 1000); // Update every hour (only days/hours shown)
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
// ── Silk-Hintergrund Farben ──
|
||||
const isDark = resolvedTheme === "dark";
|
||||
@@ -149,32 +103,6 @@ const Hero = () => {
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div className="btn-wrapper w-full sm:w-auto">
|
||||
<button
|
||||
ref={secondBtnRef}
|
||||
type="button"
|
||||
className="btn w-full sm:w-auto justify-center"
|
||||
onClick={() => {
|
||||
const el = document.getElementById("projects");
|
||||
if (el) el.scrollIntoView({ behavior: "smooth" });
|
||||
else navigate("/#projects");
|
||||
}}
|
||||
aria-label="Projekte ansehen"
|
||||
>
|
||||
{SPARKLE_SVG}
|
||||
<div className="txt-wrapper">
|
||||
<span className="txt-width-helper" aria-hidden="true">
|
||||
<DemoButtonLetters text="Projekte ansehen" />
|
||||
</span>
|
||||
<div className="txt-1">
|
||||
<DemoButtonLetters text="Projekte ansehen" />
|
||||
</div>
|
||||
<div className="txt-2">
|
||||
<DemoButtonLetters text="Wird geladen..." />
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Trust Line */}
|
||||
@@ -184,46 +112,16 @@ const Hero = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom Stats Row */}
|
||||
{/* Bottom Facts Row */}
|
||||
<div className="container mx-auto px-6 mt-auto pb-12 pt-20 relative z-10">
|
||||
<div className="divider mb-12" />
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 md:gap-12">
|
||||
<div className="animate-fade-in" style={{ animationDelay: '0.6s' }}>
|
||||
<div className="stat-number text-4xl md:text-5xl text-foreground mb-2">
|
||||
<CountUp
|
||||
from={0}
|
||||
to={projectCount}
|
||||
direction="up"
|
||||
duration={1.2}
|
||||
className="count-up-text"
|
||||
startWhen={true}
|
||||
suffix="+"
|
||||
/>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 md:gap-12">
|
||||
{FAKTEN.map((fakt, i) => (
|
||||
<div key={fakt.wert} className="animate-fade-in" style={{ animationDelay: `${0.6 + i * 0.1}s` }}>
|
||||
<div className="stat-number text-3xl lg:text-4xl xl:text-5xl text-foreground mb-2">{fakt.wert}</div>
|
||||
<div className="label-tag">{fakt.label}</div>
|
||||
</div>
|
||||
<div className="label-tag">Projekte</div>
|
||||
</div>
|
||||
<div className="animate-fade-in" style={{ animationDelay: '0.7s' }}>
|
||||
<div className="stat-number text-4xl md:text-5xl text-foreground mb-2">{companyAge}</div>
|
||||
<div className="label-tag">Am Markt</div>
|
||||
</div>
|
||||
<div className="animate-fade-in" style={{ animationDelay: '0.8s' }}>
|
||||
<div className="stat-number text-4xl md:text-5xl text-foreground mb-2">
|
||||
<CountUp
|
||||
from={0}
|
||||
to={99.9}
|
||||
direction="up"
|
||||
duration={1.5}
|
||||
className="count-up-text"
|
||||
startWhen={true}
|
||||
suffix="%"
|
||||
/>
|
||||
</div>
|
||||
<div className="label-tag">Systemverfügbarkeit</div>
|
||||
</div>
|
||||
<div className="animate-fade-in" style={{ animationDelay: '0.9s' }}>
|
||||
<div className="stat-number text-4xl md:text-5xl text-foreground mb-2">🇩🇪</div>
|
||||
<div className="label-tag">Serverstandort in Deutschland</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in new issue
Block a user