Spaces:
Sleeping
Sleeping
"use client"; | |
// Core React imports | |
import { useState, useEffect } from "react"; | |
// Local component imports | |
import ChatBotCard from "./chat-bot-card"; | |
// Type definitions and data | |
import { ChatBot } from "@/app/(audiences)/for-students/data/chatbots"; | |
/** | |
* Props interface for the Section component that displays a group of chatbots | |
* in a responsive grid layout with a title and subtitle | |
*/ | |
interface SectionProps { | |
title: string; | |
subtitle: string; | |
bots: ChatBot[]; | |
columns?: number; | |
} | |
/** | |
* Renders a section of chatbots with a header and responsive grid layout | |
* Returns null if no bots are provided | |
*/ | |
function Section({ title, subtitle, bots, columns = 4 }: SectionProps) { | |
if (bots.length === 0) return null; | |
return ( | |
<div className="mb-16"> | |
<div className="mb-8"> | |
<h2 className="text-2xl font-bold text-text-primary mb-2">{title}</h2> | |
<p className="text-text-secondary">{subtitle}</p> | |
</div> | |
{/* Responsive grid: 1 column on mobile, 2 on sm screens, configurable columns on lg */} | |
<div className={`grid gap-4 sm:grid-cols-2 lg:grid-cols-${columns}`}> | |
{bots.map((chatbot) => ( | |
<ChatBotCard key={chatbot.id} chatbot={chatbot} /> | |
))} | |
</div> | |
</div> | |
); | |
} | |
/** | |
* Props interface for the main ChatBotGrid component | |
* Includes filter criteria and functions to fetch different bot categories | |
*/ | |
interface ChatBotGridProps { | |
selectedCategories: string[]; | |
selectedSubjects: string[]; | |
searchQuery: string; | |
getPopularBots: () => ChatBot[]; | |
getTrendingBots: () => ChatBot[]; | |
getAllBots: () => ChatBot[]; | |
} | |
/** | |
* Main component that displays a filterable grid of chatbots | |
* Supports filtering by category, subject, and search query | |
* Shows different sections based on filter state | |
*/ | |
export default function ChatBotGrid({ | |
selectedCategories, | |
selectedSubjects, | |
searchQuery, | |
getPopularBots, | |
getTrendingBots, | |
getAllBots, | |
}: ChatBotGridProps) { | |
// Initialize state with lazy evaluation using callback functions | |
const [filteredPopularBots, setFilteredPopularBots] = useState<ChatBot[]>( | |
() => getPopularBots(), | |
); | |
const [filteredTrendingBots, setFilteredTrendingBots] = useState<ChatBot[]>( | |
() => getTrendingBots(), | |
); | |
const [filteredAllBots, setFilteredAllBots] = useState<ChatBot[]>(() => | |
getAllBots(), | |
); | |
/** | |
* Effect to filter bots whenever filter criteria or bot data changes | |
* Applies search query, category, and subject filters | |
*/ | |
useEffect(() => { | |
const filterBots = (bots: ChatBot[]) => { | |
let filtered = bots; | |
// Apply text search filter across multiple fields | |
if (searchQuery) { | |
const query = searchQuery.toLowerCase(); | |
filtered = filtered.filter( | |
(bot) => | |
bot.title.toLowerCase().includes(query) || | |
bot.description.toLowerCase().includes(query) || | |
bot.subject.toLowerCase().includes(query) || | |
bot.category.toLowerCase().includes(query), | |
); | |
} | |
// Apply category filter if categories are selected | |
if (selectedCategories.length > 0) { | |
filtered = filtered.filter( | |
(bot) => bot.category && selectedCategories.includes(bot.category), | |
); | |
} | |
// Apply subject filter if subjects are selected | |
if (selectedSubjects.length > 0) { | |
filtered = filtered.filter((bot) => | |
selectedSubjects.includes(bot.subject), | |
); | |
} | |
return filtered; | |
}; | |
// Update all filtered bot lists | |
setFilteredPopularBots(filterBots(getPopularBots())); | |
setFilteredTrendingBots(filterBots(getTrendingBots())); | |
setFilteredAllBots(filterBots(getAllBots())); | |
}, [ | |
selectedCategories, | |
selectedSubjects, | |
searchQuery, | |
getPopularBots, | |
getTrendingBots, | |
getAllBots, | |
]); | |
// Check if any filters are active | |
const hasFiltersOrSearch = | |
selectedCategories.length > 0 || selectedSubjects.length > 0 || searchQuery; | |
// When filters are active, show only search results | |
if (hasFiltersOrSearch) { | |
return ( | |
<Section | |
title="搜尋結果" | |
subtitle={`符合 ${searchQuery ? '"' + searchQuery + '" ' : ""}的工具`} | |
bots={filteredAllBots} | |
columns={4} | |
/> | |
); | |
} | |
// Filter bots by category and subject for different sections | |
const tutorBots = filteredAllBots.filter((bot) => bot.category === "教育"); | |
const teachingBots = filteredAllBots.filter((bot) => bot.subject === "教學"); | |
const assessmentBots = filteredAllBots.filter( | |
(bot) => bot.subject === "評量", | |
); | |
// Default view showing multiple sections of categorized bots | |
return ( | |
<div className="space-y-8"> | |
<Section | |
title="精選" | |
subtitle="歷來最多人使用的工具" | |
bots={filteredPopularBots} | |
columns={4} | |
/> | |
<Section | |
title="熱門" | |
subtitle="近期最多人使用的工具" | |
bots={filteredTrendingBots} | |
columns={4} | |
/> | |
<Section | |
title="教學輔助" | |
subtitle="教學規劃與課程設計工具" | |
bots={teachingBots} | |
columns={4} | |
/> | |
<Section | |
title="評量工具" | |
subtitle="作業與評量相關工具" | |
bots={assessmentBots} | |
columns={4} | |
/> | |
<Section | |
title="教育資源" | |
subtitle="其他教育相關工具" | |
bots={tutorBots} | |
columns={4} | |
/> | |
</div> | |
); | |
} | |