diff options
Diffstat (limited to 'frontend/src')
| -rw-r--r-- | frontend/src/components/NavBar.tsx | 59 | ||||
| -rw-r--r-- | frontend/src/components/displays/GenericScoreDisplay.tsx | 43 | ||||
| -rw-r--r-- | frontend/src/components/modals/JsonUploadModal.tsx | 2 | ||||
| -rw-r--r-- | frontend/src/pages/Home.tsx | 20 | ||||
| -rw-r--r-- | frontend/src/pages/Import.tsx | 24 | ||||
| -rw-r--r-- | frontend/src/pages/Landing.tsx | 50 | ||||
| -rw-r--r-- | frontend/src/pages/Score.tsx | 35 |
7 files changed, 147 insertions, 86 deletions
diff --git a/frontend/src/components/NavBar.tsx b/frontend/src/components/NavBar.tsx index 4be8607..ac70dfb 100644 --- a/frontend/src/components/NavBar.tsx +++ b/frontend/src/components/NavBar.tsx @@ -1,10 +1,13 @@ import { Link } from "react-router"; +import { useState } from "react"; export const NavBar = ({ currentPage, user, handleLogout }: { currentPage: string; user: { username: string }; handleLogout: () => void; }) => { + const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); + const menuOptions = ( <> <Link @@ -14,6 +17,7 @@ export const NavBar = ({ currentPage, user, handleLogout }: { ? 'bg-violet-600 text-white shadow-lg shadow-violet-500/25' : 'text-slate-300 hover:text-white hover:bg-slate-800/50' }`} + onClick={() => setIsMobileMenuOpen(false)} > Home </Link> @@ -24,6 +28,7 @@ export const NavBar = ({ currentPage, user, handleLogout }: { ? 'bg-violet-600 text-white shadow-lg shadow-violet-500/25' : 'text-slate-300 hover:text-white hover:bg-slate-800/50' }`} + onClick={() => setIsMobileMenuOpen(false)} > Import Data </Link> @@ -34,6 +39,7 @@ export const NavBar = ({ currentPage, user, handleLogout }: { ? 'bg-violet-600 text-white shadow-lg shadow-violet-500/25' : 'text-slate-300 hover:text-white hover:bg-slate-800/50' }`} + onClick={() => setIsMobileMenuOpen(false)} > Community Scores </Link> @@ -44,13 +50,16 @@ export const NavBar = ({ currentPage, user, handleLogout }: { <nav className="border-b border-slate-800/50 bg-slate-950/95 backdrop-blur-sm sticky top-0 z-50"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="flex items-center justify-between h-16"> + {/* Logo */} <div className="flex items-center space-x-3"> <div className="w-8 h-8 bg-gradient-to-r from-violet-600 to-violet-700 rounded-lg flex items-center justify-center shadow-lg"> <span className="text-white font-bold text-sm">M</span> </div> <span className="text-white font-semibold text-lg">Mirage</span> </div> - <div className="flex items-center space-x-4"> + + {/* Desktop Menu */} + <div className="hidden md:flex items-center space-x-4"> {menuOptions} <span className="text-slate-300 text-sm"> Welcome back,{" "} @@ -65,8 +74,54 @@ export const NavBar = ({ currentPage, user, handleLogout }: { Sign Out </button> </div> + + {/* Mobile menu button */} + <div className="flex md:hidden"> + <button + onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)} + className="inline-flex items-center justify-center p-2 rounded-md text-slate-300 hover:text-white hover:bg-slate-800/50 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-violet-500" + aria-expanded={isMobileMenuOpen} + > + <span className="sr-only">Open main menu</span> + {isMobileMenuOpen ? ( + <svg className="block h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> + <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /> + </svg> + ) : ( + <svg className="block h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> + <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" /> + </svg> + )} + </button> + </div> </div> + + {/* Mobile Menu */} + {isMobileMenuOpen && ( + <div className="md:hidden"> + <div className="px-2 pt-2 pb-3 space-y-1"> + {menuOptions} + <div className="border-t border-slate-800/50 mt-3 pt-3"> + <div className="text-slate-300 text-sm px-4 py-2"> + Welcome back,{" "} + <span className="text-violet-400 font-medium"> + {user.username} + </span> + </div> + <button + onClick={() => { + handleLogout(); + setIsMobileMenuOpen(false); + }} + className="w-full text-left text-slate-300 hover:text-white px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 hover:bg-slate-800/50" + > + Sign Out + </button> + </div> + </div> + </div> + )} </div> </nav> ); -}; +};
\ No newline at end of file diff --git a/frontend/src/components/displays/GenericScoreDisplay.tsx b/frontend/src/components/displays/GenericScoreDisplay.tsx index 4201d95..41a125c 100644 --- a/frontend/src/components/displays/GenericScoreDisplay.tsx +++ b/frontend/src/components/displays/GenericScoreDisplay.tsx @@ -269,7 +269,7 @@ const ScoreDisplay: React.FC<ScoreDisplayProps> = ({ if (viewMode === "cards") { return ( - <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 gap-6"> + <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 gap-4 sm:gap-6"> {sortedScores.map((score, index) => { const chartIdHash = SHA1(`${internalGameName}${score.title}${score.artist}`).toString(); // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -279,18 +279,18 @@ const ScoreDisplay: React.FC<ScoreDisplayProps> = ({ return ( <div key={score.id || index} - className="bg-slate-900/50 backdrop-blur-sm border border-slate-800/50 rounded-xl p-6 hover:border-violet-500/30 transition-all duration-300 hover:shadow-lg hover:shadow-violet-500/10" + className="bg-slate-900/50 backdrop-blur-sm border border-slate-800/50 rounded-lg sm:rounded-xl p-4 sm:p-6 hover:border-violet-500/30 transition-all duration-300 hover:shadow-lg hover:shadow-violet-500/10" > {/* Primary Info */} <div className="flex items-start justify-between mb-4"> <div className="flex-1 min-w-0"> {!hideTitleArtist && ( <Link to={`/chart?chartId=${chartIdHash}`}> - <h3 className="text-lg font-semibold text-white mb-1 break-words leading-tight"> + <h3 className="text-base sm:text-lg font-semibold text-white mb-1 break-words leading-tight"> {score.title || score.song || "Unknown Title"} </h3> {score.artist && ( - <p className="text-slate-400 text-sm break-words leading-tight"> + <p className="text-slate-400 text-xs sm:text-sm break-words leading-tight"> {score.artist} </p> )} @@ -306,13 +306,13 @@ const ScoreDisplay: React.FC<ScoreDisplayProps> = ({ {/* Main Stats */} {mainStats.length > 0 && ( - <div className="grid grid-cols-2 gap-4 mb-4"> + <div className="grid grid-cols-2 gap-2 sm:gap-4 mb-4"> {mainStats.slice(0, 4).map(([key, value]) => ( - <div key={key} className="bg-slate-800/50 rounded-lg p-3"> - <p className="text-slate-400 text-xs uppercase tracking-wide mb-1"> + <div key={key} className="bg-slate-800/50 rounded-lg p-2 sm:p-3"> + <p className="text-slate-400 text-[10px] sm:text-xs uppercase tracking-wide mb-1"> {getDisplayName(key)} </p> - <p className="text-white font-semibold text-lg"> + <p className="text-white font-semibold text-sm sm:text-lg"> {renderValue(value, key)} </p> </div> @@ -335,7 +335,7 @@ const ScoreDisplay: React.FC<ScoreDisplayProps> = ({ <div className="mb-4"> <div className="grid grid-cols-2 gap-2 text-sm"> {others.map(([key, value]) => ( - <div key={key} className="flex justify-between"> + <div key={key} className="flex justify-between text-xs sm:text-sm"> <span className="text-slate-400"> {getDisplayName(key)}: </span> @@ -350,7 +350,7 @@ const ScoreDisplay: React.FC<ScoreDisplayProps> = ({ {/* Timestamp */} <div className="pt-4 border-t border-slate-800/50"> - <p className="text-slate-500 text-xs"> + <p className="text-slate-500 text-[10px] sm:text-xs"> {new Date( typeof timestamp === "number" ? timestamp : timestamp, ).toLocaleDateString()}{" "} @@ -372,21 +372,22 @@ const ScoreDisplay: React.FC<ScoreDisplayProps> = ({ return ( <div className="bg-slate-900/50 backdrop-blur-sm border border-slate-800/50 rounded-xl overflow-hidden"> - <div className="overflow-x-auto"> - <table className="w-full text-sm min-w-[1000px]"> + <div className="overflow-x-auto relative"> + <div className="md:hidden absolute right-0 top-0 bottom-0 w-8 bg-gradient-to-l from-slate-900/80 to-transparent pointer-events-none z-10"></div> + <table className="w-full text-sm min-w-[800px] md:min-w-[1000px]"> <thead className="bg-slate-800/50 border-b border-slate-700/50"> <tr> {tableKeys.map((key) => ( <th key={key} - className="px-4 py-3 text-left text-slate-300 font-medium" + className="px-2 sm:px-4 py-2 sm:py-3 text-left text-slate-300 font-medium text-xs sm:text-sm" > {key === "judgements" ? ( <span>{getDisplayName(key)}</span> ) : ( <button onClick={() => onSort(key)} - className="flex items-center space-x-2 hover:text-white transition-colors" + className="flex items-center space-x-1 sm:space-x-2 hover:text-white transition-colors" > <span>{getDisplayName(key)}</span> <SortIcon field={key} /> @@ -395,7 +396,7 @@ const ScoreDisplay: React.FC<ScoreDisplayProps> = ({ </th> ))} {showActions && ( - <th className="px-4 py-3 text-left text-slate-300 font-medium w-16"> + <th className="px-2 sm:px-4 py-2 sm:py-3 text-left text-slate-300 font-medium w-16 text-xs sm:text-sm"> Actions </th> )} @@ -408,10 +409,10 @@ const ScoreDisplay: React.FC<ScoreDisplayProps> = ({ className="hover:bg-slate-800/30 transition-colors group" > {tableKeys.map((key) => ( - <td key={key} className="px-4 py-3"> + <td key={key} className="px-2 sm:px-4 py-2 sm:py-3 text-xs sm:text-sm"> {key === "lamp" || key === "diff_lamp" ? ( <div className="flex items-center space-x-2"> - <span className="inline-block bg-slate-800/50 text-slate-200 px-2 py-1 rounded text-xs border border-slate-600"> + <span className="inline-block bg-slate-800/50 text-slate-200 px-1 sm:px-2 py-0.5 sm:py-1 rounded text-[10px] sm:text-xs border border-slate-600 whitespace-nowrap"> {score[key] || "No Clear"} </span> </div> @@ -420,7 +421,7 @@ const ScoreDisplay: React.FC<ScoreDisplayProps> = ({ {renderValue(score[key], key, true)} </div> ) : key === "timestamp" ? ( - <span className="text-slate-400 text-xs"> + <span className="text-slate-400 text-[10px] sm:text-xs whitespace-nowrap"> {new Date( typeof score[key] === "number" ? score[key] @@ -428,7 +429,7 @@ const ScoreDisplay: React.FC<ScoreDisplayProps> = ({ ).toLocaleDateString()} </span> ) : key === "username" ? ( - <span className="text-violet-400 text-sm font-medium"> + <span className="text-violet-400 text-xs sm:text-sm font-medium"> {score[key] || "Unknown"} </span> ) : ( @@ -441,13 +442,13 @@ const ScoreDisplay: React.FC<ScoreDisplayProps> = ({ </td> ))} {showActions && ( - <td className="px-4 py-3"> + <td className="px-2 sm:px-4 py-2 sm:py-3"> <button onClick={() => onDelete(score.id)} className="text-red-400 hover:text-red-300 opacity-100 transition-opacity duration-200 p-1 rounded bg-red-500/10" title="Delete score" > - <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> + <svg className="w-3 h-3 sm:w-4 sm:h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /> </svg> </button> diff --git a/frontend/src/components/modals/JsonUploadModal.tsx b/frontend/src/components/modals/JsonUploadModal.tsx index 7d8f838..3bb2b9b 100644 --- a/frontend/src/components/modals/JsonUploadModal.tsx +++ b/frontend/src/components/modals/JsonUploadModal.tsx @@ -77,7 +77,7 @@ const JsonUploadModal = ({ isOpen, onClose, onUpload, game }: JsonUploadModalPro /> {/* Modal */} - <div className="flex min-h-full items-center justify-center p-4"> + <div className="flex min-h-full items-center justify-center p-4 sm:p-6"> <div className="relative bg-slate-900 rounded-lg border border-slate-700 w-full max-w-md p-6 shadow-xl"> {/* Header */} <div className="mb-6"> diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index 71ba772..4eb1c59 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -75,22 +75,22 @@ const Home = () => { <NavBar user={user} handleLogout={handleLogout} currentPage="home"/> {/* Main Content */} - <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> + <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 sm:py-8"> {/* Header */} - <div className="mb-8"> - <h1 className="text-3xl font-bold text-white mb-2">Dashboard</h1> - <p className="text-slate-400"> + <div className="mb-6 sm:mb-8"> + <h1 className="text-2xl sm:text-3xl font-bold text-white mb-2">Dashboard</h1> + <p className="text-sm sm:text-base text-slate-400"> Track your rhythm game progress and performance </p> </div> {/* Supported Games */} - <div className="mb-12"> - <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> + <div className="mb-8 sm:mb-12"> + <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6"> {supportedGames.map((game) => ( <div key={game.internalName} - className="bg-slate-900 rounded-xl border border-slate-700 overflow-hidden hover:border-violet-500 hover:shadow-lg hover:shadow-violet-500/10 transition-all duration-300 group cursor-pointer" + className="bg-slate-900 rounded-lg sm:rounded-xl border border-slate-700 overflow-hidden hover:border-violet-500 hover:shadow-lg hover:shadow-violet-500/10 transition-all duration-300 group cursor-pointer" onClick={() => navigate(`/score?game=${game.internalName}`)} > <div className="aspect-video bg-slate-800 relative overflow-hidden"> @@ -120,11 +120,11 @@ const Home = () => { </div> )} </div> - <div className="p-6"> - <h3 className="text-xl font-semibold text-white mb-2 group-hover:text-violet-400 transition-colors"> + <div className="p-4 sm:p-6"> + <h3 className="text-lg sm:text-xl font-semibold text-white mb-2 group-hover:text-violet-400 transition-colors"> {game.formattedName} </h3> - <p className="text-slate-400 text-sm leading-relaxed"> + <p className="text-slate-400 text-xs sm:text-sm leading-relaxed"> {game.description} </p> </div> diff --git a/frontend/src/pages/Import.tsx b/frontend/src/pages/Import.tsx index fe2501f..08976c7 100644 --- a/frontend/src/pages/Import.tsx +++ b/frontend/src/pages/Import.tsx @@ -92,8 +92,8 @@ const Import = () => { }; const JsonUploadCard = () => ( - <div className="bg-slate-800 rounded-lg border border-slate-700 p-6 hover:border-violet-500 transition-colors"> - <div className="w-12 h-12 bg-violet-600/20 rounded-lg flex items-center justify-center mb-4"> + <div className="bg-slate-800 rounded-lg border border-slate-700 p-4 sm:p-6 hover:border-violet-500 transition-colors"> + <div className="w-10 sm:w-12 h-10 sm:h-12 bg-violet-600/20 rounded-lg flex items-center justify-center mb-3 sm:mb-4"> <svg className="w-6 h-6 text-violet-400" fill="none" @@ -114,7 +114,7 @@ const Import = () => { </p> <button onClick={() => setIsJsonModalOpen(true)} - className="w-full bg-violet-600 hover:bg-violet-700 text-white py-2 px-4 rounded-md font-medium transition-colors" + className="w-full bg-violet-600 hover:bg-violet-700 text-white py-2 px-3 sm:px-4 rounded-md text-sm sm:text-base font-medium transition-colors" > Upload JSON </button> @@ -148,7 +148,7 @@ const Import = () => { </p> <button onClick={() => setIsEamusementModalOpen(true)} - className="w-full bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-md font-medium transition-colors" + className="w-full bg-blue-600 hover:bg-blue-700 text-white py-2 px-3 sm:px-4 rounded-md text-sm sm:text-base font-medium transition-colors" > Connect e-amusement </button> @@ -193,11 +193,11 @@ const Import = () => { <NavBar user={user} handleLogout={handleLogout} currentPage="import"/> {/* Main Content */} - <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> + <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 sm:py-8"> {/* Header */} - <div className="mb-8"> - <h1 className="text-3xl font-bold text-white mb-2">Import Data</h1> - <p className="text-slate-400"> + <div className="mb-6 sm:mb-8"> + <h1 className="text-2xl sm:text-3xl font-bold text-white mb-2">Import Data</h1> + <p className="text-sm sm:text-base text-slate-400"> Import your game scores and progress from various sources </p> </div> @@ -224,10 +224,10 @@ const Import = () => { )} {/* Game Selection Card */} - <div className="bg-slate-900 rounded-lg border border-slate-700 p-8"> - <div className="mb-8"> - <h2 className="text-xl font-bold text-white mb-4">Select Game</h2> - <p className="text-slate-400 text-sm mb-6"> + <div className="bg-slate-900 rounded-lg border border-slate-700 p-4 sm:p-6 lg:p-8"> + <div className="mb-6 sm:mb-8"> + <h2 className="text-lg sm:text-xl font-bold text-white mb-3 sm:mb-4">Select Game</h2> + <p className="text-slate-400 text-xs sm:text-sm mb-4 sm:mb-6"> Choose the game you want to import data for </p> diff --git a/frontend/src/pages/Landing.tsx b/frontend/src/pages/Landing.tsx index c607cee..5ca4459 100644 --- a/frontend/src/pages/Landing.tsx +++ b/frontend/src/pages/Landing.tsx @@ -14,16 +14,16 @@ const Landing = () => { </div> <span className="text-white font-semibold text-lg">Mirage</span> </div> - <div className="flex items-center space-x-4"> + <div className="flex items-center space-x-2 sm:space-x-4"> <Link to="/login" - className="text-slate-300 hover:text-white px-3 py-2 rounded-md text-sm font-medium transition-colors" + className="text-slate-300 hover:text-white px-2 sm:px-3 py-2 rounded-md text-sm font-medium transition-colors" > Log In </Link> <Link to="/register" - className="bg-violet-600 hover:bg-violet-700 text-white px-4 py-2 rounded-md text-sm font-medium transition-colors" + className="bg-violet-600 hover:bg-violet-700 text-white px-3 sm:px-4 py-2 rounded-md text-sm font-medium transition-colors" > Create Account </Link> @@ -32,7 +32,7 @@ const Landing = () => { </div> </nav> - <section className="relative py-18 lg:py-24 overflow-hidden"> + <section className="relative py-12 sm:py-18 lg:py-24 overflow-hidden"> {/* Background Image */} <div className="absolute inset-0 bg-cover bg-center bg-no-repeat opacity-30" @@ -48,10 +48,10 @@ const Landing = () => { {/* Content */} <div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="max-w-4xl"> - <h1 className="text-5xl md:text-6xl font-bold text-white mb-6 leading-tight"> + <h1 className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-bold text-white mb-4 sm:mb-6 leading-tight"> Welcome to <span className="text-violet-400">Mirage!</span> </h1> - <p className="text-xl text-slate-300 mb-8 leading-relaxed"> + <p className="text-lg sm:text-xl text-slate-300 mb-6 sm:mb-8 leading-relaxed"> Looks like you're not logged in. If you've got an account,{" "} <Link to="/login" @@ -65,17 +65,17 @@ const Landing = () => { </section> {/* Introduction Section */} - <section className="py-8 border-t border-slate-800"> + <section className="py-6 sm:py-8 border-t border-slate-800"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="max-w-4xl"> - <h2 className="text-3xl md:text-4xl font-bold text-white mb-6"> + <h2 className="text-2xl sm:text-3xl md:text-4xl font-bold text-white mb-4 sm:mb-6"> What is this? </h2> - <p className="text-lg text-slate-300 leading-relaxed"> + <p className="text-base sm:text-lg text-slate-300 leading-relaxed"> <span className="font-semibold text-violet-300">Mirage</span> is a flexible Rhythm Game Score Tracker that works without relying on predefined seeds. It offers a lightweight alternative that can act - as a holdover for tracking scores when chart metadata isn’t + as a holdover for tracking scores when chart metadata isn't readily available. </p> </div> @@ -83,13 +83,13 @@ const Landing = () => { </section> {/* Track Your Scores Section */} - <section className="py-8 border-t border-slate-800"> + <section className="py-6 sm:py-8 border-t border-slate-800"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="max-w-4xl"> - <h2 className="text-3xl md:text-4xl font-bold text-white mb-6"> + <h2 className="text-2xl sm:text-3xl md:text-4xl font-bold text-white mb-4 sm:mb-6"> <span className="text-violet-400">Track</span> Your Scores </h2> - <p className="text-lg text-slate-300 leading-relaxed"> + <p className="text-base sm:text-lg text-slate-300 leading-relaxed"> Mirage lets you import scores from the games you play for safe-keeping, so you can prevent losing them to the void should anything ever happen. @@ -99,13 +99,13 @@ const Landing = () => { </section> {/* Add your own games */} - <section className="py-8 border-t border-slate-800"> + <section className="py-6 sm:py-8 border-t border-slate-800"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="max-w-4xl"> - <h2 className="text-3xl md:text-4xl font-bold text-white mb-6"> + <h2 className="text-2xl sm:text-3xl md:text-4xl font-bold text-white mb-4 sm:mb-6"> <span className="text-purple-400">Add</span> Your Own Games </h2> - <p className="text-lg text-slate-300 leading-relaxed"> + <p className="text-base sm:text-lg text-slate-300 leading-relaxed"> While not as robust as other seed-based systems. Mirage makes it easy for you to track and keep score for any game, no matter how niche it is. @@ -115,13 +115,13 @@ const Landing = () => { </section> {/* Join with others */} - <section className="py-8 border-t border-slate-800"> + <section className="py-6 sm:py-8 border-t border-slate-800"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="max-w-4xl"> - <h2 className="text-3xl md:text-4xl font-bold text-white mb-6"> + <h2 className="text-2xl sm:text-3xl md:text-4xl font-bold text-white mb-4 sm:mb-6"> Mirage <span className="text-violet-400">Together</span> </h2> - <p className="text-lg text-slate-300 leading-relaxed"> + <p className="text-base sm:text-lg text-slate-300 leading-relaxed"> Mirage is easily self-hostable and can be used locally for a single person or hosted so you can track scores with your friends. Everything is open-source. </p> </div> @@ -129,22 +129,22 @@ const Landing = () => { </section> {/* Call to Action Section */} - <section className="py-12 border-t border-slate-800 bg-slate-900"> + <section className="py-8 sm:py-12 border-t border-slate-800 bg-slate-900"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="text-center max-w-2xl mx-auto"> - <h2 className="text-3xl md:text-4xl font-bold text-white mb-8"> + <h2 className="text-2xl sm:text-3xl md:text-4xl font-bold text-white mb-6 sm:mb-8"> Ready to Start <span className="text-violet-400">Tracking?</span> </h2> <div className="flex flex-col sm:flex-row items-center justify-center gap-4"> <Link to="/register" - className="bg-violet-600 hover:bg-violet-700 text-white px-8 py-3 rounded-md text-lg font-medium transition-colors min-w-[200px] shadow-lg shadow-violet-600/25" + className="w-full sm:w-auto bg-violet-600 hover:bg-violet-700 text-white px-6 sm:px-8 py-3 rounded-md text-base sm:text-lg font-medium transition-colors min-w-[200px] shadow-lg shadow-violet-600/25 text-center" > Create Account </Link> <Link to="/login" - className="border border-slate-600 hover:border-violet-500 text-slate-300 hover:text-white px-8 py-3 rounded-md text-lg font-medium transition-colors min-w-[200px]" + className="w-full sm:w-auto border border-slate-600 hover:border-violet-500 text-slate-300 hover:text-white px-6 sm:px-8 py-3 rounded-md text-base sm:text-lg font-medium transition-colors min-w-[200px] text-center" > Log In </Link> @@ -154,7 +154,7 @@ const Landing = () => { </section> {/* Footer */} - <footer className="border-t border-slate-800 py-12 bg-slate-950"> + <footer className="border-t border-slate-800 py-8 sm:py-12 bg-slate-950"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="text-center text-slate-400"> <a className="hover:underline" href=""> @@ -167,4 +167,4 @@ const Landing = () => { ); }; -export default Landing; +export default Landing;
\ No newline at end of file diff --git a/frontend/src/pages/Score.tsx b/frontend/src/pages/Score.tsx index 0d18d7a..d842cb0 100644 --- a/frontend/src/pages/Score.tsx +++ b/frontend/src/pages/Score.tsx @@ -39,12 +39,12 @@ const Score = () => { const renderRequestFilterMenu = () => { const filterOptions = getFilterOptions(gameName); return ( - <div className="flex items-center space-x-2 bg-slate-900/50 backdrop-blur-sm rounded-xl p-1 border border-slate-800/50"> + <div className="flex flex-wrap items-center gap-2 bg-slate-900/50 backdrop-blur-sm rounded-xl p-1 border border-slate-800/50"> {filterOptions.map((option) => ( <button key={option.value} onClick={() => setRequestOrder(option.value)} - className={`px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 ${ + className={`px-3 sm:px-4 py-2 rounded-lg text-xs sm:text-sm font-medium transition-all duration-200 whitespace-nowrap ${ requestOrder === option.value ? "bg-violet-600 text-white shadow-lg shadow-violet-500/25" : "text-slate-300 hover:text-white hover:bg-slate-800/50" @@ -153,16 +153,16 @@ const Score = () => { return ( <div className="min-h-screen bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950 text-white"> <NavBar user={user} handleLogout={handleLogout} currentPage="score" /> - <main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12"> - <div className="mb-12"> - <div className="flex items-center justify-between mb-4"> - <h1 className="text-4xl font-bold bg-gradient-to-r from-violet-400 to-violet-600 bg-clip-text text-transparent"> + <main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6 sm:py-12"> + <div className="mb-6 sm:mb-12"> + <div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 mb-4"> + <h1 className="text-2xl sm:text-3xl md:text-4xl font-bold bg-gradient-to-r from-violet-400 to-violet-600 bg-clip-text text-transparent"> Your Scores </h1> - <div className="flex items-center space-x-2 bg-slate-900/50 backdrop-blur-sm rounded-xl p-1 border border-slate-800/50"> + <div className="flex items-center space-x-1 sm:space-x-2 bg-slate-900/50 backdrop-blur-sm rounded-xl p-1 border border-slate-800/50"> <button onClick={() => setViewMode("cards")} - className={`px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 ${ + className={`px-3 sm:px-4 py-2 rounded-lg text-xs sm:text-sm font-medium transition-all duration-200 ${ viewMode === "cards" ? "bg-violet-600 text-white shadow-lg shadow-violet-500/25" : "text-slate-300 hover:text-white hover:bg-slate-800/50" @@ -172,7 +172,7 @@ const Score = () => { </button> <button onClick={() => setViewMode("table")} - className={`px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 ${ + className={`px-3 sm:px-4 py-2 rounded-lg text-xs sm:text-sm font-medium transition-all duration-200 ${ viewMode === "table" ? "bg-violet-600 text-white shadow-lg shadow-violet-500/25" : "text-slate-300 hover:text-white hover:bg-slate-800/50" @@ -184,8 +184,8 @@ const Score = () => { </div> {/* Filter Menu */} - <div className="flex items-center justify-between mb-6"> - <div className="flex items-center space-x-4"> + <div className="flex items-center justify-between mb-4 sm:mb-6"> + <div className="w-full sm:w-auto"> {renderRequestFilterMenu()} </div> </div> @@ -219,13 +219,13 @@ const Score = () => { })()} {numPages > 1 && ( - <div className="flex justify-center mt-12"> - <div className="flex space-x-2 bg-slate-900/50 backdrop-blur-sm rounded-xl p-2 border border-slate-800/50"> + <div className="flex justify-center mt-8 sm:mt-12"> + <div className="flex flex-wrap gap-1 sm:gap-2 bg-slate-900/50 backdrop-blur-sm rounded-xl p-2 border border-slate-800/50 max-w-full overflow-x-auto"> {[...Array(numPages)].map((_, i) => ( <button key={i} onClick={() => fetchScores(i + 1)} - className={`px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 ${ + className={`px-3 sm:px-4 py-2 rounded-lg text-xs sm:text-sm font-medium transition-all duration-200 ${ currentPage === i + 1 ? "bg-violet-600 text-white shadow-lg shadow-violet-500/25" : "text-slate-300 hover:text-white hover:bg-slate-800/50" @@ -237,9 +237,14 @@ const Score = () => { </div> </div> )} - <p className="text-slate-400 mt-4 text-lg"> + <p className="text-slate-400 mt-4 text-sm sm:text-base md:text-lg text-center"> Displaying {scores.length} scores • Page {currentPage} of {numPages} </p> + {viewMode === "table" && ( + <p className="text-slate-500 mt-2 text-xs text-center md:hidden"> + ← Swipe horizontally to see more → + </p> + )} </main> </div> ); |
