diff options
| -rw-r--r-- | frontend/.env.example | 1 | ||||
| -rw-r--r-- | frontend/src/App.css | 16 | ||||
| -rw-r--r-- | frontend/src/components/Leaderboards.tsx | 2 | ||||
| -rw-r--r-- | frontend/src/components/Summary.tsx | 10 | ||||
| -rw-r--r-- | frontend/src/pages/About.tsx | 4 | ||||
| -rw-r--r-- | frontend/src/pages/Games.tsx | 2 | ||||
| -rw-r--r-- | frontend/src/pages/Homepage.tsx | 2 | ||||
| -rw-r--r-- | frontend/src/pages/Maplist.tsx | 271 | ||||
| -rw-r--r-- | frontend/src/pages/Maps.tsx | 39 | ||||
| -rw-r--r-- | frontend/src/pages/Rankings.tsx | 32 | ||||
| -rw-r--r-- | frontend/src/pages/Rules.tsx | 2 | ||||
| -rw-r--r-- | frontend/src/pages/User.tsx | 627 | ||||
| -rw-r--r-- | frontend/vite.config.ts | 54 |
13 files changed, 441 insertions, 621 deletions
diff --git a/frontend/.env.example b/frontend/.env.example new file mode 100644 index 0000000..1e3e5e3 --- /dev/null +++ b/frontend/.env.example | |||
| @@ -0,0 +1 @@ | |||
| VITE_API_TARGET="https://lp.pektezol.dev" \ No newline at end of file | |||
diff --git a/frontend/src/App.css b/frontend/src/App.css index a4c058b..464b759 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css | |||
| @@ -49,22 +49,6 @@ | |||
| 49 | --font-barlow-semicondensed-semibold: 'BarlowSemiCondensed-SemiBold'; | 49 | --font-barlow-semicondensed-semibold: 'BarlowSemiCondensed-SemiBold'; |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | main { | ||
| 53 | overflow: auto; | ||
| 54 | overflow-x: hidden; | ||
| 55 | position: relative; | ||
| 56 | |||
| 57 | width: calc(100% - 380px); | ||
| 58 | height: 100vh; | ||
| 59 | left: 350px; | ||
| 60 | |||
| 61 | padding-right: 30px; | ||
| 62 | |||
| 63 | font-size: 40px; | ||
| 64 | font-family: var(--font-barlow-semicondensed-regular); | ||
| 65 | color: var(--color-text); | ||
| 66 | |||
| 67 | } | ||
| 68 | 52 | ||
| 69 | a { | 53 | a { |
| 70 | color: inherit; | 54 | color: inherit; |
diff --git a/frontend/src/components/Leaderboards.tsx b/frontend/src/components/Leaderboards.tsx index 99481a2..1de9b08 100644 --- a/frontend/src/components/Leaderboards.tsx +++ b/frontend/src/components/Leaderboards.tsx | |||
| @@ -51,7 +51,7 @@ const Leaderboards: React.FC<LeaderboardsProps> = ({ mapID }) => { | |||
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | return ( | 53 | return ( |
| 54 | <div> | 54 | <div className="text-foreground"> |
| 55 | {MessageDialogComponent} | 55 | {MessageDialogComponent} |
| 56 | <section id="section6" className="summary2"> | 56 | <section id="section6" className="summary2"> |
| 57 | <div | 57 | <div |
diff --git a/frontend/src/components/Summary.tsx b/frontend/src/components/Summary.tsx index cdecf30..686652a 100644 --- a/frontend/src/components/Summary.tsx +++ b/frontend/src/components/Summary.tsx | |||
| @@ -81,7 +81,7 @@ const Summary: React.FC<SummaryProps> = ({ | |||
| 81 | 81 | ||
| 82 | return ( | 82 | return ( |
| 83 | <> | 83 | <> |
| 84 | <section id="section3" className="summary1"> | 84 | <section id="section3" className="summary1 text-foreground"> |
| 85 | <div | 85 | <div |
| 86 | id="category" | 86 | id="category" |
| 87 | style={data.map.image === "" ? { backgroundColor: "#202232" } : {}} | 87 | style={data.map.image === "" ? { backgroundColor: "#202232" } : {}} |
| @@ -174,8 +174,8 @@ const Summary: React.FC<SummaryProps> = ({ | |||
| 174 | </section> | 174 | </section> |
| 175 | <section id="section4" className="summary1"> | 175 | <section id="section4" className="summary1"> |
| 176 | <div id="difficulty"> | 176 | <div id="difficulty"> |
| 177 | <span>Difficulty</span> | 177 | <span className="">Difficulty</span> |
| 178 | {data.summary.routes[selectedRun].rating === 0 && <span>N/A</span>} | 178 | {data.summary.routes[selectedRun].rating === 0 && <span className="text-foreground">N/A</span>} |
| 179 | {data.summary.routes[selectedRun].rating === 1 && ( | 179 | {data.summary.routes[selectedRun].rating === 1 && ( |
| 180 | <span style={{ color: "lime" }}>Very easy</span> | 180 | <span style={{ color: "lime" }}>Very easy</span> |
| 181 | )} | 181 | )} |
| @@ -255,9 +255,9 @@ const Summary: React.FC<SummaryProps> = ({ | |||
| 255 | ) : ( | 255 | ) : ( |
| 256 | "" | 256 | "" |
| 257 | )} | 257 | )} |
| 258 | <h3>Route Description</h3> | 258 | <h3 className="font-semibold">Route Description</h3> |
| 259 | <span id="description-text"> | 259 | <span id="description-text"> |
| 260 | <ReactMarkdown> | 260 | <ReactMarkdown className="text-foreground"> |
| 261 | {data.summary.routes[selectedRun].description} | 261 | {data.summary.routes[selectedRun].description} |
| 262 | </ReactMarkdown> | 262 | </ReactMarkdown> |
| 263 | </span> | 263 | </span> |
diff --git a/frontend/src/pages/About.tsx b/frontend/src/pages/About.tsx index a5bb291..7802d75 100644 --- a/frontend/src/pages/About.tsx +++ b/frontend/src/pages/About.tsx | |||
| @@ -24,11 +24,11 @@ const About: React.FC = () => { | |||
| 24 | }, []); | 24 | }, []); |
| 25 | 25 | ||
| 26 | return ( | 26 | return ( |
| 27 | <div className="p-8 text-foreground font-[--font-barlow-semicondensed-regular] prose prose-invert max-w-none"> | 27 | <div className="ml-16 p-8 text-foreground font-[--font-barlow-semicondensed-regular] prose prose-invert max-w-none"> |
| 28 | <Helmet> | 28 | <Helmet> |
| 29 | <title>LPHUB | About</title> | 29 | <title>LPHUB | About</title> |
| 30 | </Helmet> | 30 | </Helmet> |
| 31 | <ReactMarkdown>{aboutText}</ReactMarkdown> | 31 | <ReactMarkdown className={"overflow-auto"}>{aboutText}</ReactMarkdown> |
| 32 | </div> | 32 | </div> |
| 33 | ); | 33 | ); |
| 34 | }; | 34 | }; |
diff --git a/frontend/src/pages/Games.tsx b/frontend/src/pages/Games.tsx index 1ef0f57..8587635 100644 --- a/frontend/src/pages/Games.tsx +++ b/frontend/src/pages/Games.tsx | |||
| @@ -10,7 +10,7 @@ interface GamesProps { | |||
| 10 | 10 | ||
| 11 | const Games: React.FC<GamesProps> = ({ games }) => { | 11 | const Games: React.FC<GamesProps> = ({ games }) => { |
| 12 | return ( | 12 | return ( |
| 13 | <div className="ml-10 min-h-screen w-[calc(100%-320px)] text-foreground font-[--font-barlow-semicondensed-regular] overflow-y-auto scrollbar-thin"> | 13 | <div className="ml-20 min-h-screen text-foreground font-[--font-barlow-semicondensed-regular] overflow-y-auto scrollbar-thin"> |
| 14 | <Helmet> | 14 | <Helmet> |
| 15 | <title>LPHUB | Games</title> | 15 | <title>LPHUB | Games</title> |
| 16 | </Helmet> | 16 | </Helmet> |
diff --git a/frontend/src/pages/Homepage.tsx b/frontend/src/pages/Homepage.tsx index 2d16b8d..b4ac3b0 100644 --- a/frontend/src/pages/Homepage.tsx +++ b/frontend/src/pages/Homepage.tsx | |||
| @@ -3,7 +3,7 @@ import { Helmet } from "react-helmet"; | |||
| 3 | 3 | ||
| 4 | const Homepage: React.FC = () => { | 4 | const Homepage: React.FC = () => { |
| 5 | return ( | 5 | return ( |
| 6 | <main className="text-foreground font-[--font-barlow-semicondensed-regular]"> | 6 | <main className="ml-12 relative left-0 w-fullmin-h-screen p-4 sm:p-8 text-foreground font-[--font-barlow-semicondensed-regular]"> |
| 7 | <Helmet> | 7 | <Helmet> |
| 8 | <title>LPHUB | Homepage</title> | 8 | <title>LPHUB | Homepage</title> |
| 9 | </Helmet> | 9 | </Helmet> |
diff --git a/frontend/src/pages/Maplist.tsx b/frontend/src/pages/Maplist.tsx index 8343129..2f0491e 100644 --- a/frontend/src/pages/Maplist.tsx +++ b/frontend/src/pages/Maplist.tsx | |||
| @@ -87,161 +87,160 @@ const Maplist: React.FC = () => { | |||
| 87 | }, [gameChapters, location.search]); | 87 | }, [gameChapters, location.search]); |
| 88 | 88 | ||
| 89 | return ( | 89 | return ( |
| 90 | <main> | 90 | <main className="*:text-foreground w-[calc(100vw-80px)] relative left-0 ml-20 min-h-screen p-4 sm:p-8"> |
| 91 | <Helmet> | 91 | <Helmet> |
| 92 | <title>LPHUB | Maplist</title> | 92 | <title>LPHUB | Maplist</title> |
| 93 | </Helmet> | 93 | </Helmet> |
| 94 | 94 | ||
| 95 | <section className="mt-5"> | 95 | <section className="mt-5"> |
| 96 | <Link to="/games"> | 96 | <Link to="/games"> |
| 97 | <button className="nav-button rounded-[20px] h-10 bg-surface border-0 text-foreground text-lg font-[--font-barlow-semicondensed-regular] transition-colors duration-100 hover:bg-surface2 flex items-center px-2"> | 97 | <button className="nav-button rounded-[20px] h-10 bg-surface border-0 text-foreground text-lg font-[--font-barlow-semicondensed-regular] transition-colors duration-100 hover:bg-surface2 flex items-center px-2"> |
| 98 | <i className="triangle mr-2"></i> | 98 | <i className="triangle mr-2"></i> |
| 99 | <span className="px-2">Games List</span> | 99 | <span className="px-2">Games List</span> |
| 100 | </button> | 100 | </button> |
| 101 | </Link> | 101 | </Link> |
| 102 | </section> | 102 | </section> |
| 103 | 103 | ||
| 104 | {load ? ( | 104 | {load ? ( |
| 105 | <div></div> | 105 | <div></div> |
| 106 | ) : ( | 106 | ) : ( |
| 107 | <section> | 107 | <section> |
| 108 | <h1 className="font-[--font-barlow-condensed-bold] text-6xl my-0 text-foreground"> | 108 | <h1 className="font-[--font-barlow-condensed-bold] text-3xl sm:text-6xl my-0 text-foreground"> |
| 109 | {game?.name} | 109 | {game?.name} |
| 110 | </h1> | 110 | </h1> |
| 111 | |||
| 112 | <div | ||
| 113 | className="text-center rounded-3xl overflow-hidden bg-cover bg-[25%] mt-3 relative" | ||
| 114 | style={{ backgroundImage: `url(${game?.image})` }} | ||
| 115 | > | ||
| 116 | <div className="backdrop-blur-sm flex flex-col w-full"> | ||
| 117 | <div className="h-full flex flex-col justify-center items-center py-6"> | ||
| 118 | <h2 className="my-5 font-[--font-barlow-semicondensed-semibold] text-4xl sm:text-8xl text-foreground"> | ||
| 119 | { | ||
| 120 | game?.category_portals.find( | ||
| 121 | obj => obj.category.id === catNum + 1 | ||
| 122 | )?.portal_count | ||
| 123 | } | ||
| 124 | </h2> | ||
| 125 | <h3 className="font-[--font-barlow-semicondensed-regular] mx-2.5 text-2xl sm:text-4xl my-0 text-foreground"> | ||
| 126 | portals | ||
| 127 | </h3> | ||
| 128 | </div> | ||
| 129 | |||
| 130 | <div className="flex h-12 bg-surface gap-0.5"> | ||
| 131 | {game?.category_portals.map((cat, index) => ( | ||
| 132 | <button | ||
| 133 | key={index} | ||
| 134 | className={`border-0 text-foreground font-[--font-barlow-semicondensed-regular] text-sm sm:text-xl cursor-pointer transition-all duration-100 w-full ${ | ||
| 135 | currentlySelected === cat.category.id || | ||
| 136 | (cat.category.id - 1 === catNum && !hasClicked) | ||
| 137 | ? "bg-surface" | ||
| 138 | : "bg-surface1 hover:bg-surface" | ||
| 139 | }`} | ||
| 140 | onClick={() => { | ||
| 141 | setCatNum(cat.category.id - 1); | ||
| 142 | _update_currently_selected(cat.category.id); | ||
| 143 | }} | ||
| 144 | > | ||
| 145 | <span className="truncate">{cat.category.name}</span> | ||
| 146 | </button> | ||
| 147 | ))} | ||
| 148 | </div> | ||
| 149 | </div> | ||
| 150 | </div> | ||
| 111 | 151 | ||
| 152 | <div> | ||
| 153 | <section> | ||
| 154 | <div> | ||
| 155 | <span className="text-lg sm:text-lg translate-y-1.5 block mt-2.5 text-foreground"> | ||
| 156 | {curChapter?.chapter.name.split(" - ")[0]} | ||
| 157 | </span> | ||
| 158 | </div> | ||
| 159 | <div | ||
| 160 | onClick={_handle_dropdown_click} | ||
| 161 | className="cursor-pointer select-none flex w-fit items-center" | ||
| 162 | > | ||
| 163 | <span className="text-foreground text-base sm:text-2xl"> | ||
| 164 | {curChapter?.chapter.name.split(" - ")[1]} | ||
| 165 | </span> | ||
| 166 | <i className="triangle translate-x-1.5 translate-y-2 -rotate-90"></i> | ||
| 167 | </div> | ||
| 168 | \ | ||
| 112 | <div | 169 | <div |
| 113 | className="text-center rounded-3xl overflow-hidden bg-cover bg-[25%] mt-3 relative" | 170 | className={`absolute z-[1000] bg-surface1 rounded-2xl overflow-hidden p-1 animate-in fade-in duration-100 ${ |
| 114 | style={{ backgroundImage: `url(${game?.image})` }} | 171 | dropdownActive === "none" ? "hidden" : "block" |
| 172 | }`} | ||
| 115 | > | 173 | > |
| 116 | <div className="backdrop-blur-sm flex flex-col w-full"> | 174 | {gameChapters?.chapters.map((chapter, i) => { |
| 117 | <div className="h-full flex flex-col justify-center items-center"> | 175 | return ( |
| 118 | <h2 className="my-5 font-[--font-barlow-semicondensed-semibold] text-8xl text-foreground"> | 176 | <div |
| 119 | { | 177 | key={i} |
| 120 | game?.category_portals.find( | 178 | className="cursor-pointer text-base sm:text-xl rounded-[2000px] p-1 hover:bg-surface text-foreground" |
| 121 | obj => obj.category.id === catNum + 1 | 179 | onClick={() => { |
| 122 | )?.portal_count | 180 | _fetch_chapters(chapter.id.toString()); |
| 123 | } | 181 | _handle_dropdown_click(); |
| 124 | </h2> | 182 | }} |
| 125 | <h3 className="font-[--font-barlow-semicondensed-regular] mx-2.5 text-4xl my-0 text-foreground"> | 183 | > |
| 126 | portals | 184 | {chapter.name} |
| 127 | </h3> | ||
| 128 | </div> | ||
| 129 | |||
| 130 | <div className="flex h-12 bg-surface gap-0.5"> | ||
| 131 | {game?.category_portals.map((cat, index) => ( | ||
| 132 | <button | ||
| 133 | key={index} | ||
| 134 | className={`border-0 text-foreground font-[--font-barlow-semicondensed-regular] text-xl cursor-pointer transition-all duration-100 w-full ${ | ||
| 135 | currentlySelected === cat.category.id || | ||
| 136 | (cat.category.id - 1 === catNum && !hasClicked) | ||
| 137 | ? "bg-surface" | ||
| 138 | : "bg-surface1 hover:bg-surface" | ||
| 139 | }`} | ||
| 140 | onClick={() => { | ||
| 141 | setCatNum(cat.category.id - 1); | ||
| 142 | _update_currently_selected(cat.category.id); | ||
| 143 | }} | ||
| 144 | > | ||
| 145 | <span>{cat.category.name}</span> | ||
| 146 | </button> | ||
| 147 | ))} | ||
| 148 | </div> | ||
| 149 | </div> | 185 | </div> |
| 186 | ); | ||
| 187 | })} | ||
| 150 | </div> | 188 | </div> |
| 189 | </section> | ||
| 151 | 190 | ||
| 152 | <div> | 191 | <section className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-5 my-5"> |
| 153 | <section> | 192 | {curChapter?.maps.map((map, i) => { |
| 154 | <div> | 193 | return ( |
| 155 | <span className="text-lg translate-y-1.5 block mt-2.5 text-foreground"> | 194 | <div key={i} className="bg-surface rounded-3xl overflow-hidden"> |
| 156 | {curChapter?.chapter.name.split(" - ")[0]} | 195 | <Link to={`/maps/${map.id}`}> |
| 157 | </span> | 196 | <span className="text-center text-base sm:text-xl w-full block my-1.5 text-foreground truncate"> |
| 158 | </div> | 197 | {map.name} |
| 159 | <div | 198 | </span> |
| 160 | onClick={_handle_dropdown_click} | 199 | <div |
| 161 | className="cursor-pointer select-none flex w-fit items-center" | 200 | className="flex h-40 sm:h-48 bg-cover relative" |
| 201 | style={{ backgroundImage: `url(${map.image})` }} | ||
| 162 | > | 202 | > |
| 163 | <span className="text-foreground text-2xl"> | 203 | <div className="backdrop-blur-sm w-full flex items-center justify-center"> |
| 164 | {curChapter?.chapter.name.split(" - ")[1]} | 204 | <span className="text-2xl sm:text-4xl font-[--font-barlow-semicondensed-semibold] text-white mr-1.5"> |
| 205 | {map.is_disabled | ||
| 206 | ? map.category_portals[0].portal_count | ||
| 207 | : map.category_portals.find( | ||
| 208 | obj => obj.category.id === catNum + 1 | ||
| 209 | )?.portal_count} | ||
| 210 | </span> | ||
| 211 | <span className="text-2xl sm:text-4xl font-[--font-barlow-semicondensed-regular] text-white"> | ||
| 212 | portals | ||
| 165 | </span> | 213 | </span> |
| 166 | <i className="triangle translate-x-1.5 translate-y-2 -rotate-90"></i> | ||
| 167 | </div> | 214 | </div> |
| 168 | \ | ||
| 169 | <div | ||
| 170 | className={`absolute z-[1000] bg-surface1 rounded-2xl overflow-hidden p-1 animate-in fade-in duration-100 ${ | ||
| 171 | dropdownActive === "none" ? "hidden" : "block" | ||
| 172 | }`} | ||
| 173 | > | ||
| 174 | {gameChapters?.chapters.map((chapter, i) => { | ||
| 175 | return ( | ||
| 176 | <div | ||
| 177 | key={i} | ||
| 178 | className="cursor-pointer text-xl rounded-[2000px] p-1 hover:bg-surface text-foreground" | ||
| 179 | onClick={() => { | ||
| 180 | _fetch_chapters(chapter.id.toString()); | ||
| 181 | _handle_dropdown_click(); | ||
| 182 | }} | ||
| 183 | > | ||
| 184 | {chapter.name} | ||
| 185 | </div> | ||
| 186 | ); | ||
| 187 | })} | ||
| 188 | </div> | 215 | </div> |
| 189 | </section> | 216 | |
| 190 | 217 | <div className="flex mx-2.5 my-4"> | |
| 191 | <section className="grid grid-cols-4 gap-5 my-5"> | 218 | <div className="flex w-full items-center justify-center gap-1.5 rounded-[2000px] ml-0.5 translate-y-px"> |
| 192 | {curChapter?.maps.map((map, i) => { | 219 | {[1, 2, 3, 4, 5].map((point) => ( |
| 193 | return ( | 220 | <div |
| 194 | <div key={i} className="bg-surface rounded-3xl overflow-hidden"> | 221 | key={point} |
| 195 | <Link to={`/maps/${map.id}`}> | 222 | className={`flex h-0.5 w-full rounded-3xl ${ |
| 196 | <span className="text-center text-xl w-full block my-1.5 text-foreground"> | 223 | point <= (map.difficulty + 1) |
| 197 | {map.name} | 224 | ? map.difficulty === 0 |
| 198 | </span> | 225 | ? "bg-green-500" |
| 199 | <div | 226 | : map.difficulty === 1 || map.difficulty === 2 |
| 200 | className="flex h-48 bg-cover relative" | 227 | ? "bg-lime-500" |
| 201 | style={{ backgroundImage: `url(${map.image})` }} | 228 | : map.difficulty === 3 |
| 202 | > | 229 | ? "bg-red-400" |
| 203 | <div className="backdrop-blur-sm w-full flex items-center justify-center"> | 230 | : "bg-red-600" |
| 204 | <span className="text-4xl font-[--font-barlow-semicondensed-semibold] text-white mr-1.5"> | 231 | : "bg-surface1" |
| 205 | {map.is_disabled | 232 | }`} |
| 206 | ? map.category_portals[0].portal_count | 233 | /> |
| 207 | : map.category_portals.find( | 234 | ))} |
| 208 | obj => obj.category.id === catNum + 1 | 235 | </div> |
| 209 | )?.portal_count} | 236 | </div> |
| 210 | </span> | 237 | </Link> |
| 211 | <span className="text-4xl font-[--font-barlow-semicondensed-regular] text-white"> | 238 | </div> |
| 212 | portals | 239 | ); |
| 213 | </span> | 240 | })} |
| 214 | </div> | ||
| 215 | </div> | ||
| 216 | |||
| 217 | {/* Difficulty rating */} | ||
| 218 | <div className="flex mx-2.5 my-4"> | ||
| 219 | <div className="flex w-full items-center justify-center gap-1.5 rounded-[2000px] ml-0.5 translate-y-px"> | ||
| 220 | {[1, 2, 3, 4, 5].map((point) => ( | ||
| 221 | <div | ||
| 222 | key={point} | ||
| 223 | className={`flex h-0.5 w-full rounded-3xl ${ | ||
| 224 | point <= (map.difficulty + 1) | ||
| 225 | ? map.difficulty === 0 | ||
| 226 | ? "bg-green-500" | ||
| 227 | : map.difficulty === 1 || map.difficulty === 2 | ||
| 228 | ? "bg-lime-500" | ||
| 229 | : map.difficulty === 3 | ||
| 230 | ? "bg-red-400" | ||
| 231 | : "bg-red-600" | ||
| 232 | : "bg-surface1" | ||
| 233 | }`} | ||
| 234 | /> | ||
| 235 | ))} | ||
| 236 | </div> | ||
| 237 | </div> | ||
| 238 | </Link> | ||
| 239 | </div> | ||
| 240 | ); | ||
| 241 | })} | ||
| 242 | </section> | ||
| 243 | </div> | ||
| 244 | </section> | 241 | </section> |
| 242 | </div> | ||
| 243 | </section> | ||
| 245 | )} | 244 | )} |
| 246 | </main> | 245 | </main> |
| 247 | ); | 246 | ); |
diff --git a/frontend/src/pages/Maps.tsx b/frontend/src/pages/Maps.tsx index 75753ac..50fe03b 100644 --- a/frontend/src/pages/Maps.tsx +++ b/frontend/src/pages/Maps.tsx | |||
| @@ -64,37 +64,36 @@ const Maps: React.FC<MapProps> = ({ token, isModerator }) => { | |||
| 64 | // loading placeholder | 64 | // loading placeholder |
| 65 | return ( | 65 | return ( |
| 66 | <> | 66 | <> |
| 67 | <main> | 67 | <main className="*:text-foreground relative left-0 w-[calc(100%-20rem)] min-h-screen p-4 sm:p-8"> |
| 68 | <section id="section1" className="summary1"> | 68 | <section id="section1" className="summary1"> |
| 69 | <div> | 69 | <div> |
| 70 | <Link to="/games"> | 70 | <Link to="/games"> |
| 71 | <button | 71 | <button |
| 72 | className="nav-button" | 72 | className="nav-button rounded-[20px] h-10 bg-surface border-0 text-foreground text-lg font-[--font-barlow-semicondensed-regular] transition-colors duration-100 hover:bg-surface2 flex items-center px-2" |
| 73 | style={{ borderRadius: "20px 20px 20px 20px" }} | ||
| 74 | > | 73 | > |
| 75 | <i className="triangle"></i> | 74 | <i className="triangle"></i> |
| 76 | <span>Games List</span> | 75 | <span className="px-2">Games List</span> |
| 77 | </button> | 76 | </button> |
| 78 | </Link> | 77 | </Link> |
| 79 | </div> | 78 | </div> |
| 80 | </section> | 79 | </section> |
| 81 | 80 | ||
| 82 | <section id="section2" className="summary1"> | 81 | <section id="section2" className="summary1 mt-4 flex gap-2 flex-wrap"> |
| 83 | <button className="nav-button"> | 82 | <button className="nav-button"> |
| 84 | <img src={PortalIcon} alt="" className="w-6 h-6" /> | 83 | <img src={PortalIcon} alt="" className="w-5 h-5 sm:w-6 sm:h-6" /> |
| 85 | <span>Summary</span> | 84 | <span>Summary</span> |
| 86 | </button> | 85 | </button> |
| 87 | <button className="nav-button"> | 86 | <button className="nav-button"> |
| 88 | <img src={FlagIcon} alt="" className="w-6 h-6" /> | 87 | <img src={FlagIcon} alt="" className="w-5 h-5 sm:w-6 sm:h-6" /> |
| 89 | <span>Leaderboards</span> | 88 | <span>Leaderboards</span> |
| 90 | </button> | 89 | </button> |
| 91 | <button className="nav-button"> | 90 | <button className="nav-button"> |
| 92 | <img src={ChatIcon} alt="" className="w-6 h-6" /> | 91 | <img src={ChatIcon} alt="" className="w-5 h-5 sm:w-6 sm:h-6" /> |
| 93 | <span>Discussions</span> | 92 | <span>Discussions</span> |
| 94 | </button> | 93 | </button> |
| 95 | </section> | 94 | </section> |
| 96 | 95 | ||
| 97 | <section id="section6" className="summary2" /> | 96 | <section id="section6" className="summary2 mt-4" /> |
| 98 | </main> | 97 | </main> |
| 99 | </> | 98 | </> |
| 100 | ); | 99 | ); |
| @@ -118,47 +117,45 @@ const Maps: React.FC<MapProps> = ({ token, isModerator }) => { | |||
| 118 | <div id="background-image"> | 117 | <div id="background-image"> |
| 119 | <img src={mapSummaryData.map.image} alt="" /> | 118 | <img src={mapSummaryData.map.image} alt="" /> |
| 120 | </div> | 119 | </div> |
| 121 | <main> | 120 | <main className="relative left-0 w-full sm:ml-80 sm:w-[calc(100%-20rem)] min-h-screen max-h-screen overflow-y-auto p-4 sm:p-8 scrollbar-thin scrollbar-track-surface scrollbar-thumb-muted hover:scrollbar-thumb-surface1"> |
| 122 | <section id="section1" className="summary1"> | 121 | <section id="section1" className="summary1"> |
| 123 | <div> | 122 | <div> |
| 124 | <Link to="/games"> | 123 | <Link to="/games"> |
| 125 | <button | 124 | <button |
| 126 | className="nav-button" | 125 | className="nav-button rounded-[20px] h-10 bg-surface border-0 text-foreground text-lg font-[--font-barlow-semicondensed-regular] transition-colors duration-100 hover:bg-surface2 flex items-center px-2" |
| 127 | style={{ borderRadius: "20px 0px 0px 20px" }} | ||
| 128 | > | 126 | > |
| 129 | <i className="triangle"></i> | 127 | <i className="triangle"></i> |
| 130 | <span>Games List</span> | 128 | <span className="px-2">Games List</span> |
| 131 | </button> | 129 | </button> |
| 132 | </Link> | 130 | </Link> |
| 133 | <Link | 131 | <Link |
| 134 | to={`/games/${mapSummaryData.map.is_coop ? "2" : "1"}?chapter=${mapSummaryData.map.chapter_name.split(" ")[1]}`} | 132 | to={`/games/${mapSummaryData.map.is_coop ? "2" : "1"}?chapter=${mapSummaryData.map.chapter_name.split(" ")[1]}`} |
| 135 | > | 133 | > |
| 136 | <button | 134 | <button |
| 137 | className="nav-button" | 135 | className="nav-button ml-2" |
| 138 | style={{ borderRadius: "0px 20px 20px 0px", marginLeft: "2px" }} | ||
| 139 | > | 136 | > |
| 140 | <i className="triangle"></i> | 137 | <i className="triangle"></i> |
| 141 | <span>{mapSummaryData.map.chapter_name}</span> | 138 | <span className="px-2">{mapSummaryData.map.chapter_name}</span> |
| 142 | </button> | 139 | </button> |
| 143 | </Link> | 140 | </Link> |
| 144 | <br /> | 141 | <br /> |
| 145 | <span> | 142 | <span className="block mt-2 text-lg sm:text-xl text-foreground"> |
| 146 | <b>{mapSummaryData.map.map_name}</b> | 143 | <b>{mapSummaryData.map.map_name}</b> |
| 147 | </span> | 144 | </span> |
| 148 | </div> | 145 | </div> |
| 149 | </section> | 146 | </section> |
| 150 | 147 | ||
| 151 | <section id="section2" className="summary1"> | 148 | <section id="section2" className="summary1 mt-4 flex gap-2 flex-wrap"> |
| 152 | <button className="nav-button" onClick={() => setNavState(0)}> | 149 | <button className="nav-button" onClick={() => setNavState(0)}> |
| 153 | <img src={PortalIcon} alt="" className="w-6 h-6" /> | 150 | <img src={PortalIcon} alt="" className="w-5 h-5 sm:w-6 sm:h-6" /> |
| 154 | <span>Summary</span> | 151 | <span>Summary</span> |
| 155 | </button> | 152 | </button> |
| 156 | <button className="nav-button" onClick={() => setNavState(1)}> | 153 | <button className="nav-button" onClick={() => setNavState(1)}> |
| 157 | <img src={FlagIcon} alt="" className="w-6 h-6" /> | 154 | <img src={FlagIcon} alt="" className="w-5 h-5 sm:w-6 sm:h-6" /> |
| 158 | <span>Leaderboards</span> | 155 | <span>Leaderboards</span> |
| 159 | </button> | 156 | </button> |
| 160 | <button className="nav-button" onClick={() => setNavState(2)}> | 157 | <button className="nav-button" onClick={() => setNavState(2)}> |
| 161 | <img src={ChatIcon} alt="" className="w-6 h-6" /> | 158 | <img src={ChatIcon} alt="" className="w-5 h-5 sm:w-6 sm:h-6" /> |
| 162 | <span>Discussions</span> | 159 | <span>Discussions</span> |
| 163 | </button> | 160 | </button> |
| 164 | </section> | 161 | </section> |
diff --git a/frontend/src/pages/Rankings.tsx b/frontend/src/pages/Rankings.tsx index 275f9d0..dec0e17 100644 --- a/frontend/src/pages/Rankings.tsx +++ b/frontend/src/pages/Rankings.tsx | |||
| @@ -12,6 +12,17 @@ import { API } from "@api/Api"; | |||
| 12 | 12 | ||
| 13 | import "@css/Rankings.css"; | 13 | import "@css/Rankings.css"; |
| 14 | 14 | ||
| 15 | enum LeaderboardTypes { | ||
| 16 | official, | ||
| 17 | unofficial, | ||
| 18 | } | ||
| 19 | |||
| 20 | enum RankingCategories { | ||
| 21 | rankings_overall, | ||
| 22 | rankings_multiplayer, | ||
| 23 | rankings_singleplayer, | ||
| 24 | } | ||
| 25 | |||
| 15 | const Rankings: React.FC = () => { | 26 | const Rankings: React.FC = () => { |
| 16 | const [leaderboardData, setLeaderboardData] = React.useState< | 27 | const [leaderboardData, setLeaderboardData] = React.useState< |
| 17 | Ranking | SteamRanking | 28 | Ranking | SteamRanking |
| @@ -19,20 +30,11 @@ const Rankings: React.FC = () => { | |||
| 19 | const [currentLeaderboard, setCurrentLeaderboard] = React.useState< | 30 | const [currentLeaderboard, setCurrentLeaderboard] = React.useState< |
| 20 | RankingType[] | SteamRankingType[] | 31 | RankingType[] | SteamRankingType[] |
| 21 | >(); | 32 | >(); |
| 22 | enum LeaderboardTypes { | ||
| 23 | official, | ||
| 24 | unofficial, | ||
| 25 | } | ||
| 26 | const [currentRankingType, setCurrentRankingType] = | 33 | const [currentRankingType, setCurrentRankingType] = |
| 27 | React.useState<LeaderboardTypes>(LeaderboardTypes.official); | 34 | React.useState<LeaderboardTypes>(LeaderboardTypes.official); |
| 28 | 35 | ||
| 29 | const [leaderboardLoad, setLeaderboardLoad] = React.useState<boolean>(false); | 36 | const [leaderboardLoad, setLeaderboardLoad] = React.useState<boolean>(false); |
| 30 | 37 | ||
| 31 | enum RankingCategories { | ||
| 32 | rankings_overall, | ||
| 33 | rankings_multiplayer, | ||
| 34 | rankings_singleplayer, | ||
| 35 | } | ||
| 36 | const [currentLeaderboardType, setCurrentLeaderboardType] = | 38 | const [currentLeaderboardType, setCurrentLeaderboardType] = |
| 37 | React.useState<RankingCategories>(RankingCategories.rankings_singleplayer); | 39 | React.useState<RankingCategories>(RankingCategories.rankings_singleplayer); |
| 38 | const [load, setLoad] = React.useState<boolean>(false); | 40 | const [load, setLoad] = React.useState<boolean>(false); |
| @@ -100,18 +102,10 @@ const Rankings: React.FC = () => { | |||
| 100 | 102 | ||
| 101 | useEffect(() => { | 103 | useEffect(() => { |
| 102 | _fetch_rankings(); | 104 | _fetch_rankings(); |
| 103 | if (load) { | 105 | }, [_fetch_rankings]); |
| 104 | _set_current_leaderboard(RankingCategories.rankings_singleplayer); | ||
| 105 | } | ||
| 106 | }, [ | ||
| 107 | load, | ||
| 108 | RankingCategories.rankings_singleplayer, | ||
| 109 | _fetch_rankings, | ||
| 110 | _set_current_leaderboard, | ||
| 111 | ]); | ||
| 112 | 106 | ||
| 113 | return ( | 107 | return ( |
| 114 | <main> | 108 | <main className="*:text-foreground"> |
| 115 | <Helmet> | 109 | <Helmet> |
| 116 | <title>LPHUB | Rankings</title> | 110 | <title>LPHUB | Rankings</title> |
| 117 | </Helmet> | 111 | </Helmet> |
diff --git a/frontend/src/pages/Rules.tsx b/frontend/src/pages/Rules.tsx index 7cdc08b..9c7885c 100644 --- a/frontend/src/pages/Rules.tsx +++ b/frontend/src/pages/Rules.tsx | |||
| @@ -25,7 +25,7 @@ const Rules: React.FC = () => { | |||
| 25 | }, []); | 25 | }, []); |
| 26 | 26 | ||
| 27 | return ( | 27 | return ( |
| 28 | <main className="p-8 text-foreground font-[--font-barlow-semicondensed-regular] prose prose-invert max-w-none"> | 28 | <main className="ml-16 p-8 text-foreground font-[--font-barlow-semicondensed-regular] prose prose-invert max-w-none"> |
| 29 | <Helmet> | 29 | <Helmet> |
| 30 | <title>LPHUB | Rules</title> | 30 | <title>LPHUB | Rules</title> |
| 31 | </Helmet> | 31 | </Helmet> |
diff --git a/frontend/src/pages/User.tsx b/frontend/src/pages/User.tsx index 4b8a456..8c699b1 100644 --- a/frontend/src/pages/User.tsx +++ b/frontend/src/pages/User.tsx | |||
| @@ -97,473 +97,312 @@ const User: React.FC<UserProps> = ({ token, profile, gameData }) => { | |||
| 97 | }, [user, game, chapter, location, _get_game_maps]); | 97 | }, [user, game, chapter, location, _get_game_maps]); |
| 98 | 98 | ||
| 99 | if (!user) { | 99 | if (!user) { |
| 100 | return <></>; | 100 | return ( |
| 101 | <div className="flex justify-center items-center h-[50vh] text-lg text-foreground"> | ||
| 102 | Loading... | ||
| 103 | </div> | ||
| 104 | ); | ||
| 101 | } | 105 | } |
| 102 | 106 | ||
| 103 | return ( | 107 | return ( |
| 104 | <main> | 108 | <main className="ml-20 overflow-auto overflow-x-hidden relative w-[calc(100%px)] h-screen font-[--font-barlow-semicondensed-regular] text-foreground text-xl"> |
| 105 | <Helmet> | 109 | <Helmet> |
| 106 | <title>LPHUB | {user.user_name}</title> | 110 | <title>LPHUB | {user.user_name}</title> |
| 107 | <meta name="description" content={user.user_name} /> | 111 | <meta name="description" content={user.user_name} /> |
| 108 | </Helmet> | 112 | </Helmet> |
| 113 | |||
| 109 | {MessageDialogComponent} | 114 | {MessageDialogComponent} |
| 110 | <section id="section1" className="profile"> | 115 | |
| 111 | <div> | 116 | <section className="m-5 bg-gradient-to-t from-[#202232] from-50% to-[#2b2e46] to-50% rounded-3xl p-[30px] mb-[30px] text-foreground"> |
| 112 | <img src={user.avatar_link} alt="profile-image"></img> | 117 | <div className="grid grid-cols-[200px_1fr_auto] items-center gap-[25px] mb-[25px]"> |
| 113 | </div> | 118 | <img |
| 114 | <div id="profile-top"> | 119 | src={user.avatar_link} |
| 120 | alt="Profile" | ||
| 121 | className="w-[120px] h-[120px] rounded-full border-[3px] border-[rgba(205,207,223,0.2)]" | ||
| 122 | /> | ||
| 115 | <div> | 123 | <div> |
| 116 | <div>{user.user_name}</div> | 124 | <h1 className="m-0 mb-[10px] text-[50px] font-bold text-white font-[--font-barlow-semicondensed-regular]"> |
| 117 | <div> | 125 | {user.user_name} |
| 118 | {user.country_code === "XX" ? ( | 126 | </h1> |
| 119 | "" | 127 | {user.country_code !== "XX" && ( |
| 120 | ) : ( | 128 | <div className="flex items-center gap-3 mb-[15px]"> |
| 121 | <img | 129 | <img |
| 122 | src={`https://flagcdn.com/w80/${user.country_code.toLowerCase()}.jpg`} | 130 | src={`https://flagcdn.com/w80/${user.country_code.toLowerCase()}.jpg`} |
| 123 | alt={user.country_code} | 131 | alt={user.country_code} |
| 132 | className="w-6 h-4 rounded-[10px]" | ||
| 124 | /> | 133 | /> |
| 125 | )} | 134 | <span>{user.country_code}</span> |
| 126 | </div> | 135 | </div> |
| 127 | <div> | 136 | )} |
| 128 | {user.titles.map(e => ( | 137 | <div className="flex flex-wrap gap-2"> |
| 138 | {user.titles.map((title, index) => ( | ||
| 129 | <span | 139 | <span |
| 130 | className="titles" | 140 | key={index} |
| 131 | style={{ backgroundColor: `#${e.color}` }} | 141 | className="py-[6px] px-5 pt-[6px] rounded-[10px] text-lg font-normal text-white" |
| 142 | style={{ backgroundColor: `#${title.color}` }} | ||
| 132 | > | 143 | > |
| 133 | {e.name} | 144 | {title.name} |
| 134 | </span> | 145 | </span> |
| 135 | ))} | 146 | ))} |
| 136 | </div> | 147 | </div> |
| 137 | </div> | 148 | </div> |
| 138 | <div> | 149 | <div className="flex gap-[15px] items-center pr-[10px]"> |
| 139 | {user.links.steam === "-" ? ( | 150 | {user.links.steam !== "-" && ( |
| 140 | "" | 151 | <a href={user.links.steam} className="flex items-center justify-center transition-all duration-200 hover:-translate-y-0.5"> |
| 141 | ) : ( | 152 | <img src={SteamIcon} alt="Steam" className="h-[50px] px-[5px] scale-90 brightness-200" /> |
| 142 | <a href={user.links.steam}> | ||
| 143 | <img src={SteamIcon} alt="Steam" /> | ||
| 144 | </a> | 153 | </a> |
| 145 | )} | 154 | )} |
| 146 | {user.links.twitch === "-" ? ( | 155 | {user.links.twitch !== "-" && ( |
| 147 | "" | 156 | <a href={user.links.twitch} className="flex items-center justify-center transition-all duration-200 hover:-translate-y-0.5"> |
| 148 | ) : ( | 157 | <img src={TwitchIcon} alt="Twitch" className="h-[50px] px-[5px] scale-90 brightness-200" /> |
| 149 | <a href={user.links.twitch}> | ||
| 150 | <img src={TwitchIcon} alt="Twitch" /> | ||
| 151 | </a> | 158 | </a> |
| 152 | )} | 159 | )} |
| 153 | {user.links.youtube === "-" ? ( | 160 | {user.links.youtube !== "-" && ( |
| 154 | "" | 161 | <a href={user.links.youtube} className="flex items-center justify-center transition-all duration-200 hover:-translate-y-0.5"> |
| 155 | ) : ( | 162 | <img src={YouTubeIcon} alt="YouTube" className="h-[50px] px-[5px] scale-90 brightness-200" /> |
| 156 | <a href={user.links.youtube}> | ||
| 157 | <img src={YouTubeIcon} alt="Youtube" /> | ||
| 158 | </a> | 163 | </a> |
| 159 | )} | 164 | )} |
| 160 | {user.links.p2sr === "-" ? ( | 165 | {user.links.p2sr !== "-" && ( |
| 161 | "" | 166 | <a href={user.links.p2sr} className="flex items-center justify-center transition-all duration-200 hover:-translate-y-0.5"> |
| 162 | ) : ( | 167 | <img src={PortalIcon} alt="P2SR" className="h-[50px] px-[5px] scale-90 brightness-200" /> |
| 163 | <a href={user.links.p2sr}> | ||
| 164 | <img src={PortalIcon} alt="P2SR" style={{ padding: "0" }} /> | ||
| 165 | </a> | 168 | </a> |
| 166 | )} | 169 | )} |
| 167 | </div> | 170 | </div> |
| 168 | </div> | 171 | </div> |
| 169 | <div id="profile-bottom"> | 172 | |
| 170 | <div> | 173 | <div className="grid grid-cols-3 gap-3 mt-24"> |
| 171 | <span>Overall</span> | 174 | <div className="m-3 bg-[#2b2e46] rounded-[20px] p-5 text-center grid place-items-center grid-rows-[40%_50%]"> |
| 172 | <span> | 175 | <div className="text-inherit text-lg">Overall</div> |
| 173 | {user.rankings.overall.rank === 0 | 176 | <div className="text-white text-[40px]"> |
| 174 | ? "N/A " | 177 | {user.rankings.overall.rank === 0 ? "N/A" : `#${user.rankings.overall.rank}`} |
| 175 | : "#" + user.rankings.overall.rank + " "} | 178 | </div> |
| 176 | <span> | 179 | <div className="text-white text-xl"> |
| 177 | ({user.rankings.overall.completion_count}/ | 180 | {user.rankings.overall.completion_count}/{user.rankings.overall.completion_total} |
| 178 | {user.rankings.overall.completion_total}) | 181 | </div> |
| 179 | </span> | ||
| 180 | </span> | ||
| 181 | </div> | 182 | </div> |
| 182 | <div> | 183 | <div className="m-3 bg-[#2b2e46] rounded-[20px] p-5 text-center grid place-items-center grid-rows-[40%_50%]"> |
| 183 | <span>Singleplayer</span> | 184 | <div className="text-inherit text-lg">Singleplayer</div> |
| 184 | <span> | 185 | <div className="text-white text-[40px]"> |
| 185 | {user.rankings.singleplayer.rank === 0 | 186 | {user.rankings.singleplayer.rank === 0 ? "N/A" : `#${user.rankings.singleplayer.rank}`} |
| 186 | ? "N/A " | 187 | </div> |
| 187 | : "#" + user.rankings.singleplayer.rank + " "} | 188 | <div className="text-white text-xl"> |
| 188 | <span> | 189 | {user.rankings.singleplayer.completion_count}/{user.rankings.singleplayer.completion_total} |
| 189 | ({user.rankings.singleplayer.completion_count}/ | 190 | </div> |
| 190 | {user.rankings.singleplayer.completion_total}) | ||
| 191 | </span> | ||
| 192 | </span> | ||
| 193 | </div> | 191 | </div> |
| 194 | <div> | 192 | <div className="m-3 bg-[#2b2e46] rounded-[20px] p-5 text-center grid place-items-center grid-rows-[40%_50%]"> |
| 195 | <span>Cooperative</span> | 193 | <div className="text-inherit text-lg">Cooperative</div> |
| 196 | <span> | 194 | <div className="text-white text-[40px]"> |
| 197 | {user.rankings.cooperative.rank === 0 | 195 | {user.rankings.cooperative.rank === 0 ? "N/A" : `#${user.rankings.cooperative.rank}`} |
| 198 | ? "N/A " | 196 | </div> |
| 199 | : "#" + user.rankings.cooperative.rank + " "} | 197 | <div className="text-white text-xl"> |
| 200 | <span> | 198 | {user.rankings.cooperative.completion_count}/{user.rankings.cooperative.completion_total} |
| 201 | ({user.rankings.cooperative.completion_count}/ | 199 | </div> |
| 202 | {user.rankings.cooperative.completion_total}) | ||
| 203 | </span> | ||
| 204 | </span> | ||
| 205 | </div> | 200 | </div> |
| 206 | </div> | 201 | </div> |
| 207 | </section> | 202 | </section> |
| 208 | 203 | ||
| 209 | <section id="section2" className="profile"> | 204 | <section className="m-5 h-[60px] grid grid-cols-2"> |
| 210 | <button onClick={() => setNavState(0)}> | 205 | <button |
| 211 | <img src={FlagIcon} alt="" /> | 206 | className={`flex justify-center items-center gap-2 bg-[#2b2e46] border-0 text-inherit font-inherit text-2xl cursor-pointer transition-colors duration-100 rounded-l-3xl hover:bg-[#202232] ${ |
| 212 | Player Records | 207 | navState === 0 ? 'bg-[#202232]' : '' |
| 208 | }`} | ||
| 209 | onClick={() => setNavState(0)} | ||
| 210 | > | ||
| 211 | <img src={FlagIcon} alt="" className="w-5 h-5 scale-[1.2]" /> | ||
| 212 | Player Records | ||
| 213 | </button> | 213 | </button> |
| 214 | <button onClick={() => setNavState(1)}> | 214 | <button |
| 215 | <img src={StatisticsIcon} alt="" /> | 215 | className={`flex justify-center items-center gap-2 bg-[#2b2e46] border-0 text-inherit font-inherit text-2xl cursor-pointer transition-colors duration-100 rounded-r-3xl hover:bg-[#202232] ${ |
| 216 | Statistics | 216 | navState === 1 ? 'bg-[#202232]' : '' |
| 217 | }`} | ||
| 218 | onClick={() => setNavState(1)} | ||
| 219 | > | ||
| 220 | <img src={StatisticsIcon} alt="" className="w-5 h-5 scale-[1.2]" /> | ||
| 221 | Statistics | ||
| 217 | </button> | 222 | </button> |
| 218 | </section> | 223 | </section> |
| 219 | 224 | ||
| 220 | <section id="section3" className="profile1"> | 225 | {navState === 0 && ( |
| 221 | <div id="profileboard-nav"> | 226 | <section className="m-5 block bg-[#202232] rounded-3xl overflow-hidden"> |
| 222 | {gameData === null ? ( | 227 | <div className="grid grid-cols-2 mx-5 my-5 mt-[10px] mb-5"> |
| 223 | <select>error</select> | ||
| 224 | ) : ( | ||
| 225 | <select | 228 | <select |
| 226 | id="select-game" | 229 | className="h-[50px] rounded-3xl text-center text-inherit font-inherit text-2xl border-0 bg-[#2b2e46] mr-[10px]" |
| 227 | onChange={() => { | 230 | value={game} |
| 228 | setGame( | 231 | onChange={(e) => { |
| 229 | (document.querySelector("#select-game") as HTMLInputElement) | 232 | setGame(e.target.value); |
| 230 | .value | ||
| 231 | ); | ||
| 232 | setChapter("0"); | 233 | setChapter("0"); |
| 233 | const chapterSelect = document.querySelector( | ||
| 234 | "#select-chapter" | ||
| 235 | ) as HTMLSelectElement; | ||
| 236 | if (chapterSelect) { | ||
| 237 | chapterSelect.value = "0"; | ||
| 238 | } | ||
| 239 | }} | 234 | }} |
| 240 | > | 235 | > |
| 241 | <option value={0} key={0}> | 236 | <option value="0">All Games</option> |
| 242 | All Scores | 237 | {gameData?.map((g) => ( |
| 243 | </option> | 238 | <option key={g.id} value={g.id}> |
| 244 | {gameData.map((e, i) => ( | 239 | {g.name} |
| 245 | <option value={e.id} key={i + 1}> | ||
| 246 | {e.name} | ||
| 247 | </option> | 240 | </option> |
| 248 | ))} | 241 | ))} |
| 249 | </select> | 242 | </select> |
| 250 | )} | ||
| 251 | 243 | ||
| 252 | {game === "0" ? ( | ||
| 253 | <select disabled> | ||
| 254 | <option>All Chapters</option> | ||
| 255 | </select> | ||
| 256 | ) : chapterData === null ? ( | ||
| 257 | <select></select> | ||
| 258 | ) : ( | ||
| 259 | <select | 244 | <select |
| 260 | id="select-chapter" | 245 | className="h-[50px] rounded-3xl text-center text-inherit font-inherit text-2xl border-0 bg-[#2b2e46] mr-[10px] disabled:opacity-50" |
| 261 | onChange={() => | 246 | value={chapter} |
| 262 | setChapter( | 247 | onChange={(e) => setChapter(e.target.value)} |
| 263 | ( | 248 | disabled={game === "0"} |
| 264 | document.querySelector( | ||
| 265 | "#select-chapter" | ||
| 266 | ) as HTMLInputElement | ||
| 267 | ).value | ||
| 268 | ) | ||
| 269 | } | ||
| 270 | > | 249 | > |
| 271 | <option value="0" key="0"> | 250 | <option value="0">All Chapters</option> |
| 272 | All Chapters | 251 | {chapterData?.chapters |
| 273 | </option> | 252 | .filter(c => !c.is_disabled) |
| 274 | {chapterData.chapters | 253 | .map((c) => ( |
| 275 | .filter(e => e.is_disabled === false) | 254 | <option key={c.id} value={c.id}> |
| 276 | .map((e, i) => ( | 255 | {c.name} |
| 277 | <option value={e.id} key={i + 1}> | ||
| 278 | {e.name} | ||
| 279 | </option> | 256 | </option> |
| 280 | ))} | 257 | ))} |
| 281 | </select> | 258 | </select> |
| 282 | )} | 259 | </div> |
| 283 | </div> | 260 | |
| 284 | <div id="profileboard-top"> | 261 | <div className="h-[34px] grid text-xl pl-[60px] mx-5 my-0 grid-cols-[15%_15%_5%_15%_5%_15%_15%_15%]"> |
| 285 | <span> | 262 | <div className="flex place-items-end cursor-pointer"> |
| 286 | <span>Map Name</span> | 263 | <span>Map Name</span> |
| 287 | <img src={SortIcon} alt="" /> | 264 | <img src={SortIcon} alt="Sort" className="h-5 scale-[0.8]" /> |
| 288 | </span> | 265 | </div> |
| 289 | <span style={{ justifyContent: "center" }}> | 266 | <div className="flex place-items-end cursor-pointer"> |
| 290 | <span>Portals</span> | 267 | <span>Portals</span> |
| 291 | <img src={SortIcon} alt="" /> | 268 | <img src={SortIcon} alt="Sort" className="h-5 scale-[0.8]" /> |
| 292 | </span> | 269 | </div> |
| 293 | <span style={{ justifyContent: "center" }}> | 270 | <div className="flex place-items-end cursor-pointer"> |
| 294 | <span>WRΔ </span> | 271 | <span>WRΔ</span> |
| 295 | <img src={SortIcon} alt="" /> | 272 | <img src={SortIcon} alt="Sort" className="h-5 scale-[0.8]" /> |
| 296 | </span> | 273 | </div> |
| 297 | <span style={{ justifyContent: "center" }}> | 274 | <div className="flex place-items-end cursor-pointer"> |
| 298 | <span>Time</span> | 275 | <span>Time</span> |
| 299 | <img src={SortIcon} alt="" /> | 276 | <img src={SortIcon} alt="Sort" className="h-5 scale-[0.8]" /> |
| 300 | </span> | 277 | </div> |
| 301 | <span> </span> | 278 | <div></div> |
| 302 | <span> | 279 | <div className="flex place-items-end cursor-pointer"> |
| 303 | <span>Rank</span> | 280 | <span>Rank</span> |
| 304 | <img src={SortIcon} alt="" /> | 281 | <img src={SortIcon} alt="Sort" className="h-5 scale-[0.8]" /> |
| 305 | </span> | 282 | </div> |
| 306 | <span> | 283 | <div className="flex place-items-end cursor-pointer"> |
| 307 | <span>Date</span> | 284 | <span>Date</span> |
| 308 | <img src={SortIcon} alt="" /> | 285 | <img src={SortIcon} alt="Sort" className="h-5 scale-[0.8]" /> |
| 309 | </span> | 286 | </div> |
| 310 | <div id="page-number"> | 287 | <div className="flex items-center gap-[10px] justify-center"> |
| 311 | <div> | ||
| 312 | <button | 288 | <button |
| 313 | onClick={() => { | 289 | className="w-8 h-8 border border-[#2b2e46] bg-[#2b2e46] rounded cursor-pointer flex items-center justify-center text-foreground transition-colors duration-100 hover:bg-[#202232] disabled:opacity-50 disabled:cursor-not-allowed" |
| 314 | if (pageNumber !== 1) { | 290 | onClick={() => setPageNumber(Math.max(1, pageNumber - 1))} |
| 315 | setPageNumber(prevPageNumber => prevPageNumber - 1); | 291 | disabled={pageNumber === 1} |
| 316 | const records = document.querySelectorAll( | ||
| 317 | ".profileboard-record" | ||
| 318 | ); | ||
| 319 | records.forEach(r => { | ||
| 320 | (r as HTMLInputElement).style.height = "44px"; | ||
| 321 | }); | ||
| 322 | } | ||
| 323 | }} | ||
| 324 | > | 292 | > |
| 325 | <i | 293 | ← |
| 326 | className="triangle" | ||
| 327 | style={{ position: "relative", left: "-5px" }} | ||
| 328 | ></i>{" "} | ||
| 329 | </button> | 294 | </button> |
| 330 | <span> | 295 | <span className="text-sm text-foreground">{pageNumber}/{pageMax}</span> |
| 331 | {pageNumber}/{pageMax} | ||
| 332 | </span> | ||
| 333 | <button | 296 | <button |
| 334 | onClick={() => { | 297 | className="w-8 h-8 border border-[#2b2e46] bg-[#2b2e46] rounded cursor-pointer flex items-center justify-center text-foreground transition-colors duration-100 hover:bg-[#202232] disabled:opacity-50 disabled:cursor-not-allowed" |
| 335 | if (pageNumber !== pageMax) { | 298 | onClick={() => setPageNumber(Math.min(pageMax, pageNumber + 1))} |
| 336 | setPageNumber(prevPageNumber => prevPageNumber + 1); | 299 | disabled={pageNumber === pageMax} |
| 337 | const records = document.querySelectorAll( | ||
| 338 | ".profileboard-record" | ||
| 339 | ); | ||
| 340 | records.forEach(r => { | ||
| 341 | (r as HTMLInputElement).style.height = "44px"; | ||
| 342 | }); | ||
| 343 | } | ||
| 344 | }} | ||
| 345 | > | 300 | > |
| 346 | <i | 301 | → |
| 347 | className="triangle" | ||
| 348 | style={{ | ||
| 349 | position: "relative", | ||
| 350 | left: "5px", | ||
| 351 | transform: "rotate(180deg)", | ||
| 352 | }} | ||
| 353 | ></i>{" "} | ||
| 354 | </button> | 302 | </button> |
| 355 | </div> | 303 | </div> |
| 356 | </div> | 304 | </div> |
| 357 | </div> | ||
| 358 | <hr /> | ||
| 359 | <div id="profileboard-records"> | ||
| 360 | {game === "0" ? ( | ||
| 361 | user.records | ||
| 362 | .sort((a, b) => a.map_id - b.map_id) | ||
| 363 | .map((r, index) => | ||
| 364 | Math.ceil((index + 1) / 20) === pageNumber ? ( | ||
| 365 | <button className="profileboard-record" key={index}> | ||
| 366 | {r.scores.map((e, i) => ( | ||
| 367 | <> | ||
| 368 | {i !== 0 ? ( | ||
| 369 | <hr style={{ gridColumn: "1 / span 8" }} /> | ||
| 370 | ) : ( | ||
| 371 | "" | ||
| 372 | )} | ||
| 373 | |||
| 374 | <Link to={`/maps/${r.map_id}`}> | ||
| 375 | <span>{r.map_name}</span> | ||
| 376 | </Link> | ||
| 377 | 305 | ||
| 378 | <span style={{ display: "grid" }}>{e.score_count}</span> | 306 | <div> |
| 379 | 307 | {game === "0" ? ( | |
| 380 | <span style={{ display: "grid" }}> | 308 | user.records |
| 381 | {e.score_count - r.map_wr_count > 0 | 309 | .sort((a, b) => a.map_id - b.map_id) |
| 382 | ? `+${e.score_count - r.map_wr_count}` | 310 | .map((record, index) => |
| 383 | : `-`} | 311 | Math.ceil((index + 1) / 20) === pageNumber ? ( |
| 384 | </span> | 312 | <div key={index} className="w-[calc(100%-40px)] mx-5 my-0 mt-[10px] h-11 rounded-[20px] pl-[40px] text-xl text-inherit font-inherit border-0 transition-colors duration-100 bg-[#2b2e46] grid grid-cols-[15%_15%_5%_15%_5%_15%_15%_15%] overflow-hidden whitespace-nowrap cursor-pointer hover:bg-[#202232]"> |
| 385 | <span style={{ display: "grid" }}> | 313 | <Link to={`/maps/${record.map_id}`} className="text-[#3c91e6] no-underline font-inherit flex place-items-center h-11 hover:underline"> |
| 386 | {ticks_to_time(e.score_time)} | 314 | {record.map_name} |
| 387 | </span> | 315 | </Link> |
| 388 | <span> </span> | 316 | <span className="flex place-items-center h-11">{record.scores[0]?.score_count || 'N/A'}</span> |
| 389 | {i === 0 ? <span>#{r.placement}</span> : <span> </span>} | 317 | <span className={`flex place-items-center h-11 ${record.scores[0]?.score_count - record.map_wr_count > 0 ? 'text-[#dc3545]' : ''}`}> |
| 390 | <span>{e.date.split("T")[0]}</span> | 318 | {record.scores[0]?.score_count - record.map_wr_count > 0 |
| 391 | <span style={{ flexDirection: "row-reverse" }}> | 319 | ? `+${record.scores[0].score_count - record.map_wr_count}` |
| 392 | <button | 320 | : '–'} |
| 393 | onClick={() => { | 321 | </span> |
| 394 | message( | 322 | <span className="flex place-items-center h-11">{record.scores[0] ? ticks_to_time(record.scores[0].score_time) : 'N/A'}</span> |
| 395 | "Demo Information", | 323 | <span className="flex place-items-center h-11"></span> |
| 396 | `Demo ID: ${e.demo_id}` | 324 | <span className="flex place-items-center h-11 font-semibold">#{record.placement}</span> |
| 397 | ); | 325 | <span className="flex place-items-center h-11">{record.scores[0]?.date.split("T")[0] || 'N/A'}</span> |
| 398 | }} | 326 | <div className="flex gap-[5px] justify-end flex-row-reverse place-items-center h-11"> |
| 399 | > | 327 | <button |
| 400 | <img src={ThreedotIcon} alt="demo_id" /> | 328 | className="bg-transparent border-0 cursor-pointer transition-colors duration-100 p-0.5 hover:bg-[rgba(32,34,50,0.5)]" |
| 401 | </button> | 329 | onClick={() => message("Demo Information", `Demo ID: ${record.scores[0]?.demo_id}`)} |
| 402 | <button | 330 | title="Demo Info" |
| 403 | onClick={() => | 331 | > |
| 404 | (window.location.href = `/api/v1/demos?uuid=${e.demo_id}`) | 332 | <img src={ThreedotIcon} alt="Info" className="w-4 h-4" /> |
| 405 | } | 333 | </button> |
| 406 | > | 334 | <button |
| 407 | <img src={DownloadIcon} alt="download" /> | 335 | className="bg-transparent border-0 cursor-pointer transition-colors duration-100 p-0.5 hover:bg-[rgba(32,34,50,0.5)]" |
| 336 | onClick={() => window.location.href = `/api/v1/demos?uuid=${record.scores[0]?.demo_id}`} | ||
| 337 | title="Download Demo" | ||
| 338 | > | ||
| 339 | <img src={DownloadIcon} alt="Download" className="w-4 h-4" /> | ||
| 340 | </button> | ||
| 341 | {record.scores.length > 1 && ( | ||
| 342 | <button className="bg-transparent border-0 cursor-pointer transition-colors duration-100 p-0.5 hover:bg-[rgba(32,34,50,0.5)]" title="View History"> | ||
| 343 | <img src={HistoryIcon} alt="History" className="w-4 h-4" /> | ||
| 408 | </button> | 344 | </button> |
| 409 | {i === 0 && r.scores.length > 1 ? ( | 345 | )} |
| 410 | <button | 346 | </div> |
| 411 | onClick={() => { | 347 | </div> |
| 412 | ( | 348 | ) : null |
| 413 | document.querySelectorAll( | ||
| 414 | ".profileboard-record" | ||
| 415 | )[index % 20] as HTMLInputElement | ||
| 416 | ).style.height === "44px" || | ||
| 417 | ( | ||
| 418 | document.querySelectorAll( | ||
| 419 | ".profileboard-record" | ||
| 420 | )[index % 20] as HTMLInputElement | ||
| 421 | ).style.height === "" | ||
| 422 | ? (( | ||
| 423 | document.querySelectorAll( | ||
| 424 | ".profileboard-record" | ||
| 425 | )[index % 20] as HTMLInputElement | ||
| 426 | ).style.height = | ||
| 427 | `${r.scores.length * 46}px`) | ||
| 428 | : (( | ||
| 429 | document.querySelectorAll( | ||
| 430 | ".profileboard-record" | ||
| 431 | )[index % 20] as HTMLInputElement | ||
| 432 | ).style.height = "44px"); | ||
| 433 | }} | ||
| 434 | > | ||
| 435 | <img src={HistoryIcon} alt="history" /> | ||
| 436 | </button> | ||
| 437 | ) : ( | ||
| 438 | "" | ||
| 439 | )} | ||
| 440 | </span> | ||
| 441 | </> | ||
| 442 | ))} | ||
| 443 | </button> | ||
| 444 | ) : ( | ||
| 445 | "" | ||
| 446 | ) | 349 | ) |
| 447 | ) | 350 | ) : ( |
| 448 | ) : maps ? ( | 351 | maps |
| 449 | maps | 352 | ?.filter(map => !map.is_disabled) |
| 450 | .filter(e => e.is_disabled === false) | 353 | .sort((a, b) => a.id - b.id) |
| 451 | .sort((a, b) => a.id - b.id) | 354 | .map((map, index) => { |
| 452 | .map((r, index) => { | 355 | if (Math.ceil((index + 1) / 20) !== pageNumber) return null; |
| 453 | if (Math.ceil((index + 1) / 20) === pageNumber) { | 356 | |
| 454 | let record = user.records.find(e => e.map_id === r.id); | 357 | const record = user.records.find(r => r.map_id === map.id); |
| 455 | return record === undefined ? ( | 358 | |
| 456 | <button | 359 | return ( |
| 457 | className="profileboard-record" | 360 | <div key={index} className={`w-[calc(100%-40px)] mx-5 my-0 mt-[10px] h-11 rounded-[20px] pl-[40px] text-xl text-inherit font-inherit border-0 transition-colors duration-100 bg-[#2b2e46] grid grid-cols-[15%_15%_5%_15%_5%_15%_15%_15%] overflow-hidden whitespace-nowrap cursor-pointer hover:bg-[#202232] ${!record ? 'opacity-65' : ''}`}> |
| 458 | key={index} | 361 | <Link to={`/maps/${map.id}`} className="text-[#3c91e6] no-underline font-inherit flex place-items-center h-11 hover:underline"> |
| 459 | style={{ backgroundColor: "#1b1b20" }} | 362 | {map.name} |
| 460 | > | ||
| 461 | <Link to={`/maps/${r.id}`}> | ||
| 462 | <span>{r.name}</span> | ||
| 463 | </Link> | 363 | </Link> |
| 464 | <span style={{ display: "grid" }}>N/A</span> | 364 | <span className="flex place-items-center h-11">{record?.scores[0]?.score_count || 'N/A'}</span> |
| 465 | <span style={{ display: "grid" }}>N/A</span> | 365 | <span className={`flex place-items-center h-11 ${record?.scores[0]?.score_count && record.scores[0].score_count - record.map_wr_count > 0 ? 'text-[#dc3545]' : ''}`}> |
| 466 | <span>N/A</span> | 366 | {record?.scores[0]?.score_count && record.scores[0].score_count - record.map_wr_count > 0 |
| 467 | <span> </span> | 367 | ? `+${record.scores[0].score_count - record.map_wr_count}` |
| 468 | <span>N/A</span> | 368 | : '–'} |
| 469 | <span>N/A</span> | 369 | </span> |
| 470 | <span style={{ flexDirection: "row-reverse" }}></span> | 370 | <span className="flex place-items-center h-11">{record?.scores[0] ? ticks_to_time(record.scores[0].score_time) : 'N/A'}</span> |
| 471 | </button> | 371 | <span className="flex place-items-center h-11"></span> |
| 472 | ) : ( | 372 | <span className="flex place-items-center h-11 font-semibold">{record ? `#${record.placement}` : 'N/A'}</span> |
| 473 | <button className="profileboard-record" key={index}> | 373 | <span className="flex place-items-center h-11">{record?.scores[0]?.date.split("T")[0] || 'N/A'}</span> |
| 474 | {record.scores.map((e, i) => ( | 374 | <div className="flex gap-[5px] justify-end flex-row-reverse place-items-center h-11"> |
| 475 | <> | 375 | {record?.scores[0] && ( |
| 476 | {i !== 0 ? ( | 376 | <> |
| 477 | <hr style={{ gridColumn: "1 / span 8" }} /> | ||
| 478 | ) : ( | ||
| 479 | "" | ||
| 480 | )} | ||
| 481 | <Link to={`/maps/${r.id}`}> | ||
| 482 | <span>{r.name}</span> | ||
| 483 | </Link> | ||
| 484 | <span style={{ display: "grid" }}> | ||
| 485 | {record!.scores[i].score_count} | ||
| 486 | </span> | ||
| 487 | <span style={{ display: "grid" }}> | ||
| 488 | {record!.scores[i].score_count - | ||
| 489 | record!.map_wr_count > | ||
| 490 | 0 | ||
| 491 | ? `+${record!.scores[i].score_count - record!.map_wr_count}` | ||
| 492 | : `-`} | ||
| 493 | </span> | ||
| 494 | <span style={{ display: "grid" }}> | ||
| 495 | {ticks_to_time(record!.scores[i].score_time)} | ||
| 496 | </span> | ||
| 497 | <span> </span> | ||
| 498 | {i === 0 ? ( | ||
| 499 | <span>#{record!.placement}</span> | ||
| 500 | ) : ( | ||
| 501 | <span> </span> | ||
| 502 | )} | ||
| 503 | <span>{record!.scores[i].date.split("T")[0]}</span> | ||
| 504 | <span style={{ flexDirection: "row-reverse" }}> | ||
| 505 | <button | 377 | <button |
| 506 | onClick={() => { | 378 | className="bg-transparent border-0 cursor-pointer transition-colors duration-100 p-0.5 hover:bg-[rgba(32,34,50,0.5)]" |
| 507 | message( | 379 | onClick={() => message("Demo Information", `Demo ID: ${record.scores[0].demo_id}`)} |
| 508 | "Demo Information", | 380 | title="Demo Info" |
| 509 | `Demo ID: ${e.demo_id}` | ||
| 510 | ); | ||
| 511 | }} | ||
| 512 | > | 381 | > |
| 513 | <img src={ThreedotIcon} alt="demo_id" /> | 382 | <img src={ThreedotIcon} alt="Info" className="w-4 h-4" /> |
| 514 | </button> | 383 | </button> |
| 515 | <button | 384 | <button |
| 516 | onClick={() => | 385 | className="bg-transparent border-0 cursor-pointer transition-colors duration-100 p-0.5 hover:bg-[rgba(32,34,50,0.5)]" |
| 517 | (window.location.href = `/api/v1/demos?uuid=${e.demo_id}`) | 386 | onClick={() => window.location.href = `/api/v1/demos?uuid=${record.scores[0].demo_id}`} |
| 518 | } | 387 | title="Download Demo" |
| 519 | > | 388 | > |
| 520 | <img src={DownloadIcon} alt="download" /> | 389 | <img src={DownloadIcon} alt="Download" className="w-4 h-4" /> |
| 521 | </button> | 390 | </button> |
| 522 | {i === 0 && record!.scores.length > 1 ? ( | 391 | {record.scores.length > 1 && ( |
| 523 | <button | 392 | <button className="bg-transparent border-0 cursor-pointer transition-colors duration-100 p-0.5 hover:bg-[rgba(32,34,50,0.5)]" title="View History"> |
| 524 | onClick={() => { | 393 | <img src={HistoryIcon} alt="History" className="w-4 h-4" /> |
| 525 | ( | ||
| 526 | document.querySelectorAll( | ||
| 527 | ".profileboard-record" | ||
| 528 | )[index % 20] as HTMLInputElement | ||
| 529 | ).style.height === "44px" || | ||
| 530 | ( | ||
| 531 | document.querySelectorAll( | ||
| 532 | ".profileboard-record" | ||
| 533 | )[index % 20] as HTMLInputElement | ||
| 534 | ).style.height === "" | ||
| 535 | ? (( | ||
| 536 | document.querySelectorAll( | ||
| 537 | ".profileboard-record" | ||
| 538 | )[index % 20] as HTMLInputElement | ||
| 539 | ).style.height = | ||
| 540 | `${record!.scores.length * 46}px`) | ||
| 541 | : (( | ||
| 542 | document.querySelectorAll( | ||
| 543 | ".profileboard-record" | ||
| 544 | )[index % 20] as HTMLInputElement | ||
| 545 | ).style.height = "44px"); | ||
| 546 | }} | ||
| 547 | > | ||
| 548 | <img src={HistoryIcon} alt="history" /> | ||
| 549 | </button> | 394 | </button> |
| 550 | ) : ( | ||
| 551 | "" | ||
| 552 | )} | 395 | )} |
| 553 | </span> | 396 | </> |
| 554 | </> | 397 | )} |
| 555 | ))} | 398 | </div> |
| 556 | </button> | 399 | </div> |
| 557 | ); | 400 | ); |
| 558 | } else { | 401 | }) |
| 559 | return null; | 402 | )} |
| 560 | } | 403 | </div> |
| 561 | }) | 404 | </section> |
| 562 | ) : ( | 405 | )} |
| 563 | <>{console.warn(maps)}</> | ||
| 564 | )} | ||
| 565 | </div> | ||
| 566 | </section> | ||
| 567 | </main> | 406 | </main> |
| 568 | ); | 407 | ); |
| 569 | }; | 408 | }; |
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index aa41236..c90383c 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts | |||
| @@ -1,32 +1,38 @@ | |||
| 1 | import { defineConfig } from 'vite' | 1 | import { defineConfig, loadEnv } from 'vite' |
| 2 | import react from '@vitejs/plugin-react' | 2 | import react from '@vitejs/plugin-react' |
| 3 | import tailwindcss from '@tailwindcss/vite' | 3 | import tailwindcss from '@tailwindcss/vite' |
| 4 | import path from 'path' | 4 | import path from 'path' |
| 5 | 5 | ||
| 6 | export default defineConfig({ | 6 | export default defineConfig(({ mode }) => { |
| 7 | plugins: [react(), tailwindcss()], | 7 | // load env variables for the current mode (from .env, .env.development, etc.) |
| 8 | resolve: { | 8 | const env = loadEnv(mode, process.cwd(), '') |
| 9 | alias: { | 9 | const API_TARGET = env.VITE_API_TARGET || 'https://lp.pektezol.dev/' |
| 10 | '@api': path.resolve(__dirname, './src/api'), | 10 | |
| 11 | '@components': path.resolve(__dirname, './src/components'), | 11 | return { |
| 12 | '@css': path.resolve(__dirname, './src/css'), | 12 | plugins: [react(), tailwindcss()], |
| 13 | '@customTypes': path.resolve(__dirname, './src/types'), | 13 | resolve: { |
| 14 | '@hooks': path.resolve(__dirname, './src/hooks'), | 14 | alias: { |
| 15 | '@pages': path.resolve(__dirname, './src/pages'), | 15 | '@api': path.resolve(__dirname, './src/api'), |
| 16 | '@utils': path.resolve(__dirname, './src/utils'), | 16 | '@components': path.resolve(__dirname, './src/components'), |
| 17 | '@images': path.resolve(__dirname, './src/images'), | 17 | '@css': path.resolve(__dirname, './src/css'), |
| 18 | '@customTypes': path.resolve(__dirname, './src/types'), | ||
| 19 | '@hooks': path.resolve(__dirname, './src/hooks'), | ||
| 20 | '@pages': path.resolve(__dirname, './src/pages'), | ||
| 21 | '@utils': path.resolve(__dirname, './src/utils'), | ||
| 22 | '@images': path.resolve(__dirname, './src/images'), | ||
| 23 | }, | ||
| 18 | }, | 24 | }, |
| 19 | }, | 25 | server: { |
| 20 | server: { | 26 | port: 3000, |
| 21 | port: 3000, | 27 | proxy: { |
| 22 | proxy: { | 28 | '/api': { |
| 23 | '/api': { | 29 | target: API_TARGET, |
| 24 | target: 'https://lp.pektezol.dev/', | 30 | changeOrigin: true, |
| 25 | changeOrigin: true, | 31 | }, |
| 26 | }, | 32 | }, |
| 27 | }, | 33 | }, |
| 28 | }, | 34 | build: { |
| 29 | build: { | 35 | outDir: 'build', |
| 30 | outDir: 'build', | 36 | }, |
| 31 | }, | 37 | } |
| 32 | }) \ No newline at end of file | 38 | }) \ No newline at end of file |