diff --git a/notion_cache.json b/notion_cache.json index 95636a4..72b5553 100644 --- a/notion_cache.json +++ b/notion_cache.json @@ -1 +1 @@ -{"1aabb9ea-bd14-8003-94cb-e05052c8e884": "2026-01-16T07:24:00.000Z", "1aabb9ea-bd14-801b-b84e-e5e6759bab17": "2026-01-19T15:32:00.000Z", "1aabb9ea-bd14-8034-ac22-eb67957b287c": "2025-12-30T15:43:00.000Z", "1aabb9ea-bd14-804b-980b-d17b3028bda5": "2025-12-27T14:02:00.000Z", "1aabb9ea-bd14-805e-bffa-cad47843be2b": "2025-12-30T16:56:00.000Z", "1aabb9ea-bd14-8064-9817-f1b7ee3cd7d8": "2025-12-30T13:56:00.000Z", "1aabb9ea-bd14-807b-8eec-e2f5da97fc9d": "2026-01-21T16:28:00.000Z", "1aabb9ea-bd14-80b4-9d41-ed343f0f84fb": "2025-12-15T04:50:00.000Z", "1aabb9ea-bd14-80c4-b066-c6241af213d8": "2025-12-30T13:57:00.000Z", "1aabb9ea-bd14-80d4-bcae-c47505f078f7": "2025-12-30T15:03:00.000Z", "1aabb9ea-bd14-80fc-b09d-f119168392b0": "2025-12-26T13:14:00.000Z", "1aabb9ea-bd14-80fe-89eb-f5b5495943ef": "2025-12-28T07:30:00.000Z", "2c9bb9ea-bd14-8111-a197-fd2b924a628c": "2025-12-27T09:20:00.000Z", "2c9bb9ea-bd14-8157-a1b2-e2ba27313a99": "2025-12-29T16:31:00.000Z", "2c9bb9ea-bd14-815b-adb0-c3c3e1227924": "2025-12-29T03:38:00.000Z", "2cabb9ea-bd14-8127-a5b5-d0c4be038074": "2025-12-27T09:20:00.000Z", "2cabb9ea-bd14-8155-867c-c90aef7e6ddf": "2025-12-30T01:35:00.000Z"} \ No newline at end of file +{"2cabb9ea-bd14-8155-867c-c90aef7e6ddf": "2026-08-03T15:33:00.000Z", "2cabb9ea-bd14-8127-a5b5-d0c4be038074": "2026-08-03T15:33:00.000Z", "2c9bb9ea-bd14-8111-a197-fd2b924a628c": "2026-08-03T15:33:00.000Z", "2c9bb9ea-bd14-8157-a1b2-e2ba27313a99": "2026-08-03T15:33:00.000Z", "2c9bb9ea-bd14-815b-adb0-c3c3e1227924": "2026-08-03T15:33:00.000Z", "1aabb9ea-bd14-8003-94cb-e05052c8e884": "2026-08-03T15:33:00.000Z", "1aabb9ea-bd14-804b-980b-d17b3028bda5": "2026-08-03T15:33:00.000Z", "1aabb9ea-bd14-8064-9817-f1b7ee3cd7d8": "2026-08-03T15:33:00.000Z", "1aabb9ea-bd14-807b-8eec-e2f5da97fc9d": "2026-08-03T15:33:00.000Z", "1aabb9ea-bd14-80c4-b066-c6241af213d8": "2026-08-03T15:32:00.000Z", "1aabb9ea-bd14-80d4-bcae-c47505f078f7": "2026-08-03T15:33:00.000Z", "1aabb9ea-bd14-801b-b84e-e5e6759bab17": "2026-08-03T15:32:00.000Z", "1aabb9ea-bd14-8034-ac22-eb67957b287c": "2026-07-06T05:52:00.000Z", "1aabb9ea-bd14-805e-bffa-cad47843be2b": "2026-08-03T15:32:00.000Z", "1aabb9ea-bd14-80b4-9d41-ed343f0f84fb": "2026-08-03T15:32:00.000Z", "1aabb9ea-bd14-80fc-b09d-f119168392b0": "2026-08-04T10:04:00.000Z", "1aabb9ea-bd14-80fe-89eb-f5b5495943ef": "2026-08-03T15:32:00.000Z"} \ No newline at end of file diff --git a/public/member_images/doan_quoc_an.jpg b/public/member_images/doan_quoc_an.jpg index 87b4a2c..95e591f 100644 Binary files a/public/member_images/doan_quoc_an.jpg and b/public/member_images/doan_quoc_an.jpg differ diff --git a/scripts/fetch-members.py b/scripts/fetch-members.py index e6d618d..357127c 100644 --- a/scripts/fetch-members.py +++ b/scripts/fetch-members.py @@ -3,9 +3,9 @@ from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry -# --- Set up --- NOTION_TOKEN = os.environ.get("NOTION_TOKEN") DATABASE_ID = os.environ.get("DATABASE_ID") + IMAGE_FOLDER = "public/member_images" HEADERS = { "Authorization": f"Bearer {NOTION_TOKEN}", @@ -114,7 +114,8 @@ def process_notion_data(): final_data.append({ "name": name, - "role": props.get("Role", {}).get("select", {}).get("name", "N/A"), + "role": (props.get("Role", {}).get("select") or {}).get("name", "N/A"), + "position": (props.get("Position", {}).get("select") or {}).get("name", ""), "image": img_path }) diff --git a/src/App.js b/src/App.js index bac4c83..9ccd8b1 100644 --- a/src/App.js +++ b/src/App.js @@ -19,6 +19,14 @@ const groupedData = (() => { return acc; }, {}); + Object.keys(groups).forEach((role) => { + groups[role].sort((a, b) => { + // Weight: Lead ranks 1st, Member ranks 2nd, and Senior Advisor (or blank/empty) ranks 3rd + const getWeight = (pos) => pos === "Lead" ? 1 : (pos === "Member" ? 2 : 3); + return getWeight(a.position) - getWeight(b.position); + }); + }); + const sortedGroups = {}; ROLE_ORDER.forEach((role) => { if (groups[role]) { @@ -145,7 +153,6 @@ function App() { // Resource hints for performance useEffect(() => { - // Add preconnect for external domains performanceConfig.resourceHints.forEach((hint) => { const link = document.createElement("link"); link.rel = hint.rel; @@ -155,7 +162,6 @@ function App() { }); return () => { - // Cleanup on unmount performanceConfig.resourceHints.forEach((hint) => { const links = document.querySelectorAll(`link[href="${hint.href}"]`); links.forEach((link) => link.remove()); @@ -169,9 +175,7 @@ function App() { { label: "Thông báo", href: "https://svuit.org/mmtt/docs/ThongBao/index" }, ]; - // Register service worker and set up performance monitoring useEffect(() => { - // Register service worker if enabled if (performanceConfig.features.serviceWorker) { const { register } = require("./utils/serviceWorker"); register({ @@ -189,12 +193,10 @@ function App() { }); } - // Set initial visibility with a small delay const timer = setTimeout(() => { setIsVisible(true); }, 100); - // Cleanup function return () => { clearTimeout(timer); }; @@ -273,7 +275,7 @@ function App() { overflow: "hidden", opacity: isVisible ? 1 : 0, transition: "opacity 1s ease-in-out", - zIndex: 2, // Adjusted z-index + zIndex: 2, marginBottom: "-1rem", }} > @@ -708,11 +710,11 @@ function App() { style={{ display: "flex", flexDirection: "row", - flexWrap: "wrap", // ← fix slider + flexWrap: "wrap", justifyContent: "center", alignItems: "center", gap: "2rem", - overflow: "hidden", // ← remove horizontal scroll + overflow: "hidden", paddingBottom: "1rem", width: "100%", }} @@ -726,13 +728,19 @@ function App() { alignItems: "center", }} > +
@@ -748,16 +756,38 @@ function App() { />
-

- {member.name} -

+
+

+ {member.name} +

+ + {/* Position added */} + {member.position && ( + + {member.position} + + )} +
))} @@ -1057,4 +1087,4 @@ function App() { ); } -export default App; +export default App; \ No newline at end of file diff --git a/src/data/notion_member.json b/src/data/notion_member.json index befd66b..be86a6a 100644 --- a/src/data/notion_member.json +++ b/src/data/notion_member.json @@ -1,87 +1,104 @@ [ { - "name": "Nguyễn Thế Lập", - "role": "Website Development", - "image": "/member_images/nguyen_the_lap.jpg" + "name": "Trần Phạm Bảo Trân", + "role": "Platform Operations", + "position": "Member", + "image": "/member_images/tran_pham_bao_tran.jpg" }, { - "name": "Nguyễn Hoàng Lộc", + "name": "Ngô Lê Tấn Huy", "role": "Platform Operations", - "image": "/member_images/nguyen_hoang_loc.jpg" + "position": "Member", + "image": "/member_images/ngo_le_tan_huy.jpg" }, { - "name": "Trần Công Hải", - "role": "Senior Advisor", - "image": "/member_images/tran_cong_hai.jpg" + "name": "Lý Nhật Minh", + "role": "Social Media", + "position": "Member", + "image": "/member_images/ly_nhat_minh.jpg" + }, + { + "name": "Đoàn Nguyễn Minh Thư", + "role": "Website Development", + "position": "Member", + "image": "/member_images/doan_nguyen_minh_thu.jpg" + }, + { + "name": "Trịnh Hoài An", + "role": "Website Development", + "position": "Member", + "image": "/member_images/trinh_hoai_an.jpg" + }, + { + "name": "Nguyễn Thế Lập", + "role": "Website Development", + "position": "Member", + "image": "/member_images/nguyen_the_lap.jpg" }, { "name": "Nguyễn Thành An", "role": "Social Media", + "position": "Member", "image": "/member_images/nguyen_thanh_an.jpg" }, - { - "name": "Tống Võ Anh Thuận", - "role": "Operations Lead", - "image": "/member_images/tong_vo_anh_thuan.jpg" - }, { "name": "Sơn Nguyễn Kỳ Duyên", "role": "Social Media", + "position": "Member", "image": "/member_images/son_nguyen_ky_duyen.jpg" }, { "name": "Đoàn Quốc An", "role": "Social Media", + "position": "Lead", "image": "/member_images/doan_quoc_an.jpg" }, - { - "name": "Lê Huỳnh Quang Vũ", - "role": "Senior Advisor", - "image": "/default.png" - }, { "name": "Phạm Việt Hoàng", "role": "Platform Operations", + "position": "Member", "image": "/member_images/pham_viet_hoang.jpg" }, { "name": "Trương Đỗ Như Quỳnh", "role": "Website Development", + "position": "Lead", "image": "/member_images/truong_do_nhu_quynh.jpg" }, { - "name": "Đoàn Nguyễn Lâm", - "role": "Platform Operations", - "image": "/member_images/doan_nguyen_lam.jpg" - }, - { - "name": "Đặng Chí Thành", + "name": "Nguyễn Hoàng Lộc", "role": "Platform Operations", - "image": "/member_images/dang_chi_thanh.jpg" + "position": "Lead", + "image": "/member_images/nguyen_hoang_loc.jpg" }, { - "name": "Lý Nhật Minh", - "role": "Social Media", - "image": "/member_images/ly_nhat_minh.jpg" + "name": "Trần Công Hải", + "role": "Senior Advisor", + "position": "", + "image": "/member_images/tran_cong_hai.jpg" }, { - "name": "Đoàn Nguyễn Minh Thư", - "role": "Website Development", - "image": "/member_images/doan_nguyen_minh_thu.jpg" + "name": "Tống Võ Anh Thuận", + "role": "Senior Advisor", + "position": "", + "image": "/member_images/tong_vo_anh_thuan.jpg" }, { - "name": "Trịnh Hoài An", - "role": "Website Development", - "image": "/member_images/trinh_hoai_an.jpg" + "name": "Lê Huỳnh Quang Vũ", + "role": "Senior Advisor", + "position": "", + "image": "/default.png" }, { - "name": "Ngô Lê Tấn Huy", - "role": "Platform Operations", - "image": "/member_images/ngo_le_tan_huy.jpg" + "name": "Đoàn Nguyễn Lâm", + "role": "Senior Advisor", + "position": "", + "image": "/member_images/doan_nguyen_lam.jpg" }, { - "name": "Trần Phạm Bảo Trân", + "name": "Đặng Chí Thành", "role": "Platform Operations", - "image": "/member_images/tran_pham_bao_tran.jpg" + "position": "Member", + "image": "/member_images/dang_chi_thanh.jpg" } ] \ No newline at end of file