File size: 5,588 Bytes
d4a7cd9
a2242ea
31767a7
 
 
 
 
 
d4a7cd9
 
 
 
 
 
 
 
 
 
 
 
b92b41e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4646723
 
 
cbc828e
4646723
 
 
cbc828e
4646723
 
 
cbc828e
4646723
 
 
 
 
cbc828e
 
d4a7cd9
b92b41e
 
 
 
 
 
 
 
 
 
 
 
 
4646723
d4a7cd9
 
 
 
 
 
4646723
d4a7cd9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b92b41e
 
 
d4a7cd9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9816072
d4a7cd9
 
 
 
9816072
 
 
 
 
2bcc20e
9816072
 
 
 
d4a7cd9
 
 
 
 
 
4646723
 
31767a7
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
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 uniqueTags = Array.from(new Set(policies.flatMap(policy => policy.tags.map(tag => tag.toLowerCase())))).map(tag => tag.charAt(0).toUpperCase() + tag.slice(1));

  const [selectedTags, setSelectedTags] = useState<string[]>(uniqueTags);

  const handleTagChange = (tag: string) => {
    if (selectedTags.includes(tag)) {
      setSelectedTags(selectedTags.filter(t => t !== tag));
    } else {
      setSelectedTags([...selectedTags, tag]);
    }
  };

  const filteredPolicies = selectedTags.length === 0 
    ? policies 
    : policies.filter(policy => policy.tags.some(tag => selectedTags.includes(tag.charAt(0).toUpperCase() + tag.slice(1))));

  const groupedPolicies = filteredPolicies.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">
      <div className="mb-6 flex justify-center">
        {uniqueTags.map(tag => (
          <label key={tag} className="inline-flex items-center mr-4">
            <input
              type="checkbox"
              className="form-checkbox h-5 w-5 text-blue-600"
              checked={selectedTags.includes(tag)}
              onChange={() => handleTagChange(tag)}
            />
            <span className="ml-2 text-gray-700 dark:text-gray-400">{tag}</span>
          </label>
        ))}
      </div>
      {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>
                      <div className="text-xs text-gray-400 dark:text-gray-500 mt-2">
                        {policy.tags.map(tag => tag.charAt(0).toUpperCase() + tag.slice(1)).join(', ')}
                      </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 mr-4"
                        >
                          English
                        </a>
                      )}
                      {policy.link.fr && (
                        <a
                          href={policy.link.fr}
                          target="_blank"
                          rel="noopener noreferrer"
                          className="text-blue-500 hover:underline dark:text-blue-400 mr-4"
                        >
                          Français
                        </a>
                      )}
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </div>
      ))}
    </div>
  );
};

export default AIPoliciesTable;