127 lines
4.5 KiB
TypeScript
127 lines
4.5 KiB
TypeScript
'use client';
|
|
import { Bot, Search } from 'lucide-react';
|
|
import { useState, useCallback } from 'react';
|
|
import Link from 'next/link';
|
|
|
|
import { Input } from '@/components/ui/input';
|
|
import { Button } from '@/components/ui/button';
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardFooter,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from '@/components/ui/card';
|
|
import { CategoryFilters } from '@/components/CategoryFilters';
|
|
import { useScopedI18n } from '@/locales/client';
|
|
import { useQuestionnaire } from '@/hooks/useQuestionnaire';
|
|
import { Questionnaire, QuestionnaireCategory } from '@/types';
|
|
|
|
export default function QuestionnaireList() {
|
|
const questionnaires = useQuestionnaire();
|
|
const t = useScopedI18n('component.questionnaire.list');
|
|
const [searchQuery, setSearchQuery] = useState('');
|
|
const [selectedCategory, setSelectedCategory] =
|
|
useState<QuestionnaireCategory | null>(null);
|
|
|
|
const handleCategoryChange = useCallback(
|
|
(category: QuestionnaireCategory | null) => {
|
|
setSelectedCategory(category);
|
|
},
|
|
[],
|
|
);
|
|
|
|
// Filter questionnaires based on search terms and the selected purpose.
|
|
const filteredQuestionnaires = (questionnaires as Questionnaire[]).filter((q) => {
|
|
// Text search filtering
|
|
const matchesSearch =
|
|
q.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
q.description.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
q.tags.some((tag) =>
|
|
tag.toLowerCase().includes(searchQuery.toLowerCase())
|
|
);
|
|
|
|
const matchesCategory =
|
|
selectedCategory === null || q.category === selectedCategory;
|
|
|
|
return matchesSearch && matchesCategory;
|
|
});
|
|
|
|
return (
|
|
<div className="flex flex-col min-h-screen">
|
|
<main className="flex-1">
|
|
<div className="container px-4 py-6 max-w-6xl mx-auto">
|
|
<h1 className="text-2xl font-medium mb-6">{t('title')}</h1>
|
|
|
|
<div className="mb-5 flex gap-3 border bg-muted/30 p-3 text-sm leading-6 text-muted-foreground">
|
|
<Bot className="mt-0.5 h-4 w-4 shrink-0 text-foreground" />
|
|
<p>
|
|
测评完成后可以复制完整记录给 ChatGPT 解读;如果想长期追踪变化,可以在对话中明确说“请记住我的测评背景和后续变化”。
|
|
</p>
|
|
</div>
|
|
|
|
{/* Search bar */}
|
|
<div className="mb-4 relative">
|
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
<Input
|
|
type="search"
|
|
placeholder={t('searchPlaceholder')}
|
|
className="pl-10"
|
|
value={searchQuery}
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
/>
|
|
</div>
|
|
|
|
<CategoryFilters
|
|
questionnaires={questionnaires as Questionnaire[]}
|
|
value={selectedCategory}
|
|
onChange={handleCategoryChange}
|
|
/>
|
|
|
|
{/* Questionnaire list */}
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
{filteredQuestionnaires.length > 0 ? (
|
|
filteredQuestionnaires.map((questionnaire) => (
|
|
<Card key={questionnaire.id}>
|
|
<CardHeader>
|
|
<CardTitle>{questionnaire.title}</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="mb-4 h-12">
|
|
<p
|
|
className="text-sm text-muted-foreground"
|
|
style={{
|
|
display: '-webkit-box',
|
|
WebkitLineClamp: '2',
|
|
WebkitBoxOrient: 'vertical',
|
|
overflow: 'hidden',
|
|
}}
|
|
>
|
|
{questionnaire.description}
|
|
</p>
|
|
</div>
|
|
</CardContent>
|
|
<CardFooter className="flex justify-between items-center">
|
|
<span className="text-xs text-muted-foreground">
|
|
{questionnaire.time}
|
|
</span>
|
|
<Link href={`/questionnaire/${questionnaire.id}/details`}>
|
|
<Button className="cursor-pointer">
|
|
{t('detailButton')}
|
|
</Button>
|
|
</Link>
|
|
</CardFooter>
|
|
</Card>
|
|
))
|
|
) : (
|
|
<div className="col-span-3 text-center py-8 text-muted-foreground">
|
|
{t('noMatch')}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|