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/App.css | 42 ++++++++++++ site/src/App.tsx | 13 ++++ site/src/assets/react.svg | 1 + site/src/components/NewsFeed.tsx | 136 +++++++++++++++++++++++++++++++++++++++ site/src/components/TitleBar.tsx | 119 ++++++++++++++++++++++++++++++++++ site/src/index.css | 1 + site/src/main.tsx | 13 ++++ site/src/pages/Homepage.tsx | 90 ++++++++++++++++++++++++++ site/src/vite-env.d.ts | 1 + 9 files changed, 416 insertions(+) create mode 100644 site/src/App.css create mode 100644 site/src/App.tsx create mode 100644 site/src/assets/react.svg create mode 100644 site/src/components/NewsFeed.tsx create mode 100644 site/src/components/TitleBar.tsx create mode 100644 site/src/index.css create mode 100644 site/src/main.tsx create mode 100644 site/src/pages/Homepage.tsx create mode 100644 site/src/vite-env.d.ts (limited to 'site/src') diff --git a/site/src/App.css b/site/src/App.css new file mode 100644 index 0000000..b9d355d --- /dev/null +++ b/site/src/App.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/site/src/App.tsx b/site/src/App.tsx new file mode 100644 index 0000000..5142463 --- /dev/null +++ b/site/src/App.tsx @@ -0,0 +1,13 @@ +import Home from './pages/Homepage'; +import { Routes, Route } from "react-router-dom"; + +function App() { + return ( + + } /> + } /> + + ); +} + +export default App; \ No newline at end of file diff --git a/site/src/assets/react.svg b/site/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/site/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file 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 diff --git a/site/src/index.css b/site/src/index.css new file mode 100644 index 0000000..a461c50 --- /dev/null +++ b/site/src/index.css @@ -0,0 +1 @@ +@import "tailwindcss"; \ No newline at end of file diff --git a/site/src/main.tsx b/site/src/main.tsx new file mode 100644 index 0000000..528c9a2 --- /dev/null +++ b/site/src/main.tsx @@ -0,0 +1,13 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import { BrowserRouter } from "react-router-dom"; +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + + + , +) diff --git a/site/src/pages/Homepage.tsx b/site/src/pages/Homepage.tsx new file mode 100644 index 0000000..0472c39 --- /dev/null +++ b/site/src/pages/Homepage.tsx @@ -0,0 +1,90 @@ +import { useEffect, useState } from "react"; +import { NewsData, NewsFeed } from "../components/NewsFeed"; +import { useParams } from "react-router-dom"; +import TitleBar from "../components/TitleBar"; + +interface ArcadeNewsAPIData { + fetch_time: number; + news_posts: Array; +} + +export default function Home() { + const { gameId } = useParams<{ gameId?: string }>(); + const [newsFeedData, setNewsFeedData] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const fetchNews = async () => { + setLoading(true); + let jsonFile = "news.json"; + if (gameId) { + switch(gameId) { + case "sdvx": + jsonFile = "sdvx_news.json"; + break; + case "iidx": + jsonFile = "iidx_news.json"; + break; + case "chunithm_jp": + jsonFile = "chunithm_jp_news.json"; + break; + default: + jsonFile = "news.json"; + } + } + + try { + const response = await fetch(`${import.meta.env.VITE_API_DOMAIN}/${jsonFile}`); + if (!response.ok) { + throw new Error(`Failed to fetch news: ${response.statusText}`); + } + const data: ArcadeNewsAPIData = await response.json(); + setNewsFeedData(data); + } catch (e) { + console.error(e); + } finally { + setLoading(false); + } + }; + fetchNews(); + }, [gameId]); // Re-fetch when gameId changes + + if (loading || newsFeedData === null) { + return ( + <> + +
+
+
+ + ); + } + + // Game-specific title mapping + const getGameTitle = () => { + if (!gameId) return null; + + switch(gameId) { + case "sdvx": return "SOUND VOLTEX"; + case "iidx": return "beatmania IIDX"; + case "chunithm_jp": return "CHUNITHM (JAPAN)"; + default: return gameId.toUpperCase(); + } + }; + + return ( + <> + +
+
+ {gameId && ( +

+ {getGameTitle()} News +

+ )} + +
+
+ + ); +} \ No newline at end of file diff --git a/site/src/vite-env.d.ts b/site/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/site/src/vite-env.d.ts @@ -0,0 +1 @@ +/// -- cgit v1.2.3