@@ -21,25 +21,28 @@ export default function MemorialIndexPage() { |
| 21 | 21 | const [loading, setLoading] = useState(true); |
| 22 | 22 | const [error, setError] = useState<string | null>(null); |
| 23 | 23 | const [expandedConflicts, setExpandedConflicts] = useState<Set<number>>(new Set()); |
| 24 | + const [sortBy, setSortBy] = useState<'alphabetical' | 'class_year'>('alphabetical'); |
| 24 | 25 | |
| 25 | 26 | useEffect(() => { |
| 26 | 27 | async function fetchData() { |
| 27 | 28 | try { |
| 28 | 29 | const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000/api'; |
| 29 | | - const response = await fetch(`${apiUrl}/memorial/index/`); |
| 30 | | - |
| 30 | + const response = await fetch(`${apiUrl}/memorial/index/?sort=${sortBy}`); |
| 31 | + |
| 31 | 32 | if (!response.ok) { |
| 32 | 33 | throw new Error('Failed to fetch memorial index'); |
| 33 | 34 | } |
| 34 | | - |
| 35 | + |
| 35 | 36 | const data = await response.json(); |
| 36 | 37 | setConflicts(data); |
| 37 | | - |
| 38 | | - // By default, expand conflicts with casualties |
| 39 | | - const defaultExpanded = new Set<number>( |
| 40 | | - data.filter((c: ConflictWithCasualties) => c.casualty_count > 0).map((c: ConflictWithCasualties) => c.id) |
| 41 | | - ); |
| 42 | | - setExpandedConflicts(defaultExpanded); |
| 38 | + |
| 39 | + // By default, expand conflicts with casualties (only on first load) |
| 40 | + if (expandedConflicts.size === 0) { |
| 41 | + const defaultExpanded = new Set<number>( |
| 42 | + data.filter((c: ConflictWithCasualties) => c.casualty_count > 0).map((c: ConflictWithCasualties) => c.id) |
| 43 | + ); |
| 44 | + setExpandedConflicts(defaultExpanded); |
| 45 | + } |
| 43 | 46 | } catch (err) { |
| 44 | 47 | setError(err instanceof Error ? err.message : 'Failed to load data'); |
| 45 | 48 | console.error(err); |
@@ -47,9 +50,9 @@ export default function MemorialIndexPage() { |
| 47 | 50 | setLoading(false); |
| 48 | 51 | } |
| 49 | 52 | } |
| 50 | | - |
| 53 | + |
| 51 | 54 | fetchData(); |
| 52 | | - }, []); |
| 55 | + }, [sortBy]); |
| 53 | 56 | |
| 54 | 57 | const toggleConflict = (conflictId: number) => { |
| 55 | 58 | const newExpanded = new Set(expandedConflicts); |
@@ -123,13 +126,41 @@ export default function MemorialIndexPage() { |
| 123 | 126 | <span className="text-vmi-red font-bold">{totalCasualties}</span> |
| 124 | 127 | </div> |
| 125 | 128 | </div> |
| 126 | | - <div className="mt-6"> |
| 129 | + <div className="mt-6 flex justify-between items-center"> |
| 127 | 130 | <button |
| 128 | 131 | onClick={toggleAll} |
| 129 | 132 | className="bg-vmi-red text-white px-6 py-2 rounded hover:bg-vmi-dark-red transition-colors font-semibold" |
| 130 | 133 | > |
| 131 | 134 | {expandedConflicts.size === conflicts.length ? 'Collapse All' : 'Expand All'} |
| 132 | 135 | </button> |
| 136 | + |
| 137 | + {/* Sort Toggle */} |
| 138 | + <div className="flex items-center gap-2"> |
| 139 | + <span className="text-sm text-gray-600">Sort by:</span> |
| 140 | + <button |
| 141 | + onClick={() => setSortBy(sortBy === 'alphabetical' ? 'class_year' : 'alphabetical')} |
| 142 | + className="flex items-center bg-white border-2 border-gray-300 rounded-full p-1 shadow-sm hover:shadow-md transition-shadow" |
| 143 | + > |
| 144 | + <span |
| 145 | + className={`px-3 py-1 rounded-full transition-all ${ |
| 146 | + sortBy === 'alphabetical' |
| 147 | + ? 'bg-vmi-red text-white font-semibold' |
| 148 | + : 'text-gray-500' |
| 149 | + }`} |
| 150 | + > |
| 151 | + ABC |
| 152 | + </span> |
| 153 | + <span |
| 154 | + className={`px-3 py-1 rounded-full transition-all ${ |
| 155 | + sortBy === 'class_year' |
| 156 | + ? 'bg-vmi-red text-white font-semibold' |
| 157 | + : 'text-gray-500' |
| 158 | + }`} |
| 159 | + > |
| 160 | + '42 |
| 161 | + </span> |
| 162 | + </button> |
| 163 | + </div> |
| 133 | 164 | </div> |
| 134 | 165 | </div> |
| 135 | 166 | |
@@ -179,6 +210,9 @@ export default function MemorialIndexPage() { |
| 179 | 210 | {person.rank |
| 180 | 211 | ? person.display_name.replace(person.rank + ' ', '').replace(person.rank + ', ', '') |
| 181 | 212 | : person.display_name} |
| 213 | + {person.class_year && ( |
| 214 | + <span className="text-gray-600 font-normal">'{String(person.class_year).slice(-2)}</span> |
| 215 | + )} |
| 182 | 216 | {person.pdf_key && <DocumentIcon className="flex-shrink-0" />} |
| 183 | 217 | </h3> |
| 184 | 218 | {person.rank && ( |