Skip to content

Commit d611e08

Browse files
committed
feat: add Photos page
- Added Photos page and linked it to routes and navigation - Removed News page - Implemented logic to fetch and parse Photos data from Google Sheets (photos.json) - Added PHOTOS_GOOGLE_SHEET_URL to .env
1 parent d21942a commit d611e08

File tree

8 files changed

+130
-47
lines changed

8 files changed

+130
-47
lines changed

.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ RESEARCH_SHEET=https://docs.google.com/spreadsheets/d/1CPDVN579fjmiVVpo1qVcCeDRU
44
PUBLICATIONS_SHEET=https://docs.google.com/spreadsheets/d/1CPDVN579fjmiVVpo1qVcCeDRUN1BEw_P8n_X12rXVBY/export?format=tsv&gid=698372256
55
VIDEOS_SHEET=https://docs.google.com/spreadsheets/d/1CPDVN579fjmiVVpo1qVcCeDRUN1BEw_P8n_X12rXVBY/export?format=tsv&gid=889428793
66
NEWS_SHEET=https://docs.google.com/spreadsheets/d/1CPDVN579fjmiVVpo1qVcCeDRUN1BEw_P8n_X12rXVBY/export?format=tsv&gid=1018366792
7+
PHOTOS_SHEET=https://docs.google.com/spreadsheets/d/1CPDVN579fjmiVVpo1qVcCeDRUN1BEw_P8n_X12rXVBY/export?format=tsv&gid=1578379541
78
GDRIVE_UA=aria-gdrive-downloader/1.0

scripts/fetch-sheet.js

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ function preHome(allRecords) {
6969

7070
allRecords.forEach(item => {
7171
if (item.Id === 'BannerImage') {
72-
const filename = path.join("/images/banner", item.Filename)
72+
const filename = path.join("/images/photos", item.Filename)
7373
BannerImages.push(filename);
7474
imageArchive.push({ link: String(item.Url).trim(), out: filename });
7575
} else {
@@ -170,6 +170,18 @@ function preVideos(allRecords) {
170170
return allRecords;
171171
}
172172

173+
function prePhotos(allRecords) {
174+
allRecords.forEach(item => {
175+
if (!item.Image) return;
176+
// Set Image path
177+
item.Image = path.join("/images/photos", item.Filename);
178+
// Collect image archive entry
179+
imageArchive.push({ link: String(item.Url).trim(), out: item.Image });
180+
});
181+
182+
return allRecords;
183+
}
184+
173185
function processHome() {
174186
const tsvPath = path.resolve(__dirname, '../home.tsv');
175187
const outPath = path.resolve(__dirname, '../src/data/home.json');
@@ -218,7 +230,6 @@ function processResearch() {
218230
console.log(`Deleted TSV file: ${tsvPath}`);
219231
}
220232

221-
222233
function processVideos() {
223234
const tsvPath = path.resolve(__dirname, '../videos.tsv');
224235
const outPath = path.resolve(__dirname, '../src/data/videos.json');
@@ -266,19 +277,41 @@ function processNews() {
266277
console.log(`Deleted TSV file: ${tsvPath}`);
267278
}
268279

280+
function processPhotos() {
281+
const tsvPath = path.resolve(__dirname, '../photos.tsv');
282+
const outPath = path.resolve(__dirname, '../src/data/photos.json');
283+
const url = process.env.PHOTOS_SHEET;
284+
if (!url) {
285+
console.error('Environment variable PHOTOS_SHEET not set');
286+
process.exit(1);
287+
}
288+
execSync(`curl -L "${url}" -o "${tsvPath}"`, { stdio: 'inherit' });
289+
const allRecords = parseTsv(tsvPath);
290+
const data = prePhotos(allRecords);
291+
writeJson(outPath, data);
292+
fs.unlinkSync(tsvPath);
293+
console.log(`Deleted TSV file: ${tsvPath}`);
294+
}
295+
269296
function main() {
270297
processHome();
271298
processPeople();
272299
processResearch();
273300
processVideos();
274301
processPublications();
275302
processNews();
303+
processPhotos();
276304

277-
278-
// Write aggregated image archive (if any)
279-
if (imageArchive.length > 0) {
280-
const archivePath = path.resolve(__dirname, 'image-archive.json');
281-
writeJson(archivePath, imageArchive);
305+
const archivePath = path.resolve(__dirname, 'image-archive.json');
306+
// Remove duplicate entries from imageArchive based on 'link' and 'out'
307+
const uniqueArchive = Array.from(
308+
new Map(imageArchive.map(item => [`${item.link}|${item.out}`, item])).values()
309+
);
310+
311+
312+
// Write aggregated image archive
313+
if (uniqueArchive.length > 0) {
314+
writeJson(archivePath, uniqueArchive);
282315
} else {
283316
console.log('No image archive entries found.');
284317
}

src/data/index.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import research from './research.json';
44
import publications from './publications.json';
55
import videos from './videos.json';
66
import news from './news.json';
7+
import photos from './photos.json';
78

89
export {
910
home,
1011
people,
1112
research,
1213
publications,
1314
videos,
14-
news
15+
news,
16+
photos
1517
};
1618

src/pages/Home.jsx

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { Link } from 'react-router-dom';
21
import { home, news } from '@/data';
32
import { Carousel } from '@/components';
43

@@ -11,7 +10,7 @@ const Home = () => {
1110
{/* Banner */}
1211
<section>
1312
<h1 className="text-center" >{home.Title}</h1>
14-
<Carousel imgList={home.BannerImages}/>
13+
<Carousel imgList={home.BannerImages} />
1514
</section>
1615

1716
{/* About Us */}
@@ -22,15 +21,14 @@ const Home = () => {
2221

2322
{/* News */}
2423
<section>
25-
<Link to="/news" ><h2>Recent News</h2>
26-
<ul className="space-y-sm ml-8 list-disc">
27-
{topNews.map((item, index) => (
28-
<li key={`news-${index}`}>
29-
{item.Content}
30-
</li>
31-
))}
32-
</ul>
33-
</Link>
24+
<h2>Recent News</h2>
25+
<ul className="space-y-sm ml-8 list-disc">
26+
{topNews.map((item, index) => (
27+
<li key={`news-${index}`}>
28+
{item.Content}
29+
</li>
30+
))}
31+
</ul>
3432
</section>
3533
</div>
3634
);

src/pages/News.jsx

Lines changed: 0 additions & 23 deletions
This file was deleted.

src/pages/Photos.jsx

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { useState, useEffect } from 'react';
2+
import { Title } from '@/components';
3+
import { photos } from '@/data';
4+
5+
const data = photos.reverse();
6+
7+
const Image = ({ id, path }) => {
8+
const placeholder = '/images/placeholder.png';
9+
const src = path || placeholder;
10+
11+
return (
12+
<img
13+
src={src}
14+
alt={`photo-image-${id}`}
15+
className="aspect-video object-cover rounded-xl"
16+
onError={e => {
17+
e.currentTarget.onError = null;
18+
e.currentTarget.src = "/images/placeholder.png";
19+
}}
20+
/>
21+
);
22+
}
23+
24+
const formatDate = (dateStr) => {
25+
if (!dateStr) return '';
26+
const date = new Date(dateStr);
27+
if (isNaN(date)) return dateStr;
28+
const hasDay = dateStr.match(/\d{4}-\d{2}-\d{2}/);
29+
const options = hasDay
30+
? { year: 'numeric', month: 'long', day: 'numeric' }
31+
: { year: 'numeric', month: 'long' };
32+
return date.toLocaleDateString('en-US', options);
33+
};
34+
35+
const Photos = () => {
36+
const [selectedImage, setSelectedImage] = useState(null);
37+
38+
useEffect(() => {
39+
const handleKeyDown = (e) => {
40+
if (e.key === 'Escape') {
41+
setSelectedImage(null);
42+
}
43+
};
44+
window.addEventListener('keydown', handleKeyDown);
45+
return () => window.removeEventListener('keydown', handleKeyDown);
46+
}, [selectedImage]);
47+
48+
return (
49+
<div className="pages">
50+
<Title name='Photos' />
51+
{/* Photos */}
52+
<ul className="grid grid-cols-1 md:grid-cols-2 gap-md">
53+
{data.map((item, index) => (
54+
<li key={`photos-${index}`} className="text-sm flex flex-col space-y-sm">
55+
<div onClick={() => setSelectedImage(item.Image)} className="cursor-pointer">
56+
<Image id={item.Id} path={item.Image} />
57+
</div>
58+
{item.Description && <p>{item.Description}</p>}
59+
{item.Date && <p className="italic">{formatDate(item.Date)}</p>}
60+
</li>
61+
))}
62+
</ul>
63+
{selectedImage && (
64+
<div className="fixed inset-0 bg-black/80 flex items-center justify-center z-50" onClick={() => setSelectedImage(null)}>
65+
<img src={selectedImage} alt="selected" className="max-h-6/10 max-w-6/10 rounded-xl" />
66+
</div>
67+
)}
68+
</div>
69+
);
70+
};
71+
72+
export default Photos;

src/routes/navigation.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ export const navigation = [
2626
to: "/videos",
2727
},
2828
{
29-
title: "News",
30-
key: "news",
31-
to: "/news",
29+
title: "Photos",
30+
key: "photos",
31+
to: "/photos",
3232
}
3333
]

src/routes/routes.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const People = React.lazy(() => import('@/pages/People'));
55
const Research = React.lazy(() => import('@/pages/Research'));
66
const Publications = React.lazy(() => import('@/pages/Publications'));
77
const Videos = React.lazy(() => import('@/pages/Videos'));
8-
const News = React.lazy(() => import('@/pages/News'));
8+
const Photos = React.lazy(() => import('@/pages/Photos'));
99

1010

1111
export const routes = [
@@ -14,5 +14,5 @@ export const routes = [
1414
{ path: '/research', element: Research, title: "Research" },
1515
{ path: '/publications', element: Publications, title: "Publications" },
1616
{ path: '/videos', element: Videos, title: "Videos" },
17-
{ path: '/news', element: News,title: "News" },
17+
{ path: '/photos', element: Photos, title: "Photos" },
1818
];

0 commit comments

Comments
 (0)