faq parsing info styles update
This commit is contained in:
+63
-17
@@ -7,11 +7,10 @@ import {
|
|||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionTrigger,
|
AccordionTrigger,
|
||||||
} from "@/components/ui/accordion";
|
} from "@/components/ui/accordion";
|
||||||
import { Loader2 } from "lucide-react";
|
import { Loader2, HelpCircle } from "lucide-react";
|
||||||
import { Dictionary } from "@/lib/IDict";
|
import { Dictionary } from "@/lib/IDict";
|
||||||
import { pb } from "@/lib/pb";
|
import { pb } from "@/lib/pb";
|
||||||
|
|
||||||
// Модель для FAQ из PocketBase
|
|
||||||
interface PBFaq {
|
interface PBFaq {
|
||||||
id: string;
|
id: string;
|
||||||
question: string;
|
question: string;
|
||||||
@@ -42,38 +41,85 @@ export function FAQ({ dict, lang }: { dict: Dictionary["faq"]; lang: string }) {
|
|||||||
fetchFaqs();
|
fetchFaqs();
|
||||||
}, [lang]);
|
}, [lang]);
|
||||||
|
|
||||||
|
// Функция для красивого рендеринга текста с поддержкой переносов и буллитов
|
||||||
|
const formatAnswer = (text: string) => {
|
||||||
|
return text.split("\n").map((line, i) => {
|
||||||
|
const trimmedLine = line.trim();
|
||||||
|
|
||||||
|
// Если строка начинается с буллита
|
||||||
|
if (trimmedLine.startsWith("•")) {
|
||||||
|
return (
|
||||||
|
<div key={i} className="flex gap-3 mb-2 pl-2 group">
|
||||||
|
<span className="text-secondary font-bold group-hover:animate-pulse">
|
||||||
|
•
|
||||||
|
</span>
|
||||||
|
<span className="text-muted-foreground/90">
|
||||||
|
{trimmedLine.replace("•", "").trim()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если строка пустая — это отступ между абзацами
|
||||||
|
if (!trimmedLine) return <div key={i} className="h-4" />;
|
||||||
|
|
||||||
|
// Обычный параграф
|
||||||
|
return (
|
||||||
|
<p
|
||||||
|
key={i}
|
||||||
|
className="mb-3 last:mb-0 text-muted-foreground/90 leading-relaxed"
|
||||||
|
>
|
||||||
|
{line}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
id="faq"
|
id="faq"
|
||||||
className="py-20 px-4 container mx-auto max-w-3xl min-h-[400px]"
|
className="py-32 px-4 container mx-auto max-w-4xl relative overflow-hidden"
|
||||||
>
|
>
|
||||||
<h2 className="text-3xl font-bold mb-10 text-center">{dict.title}</h2>
|
{/* Мягкий фон */}
|
||||||
|
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[100vw] h-[500px] bg-primary/5 blur-[120px] rounded-full pointer-events-none -z-10" />
|
||||||
|
|
||||||
|
<div className="flex flex-col items-center mb-16">
|
||||||
|
<div className="bg-primary/10 p-3 rounded-2xl mb-6 border border-primary/20">
|
||||||
|
<HelpCircle className="w-8 h-8 text-primary" />
|
||||||
|
</div>
|
||||||
|
<h2 className="text-4xl md:text-5xl font-black tracking-tight text-center">
|
||||||
|
{dict.title}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="flex flex-col items-center justify-center py-10 text-primary opacity-50">
|
<div className="flex flex-col items-center justify-center py-20 text-primary opacity-50">
|
||||||
<Loader2 className="w-8 h-8 animate-spin mb-4" />
|
<Loader2 className="w-10 h-10 animate-spin mb-4" />
|
||||||
<span className="text-sm tracking-widest uppercase font-mono">
|
<span className="text-xs tracking-[0.3em] uppercase font-mono">
|
||||||
Loading_Data...
|
Synchronizing_Knowledge...
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : faqs.length === 0 ? (
|
) : faqs.length === 0 ? (
|
||||||
<p className="text-center text-muted-foreground font-mono">
|
<div className="text-center py-20 border border-dashed border-border/50 rounded-[2.5rem] bg-card/20">
|
||||||
{/* Если база пуста, можно вывести сообщение или fallback */}0 records
|
<p className="text-muted-foreground font-mono text-sm uppercase tracking-widest">
|
||||||
found in database.
|
0 protocols found in cache.
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Accordion type="single" collapsible className="w-full">
|
<Accordion type="single" collapsible className="w-full space-y-4">
|
||||||
{faqs.map((faq, index) => (
|
{faqs.map((faq) => (
|
||||||
<AccordionItem
|
<AccordionItem
|
||||||
key={faq.id}
|
key={faq.id}
|
||||||
value={`item-${faq.id}`}
|
value={`item-${faq.id}`}
|
||||||
className="border-border/50"
|
className="border border-border/40 bg-card/30 backdrop-blur-md rounded-[1.5rem] px-6 transition-all duration-300 hover:border-primary/30 data-[state=open]:border-primary/50 data-[state=open]:bg-card/50 shadow-sm"
|
||||||
>
|
>
|
||||||
<AccordionTrigger className="text-left text-foreground hover:text-primary transition-colors font-medium">
|
<AccordionTrigger className="text-left text-foreground hover:no-underline hover:text-primary transition-colors font-bold text-lg md:text-xl py-6">
|
||||||
{faq.question}
|
{faq.question}
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent className="text-muted-foreground leading-relaxed">
|
<AccordionContent className="pb-8 text-base antialiased">
|
||||||
{faq.answer}
|
<div className="border-t border-border/30 pt-6">
|
||||||
|
{formatAnswer(faq.answer)}
|
||||||
|
</div>
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user