| import React, { useState } from "react"; | |
| import { PolicyData } from "../types"; | |
| interface AIPoliciesTableProps { | |
| policies: PolicyData[]; | |
| } | |
| const AIPoliciesTable: React.FC<AIPoliciesTableProps> = ({ policies }) => { | |
| const initialOpenYears = policies.reduce((acc, policy) => { | |
| const year = new Date(policy.releaseDate).getFullYear(); | |
| acc[year] = true; | |
| return acc; | |
| }, {} as { [key: number]: boolean }); | |
| const [openYears, setOpenYears] = useState<{ [key: number]: boolean }>(initialOpenYears); | |
| const toggleYear = (year: number) => { | |
| setOpenYears((prev) => ({ ...prev, [year]: !prev[year] })); | |
| }; | |
| const groupedPolicies = policies.reduce((acc, policy) => { | |
| const year = new Date(policy.releaseDate).getFullYear(); | |
| if (!acc[year]) { | |
| acc[year] = []; | |
| } | |
| acc[year].push(policy); | |
| return acc; | |
| }, {} as { [key: number]: PolicyData[] }); | |
| const sortedYears = Object.keys(groupedPolicies) | |
| .map(Number) | |
| .sort((a, b) => b - a); | |
| const formatDate = (dateString: string) => { | |
| const date = new Date(dateString); | |
| const options: Intl.DateTimeFormatOptions = { month: 'short', day: 'numeric', year: 'numeric' }; | |
| return date.toLocaleDateString('en-US', options); | |
| }; | |
| return ( | |
| <div className="container mx-auto p-6"> | |
| {sortedYears.map((year) => ( | |
| <div key={year} className="mb-12"> | |
| <button | |
| onClick={() => toggleYear(year)} | |
| className="text-2xl font-semibold mb-6 focus:outline-none text-gray-800 dark:text-white" | |
| > | |
| {year} {openYears[year] ? "▲" : "▼"} | |
| </button> | |
| {openYears[year] && ( | |
| <table className="w-full border-collapse table-auto shadow-lg rounded-lg"> | |
| <tbody> | |
| {groupedPolicies[year].map((policy, index) => ( | |
| <tr | |
| key={`${year}-${index}`} | |
| className={`${ | |
| index % 2 === 0 ? "bg-gray-300" : "bg-gray-500" | |
| } dark:${ | |
| index % 2 === 0 ? "bg-gray-700" : "bg-gray-900" | |
| } border-b border-gray-200 dark:border-gray-700`} | |
| > | |
| <td className="py-6 px-6 text-gray-900 dark:text-white"> | |
| <div className="text-lg font-medium">{policy.zh}</div> | |
| <div className="text-sm text-gray-500 dark:text-gray-400 mt-2"> | |
| {policy.en} | |
| </div> | |
| <div className="text-xs text-gray-400 dark:text-gray-500 mt-2"> | |
| {formatDate(policy.releaseDate)} | |
| </div> | |
| </td> | |
| <td className="py-6 px-6 text-right"> | |
| {policy.link.zh && ( | |
| <a | |
| href={policy.link.zh} | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| className="text-blue-500 hover:underline dark:text-blue-400 mr-4" | |
| > | |
| 中文 | |
| </a> | |
| )} | |
| {policy.link.en && ( | |
| <a | |
| href={policy.link.en} | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| className="text-blue-500 hover:underline dark:text-blue-400" | |
| > | |
| English | |
| </a> | |
| )} | |
| </td> | |
| </tr> | |
| ))} | |
| </tbody> | |
| </table> | |
| )} | |
| </div> | |
| ))} | |
| </div> | |
| ); | |
| }; | |
| export default AIPoliciesTable; | |