feat(ai-search): optimize dev server, add qdrant boot sync, fix orb overflow
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 6s
Build & Deploy / 🧪 QA (push) Successful in 1m0s
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🏗️ Build (push) Has been cancelled
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 6s
Build & Deploy / 🧪 QA (push) Successful in 1m0s
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Post-Deploy Verification (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
Build & Deploy / 🏗️ Build (push) Has been cancelled
This commit is contained in:
@@ -6,11 +6,11 @@ import { useTranslations, useLocale } from 'next-intl';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useAnalytics } from '../analytics/useAnalytics';
|
||||
import { AnalyticsEvents } from '../analytics/analytics-events';
|
||||
import AIOrb from '../search/AIOrb';
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
import { AISearchResults } from '../search/AISearchResults';
|
||||
const HeroIllustration = dynamic(() => import('./HeroIllustration'), { ssr: false });
|
||||
const AIOrb = dynamic(() => import('../search/AIOrb'), { ssr: false });
|
||||
|
||||
export default function Hero({ data }: { data?: any }) {
|
||||
const t = useTranslations('Home.hero');
|
||||
@@ -19,6 +19,62 @@ export default function Hero({ data }: { data?: any }) {
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [isSearchOpen, setIsSearchOpen] = useState(false);
|
||||
const [heroPlaceholder, setHeroPlaceholder] = useState(
|
||||
'Projekt beschreiben oder Kabel suchen...',
|
||||
);
|
||||
const typingRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
const HERO_PLACEHOLDERS = [
|
||||
'Querschnittsberechnung für 110kV Trasse', // Hochspannung
|
||||
'Wie schwer ist NAYY 4x150?',
|
||||
'Ich plane einen Solarpark, was brauche ich?', // Projekt Solar
|
||||
'Unterschied zwischen N2XSY und NAY2XSY?', // Fach
|
||||
'Mittelspannungskabel für Windkraftanlage', // Windpark
|
||||
'Welches Aluminiumkabel für 20kV?', // Mittelspannung
|
||||
];
|
||||
|
||||
// Typing animation for the hero search placeholder
|
||||
useEffect(() => {
|
||||
if (searchQuery) {
|
||||
setHeroPlaceholder('Projekt beschreiben oder Kabel suchen...');
|
||||
return;
|
||||
}
|
||||
|
||||
let textIdx = 0;
|
||||
let charIdx = 0;
|
||||
let deleting = false;
|
||||
|
||||
const tick = () => {
|
||||
const fullText = HERO_PLACEHOLDERS[textIdx];
|
||||
|
||||
if (deleting) {
|
||||
charIdx--;
|
||||
setHeroPlaceholder(fullText.substring(0, charIdx));
|
||||
} else {
|
||||
charIdx++;
|
||||
setHeroPlaceholder(fullText.substring(0, charIdx));
|
||||
}
|
||||
|
||||
let delay = deleting ? 30 : 70;
|
||||
|
||||
if (!deleting && charIdx === fullText.length) {
|
||||
delay = 2500;
|
||||
deleting = true;
|
||||
} else if (deleting && charIdx === 0) {
|
||||
deleting = false;
|
||||
textIdx = (textIdx + 1) % HERO_PLACEHOLDERS.length;
|
||||
delay = 400;
|
||||
}
|
||||
|
||||
typingRef.current = setTimeout(tick, delay);
|
||||
};
|
||||
|
||||
typingRef.current = setTimeout(tick, 1500);
|
||||
|
||||
return () => {
|
||||
if (typingRef.current) clearTimeout(typingRef.current);
|
||||
};
|
||||
}, [searchQuery]);
|
||||
|
||||
const handleSearchSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
@@ -79,15 +135,16 @@ export default function Hero({ data }: { data?: any }) {
|
||||
onSubmit={handleSearchSubmit}
|
||||
className="w-full max-w-2xl bg-white/10 backdrop-blur-md border border-white/20 rounded-2xl p-2 flex items-center mt-8 mb-10 transition-all focus-within:bg-white/15 focus-within:border-accent shadow-lg relative"
|
||||
>
|
||||
<div className="absolute left-2 w-12 h-12 flex items-center justify-center opacity-80 pointer-events-none">
|
||||
<div className="absolute left-1 w-20 h-20 flex items-center justify-center z-10 overflow-visible">
|
||||
<AIOrb isThinking={false} />
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder="Projekt beschreiben oder Kabel suchen..."
|
||||
className="flex-1 bg-transparent border-none text-white pl-12 pr-2 py-4 placeholder:text-white/50 focus:outline-none text-lg lg:text-xl"
|
||||
placeholder={heroPlaceholder}
|
||||
className="flex-1 bg-transparent border-none text-white pl-20 pr-2 py-4 placeholder:text-white/50 focus:outline-none text-lg lg:text-xl"
|
||||
autoFocus
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
|
||||
@@ -74,11 +74,14 @@ export default async function RecentPosts({ locale, data }: RecentPostsProps) {
|
||||
suppressHydrationWarning
|
||||
className="px-3 py-1 text-white/80 text-[10px] md:text-xs font-bold uppercase tracking-widest border border-white/20 rounded-full bg-white/10 backdrop-blur-md"
|
||||
>
|
||||
{new Date(post.frontmatter.date).toLocaleDateString(locale || 'de', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
})}
|
||||
{new Date(post.frontmatter.date).toLocaleDateString(
|
||||
locale?.length === 2 ? locale : 'de',
|
||||
{
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
},
|
||||
)}
|
||||
</time>
|
||||
{(new Date(post.frontmatter.date) > new Date() ||
|
||||
post.frontmatter.public === false) && (
|
||||
|
||||
Reference in New Issue
Block a user