From 28fc7a075cb5da7aa066a2aad5559c5426248dfc Mon Sep 17 00:00:00 2001 From: Pinapelz Date: Mon, 14 Apr 2025 00:25:32 -0700 Subject: initial frontend site --- site/src/components/NewsFeed.tsx | 136 +++++++++++++++++++++++++++++++++++++++ site/src/components/TitleBar.tsx | 119 ++++++++++++++++++++++++++++++++++ 2 files changed, 255 insertions(+) create mode 100644 site/src/components/NewsFeed.tsx create mode 100644 site/src/components/TitleBar.tsx (limited to 'site/src/components') diff --git a/site/src/components/NewsFeed.tsx b/site/src/components/NewsFeed.tsx new file mode 100644 index 0000000..f030200 --- /dev/null +++ b/site/src/components/NewsFeed.tsx @@ -0,0 +1,136 @@ + +export interface NewsData { + date: string; + identifier: string; + type: string | null; + timestamp: number; + headline: string | null; + content: string; + url: string | null; + images: Array<{ + image: string; + link: string | null; + }>; +} + +interface NewsFeedProps { + newsItems: NewsData[]; +} + +export const NewsFeed: React.FC = ({ newsItems }) => { + return ( +
+ {newsItems.map((news) => { + const formattedDate = new Date(news.timestamp * 1000).toLocaleDateString("ja-JP", { + year: "numeric", + month: "2-digit", + day: "2-digit", + }); + + const gameId = news.identifier; + + return ( +
+ {/* Header (Game Icon + Info) */} +
+
+ {/* Game Icon */} +
+ {gameId.substring(0, 1)} +
+ +
+ + {getGameName(news.identifier)} + + + {formattedDate} + + {/* Display News Type */} + {news.type && ( + + {news.type} + + )} +
+
+
+ + {/* Content Area */} +
+ {/* Headline */} + {news.headline && ( +

+ {news.headline} +

+ )} + + {/* Content */} +

+ {news.content} +

+
+ + {/* Post Image(s) */} +
+ {news.images.map((img, i) => ( + img.link ? ( + + news visual + + ) : ( +
+ news visual +
+ ) + ))} +
+ + {/* Footer with Read More Link */} + {news.url && ( +
+ + READ MORE + +
+ )} +
+ ); + })} +
+ ); +}; +function getGameName(identifier: string): string | null { + if(identifier.startsWith("SOUND_VOLTEX")){ + return "SOUND VOLTEX"; + } + else if(identifier.startsWith("IIDX")){ + return "beatmania IIDX"; + } + else if(identifier.startsWith("CHUNITHM_JP")){ + return "CHUNITHM (JAPAN)"; + } + return null; +} diff --git a/site/src/components/TitleBar.tsx b/site/src/components/TitleBar.tsx new file mode 100644 index 0000000..bf79191 --- /dev/null +++ b/site/src/components/TitleBar.tsx @@ -0,0 +1,119 @@ +import { Link } from 'react-router-dom'; +import { useState, useEffect, useRef } from 'react'; + +interface GameCategory { + name: string; + games: { id: string; title: string }[]; +} + +const TitleBar: React.FC = () => { + const [dropdownOpen, setDropdownOpen] = useState(false); + const dropdownRef = useRef(null); + + const gameCategories: GameCategory[] = [ + { + name: "KONAMI", + games: [ + { id: "sdvx", title: "SOUND VOLTEX" }, + { id: "iidx", title: "beatmania IIDX" }, + ] + }, + { + name: "SEGA", + games: [ + { id: "chunithm_jp", title: "CHUNITHM JPN" }, + ] + } + ]; + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { + setDropdownOpen(false); + } + }; + + if (dropdownOpen) { + document.addEventListener('mousedown', handleClickOutside); + } + + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [dropdownOpen]); + + return ( +
+
+
+
+ 573 Updates Logo +
+ 573 +
+ + {/* Site Title */} + + UPDATES + +
+ + {/* Navigation Section */} +
+ + All Games + + + {/* Dropdown Menu */} +
+ + + {dropdownOpen && ( +
+
+ {gameCategories.map((category, index) => ( +
+
+ {category.name} +
+
+ {category.games.map((game) => ( + setDropdownOpen(false)} + > + {game.title} + + ))} +
+
+ ))} +
+
+ )} +
+
+
+
+
+ ); +}; + +export default TitleBar; \ No newline at end of file -- cgit v1.2.3