"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 (
{/* Responsive grid: 1 column on mobile, 2 on sm screens, configurable columns on lg */}
{bots.map((chatbot) => (
))}
);
}
/**
* 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(
() => getPopularBots(),
);
const [filteredTrendingBots, setFilteredTrendingBots] = useState(
() => getTrendingBots(),
);
const [filteredAllBots, setFilteredAllBots] = useState(() =>
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 (
);
}
// 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 (
);
}