feat: 完善中文心理测评平台
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
'use client';
|
||||
import { 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>
|
||||
|
||||
{/* 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user