aboutsummaryrefslogtreecommitdiff
path: root/frontend
diff options
context:
space:
mode:
authorNidboj132 <28981031+Nidboj132@users.noreply.github.com>2023-09-22 15:58:48 +0200
committerGitHub <noreply@github.com>2023-09-22 15:58:48 +0200
commit57856b4222b790b3bfaff6b4ed129a99818ab88b (patch)
tree5ba99942430dc59803fbc8086d5adf7594da0460 /frontend
parentfix: page number error on empty records (#62) (diff)
downloadlphub-57856b4222b790b3bfaff6b4ed129a99818ab88b.tar.gz
lphub-57856b4222b790b3bfaff6b4ed129a99818ab88b.tar.bz2
lphub-57856b4222b790b3bfaff6b4ed129a99818ab88b.zip
added leaderboard (#53)
Former-commit-id: cca018d7af153dbeab6689f48293ee58a8c871fe
Diffstat (limited to 'frontend')
-rw-r--r--frontend/src/components/pages/summary.css138
-rw-r--r--frontend/src/components/pages/summary.js104
-rw-r--r--frontend/src/imgs/12.pngbin0 -> 1545 bytes
-rw-r--r--frontend/src/imgs/13.pngbin0 -> 1251 bytes
4 files changed, 224 insertions, 18 deletions
diff --git a/frontend/src/components/pages/summary.css b/frontend/src/components/pages/summary.css
index a0f2f5d..6ac56eb 100644
--- a/frontend/src/components/pages/summary.css
+++ b/frontend/src/components/pages/summary.css
@@ -65,7 +65,7 @@
65#section3{ 65#section3{
66 margin: 40px 0 0 0; 66 margin: 40px 0 0 0;
67 67
68 display: grid; 68 display: none;
69 grid-template-columns: 1fr 1fr; 69 grid-template-columns: 1fr 1fr;
70 gap: 20px; 70 gap: 20px;
71} 71}
@@ -73,7 +73,7 @@
73#category{ 73#category{
74 display: grid; 74 display: grid;
75 height: 350px; 75 height: 350px;
76 border-radius: 30px; 76 border-radius: 24px;
77 overflow: hidden; 77 overflow: hidden;
78 78
79} 79}
@@ -121,7 +121,7 @@ p>span.portal-count{font-weight: bold;font-size: 100px;vertical-align: -15%;}
121#history{ 121#history{
122 min-width: 560px; 122 min-width: 560px;
123 background-color: #202232; 123 background-color: #202232;
124 border-radius: 30px; 124 border-radius: 24px;
125 125
126} 126}
127 127
@@ -162,8 +162,8 @@ p>span.portal-count{font-weight: bold;font-size: 100px;vertical-align: -15%;}
162 cursor: pointer; 162 cursor: pointer;
163 transition: background-color .1s; 163 transition: background-color .1s;
164} 164}
165#history>span>button:nth-child(1){border-radius: 0 0 0 30px;} 165#history>span>button:nth-child(1){border-radius: 0 0 0 24px;}
166#history>span>button:nth-child(2){border-radius: 0 0 30px 0;} 166#history>span>button:nth-child(2){border-radius: 0 0 24px 0;}
167 167
168#graph{ 168#graph{
169 display: grid; 169 display: grid;
@@ -239,7 +239,7 @@ p>span.portal-count{font-weight: bold;font-size: 100px;vertical-align: -15%;}
239/* Section 4: Difficulty + count */ 239/* Section 4: Difficulty + count */
240 240
241#section4{ 241#section4{
242 display: grid; 242 display: none;
243 grid-template-columns: 1fr 1fr; 243 grid-template-columns: 1fr 1fr;
244 gap: 20px; 244 gap: 20px;
245 margin: 40px 0 0 0; 245 margin: 40px 0 0 0;
@@ -252,7 +252,7 @@ p>span.portal-count{font-weight: bold;font-size: 100px;vertical-align: -15%;}
252 text-align: center; 252 text-align: center;
253 cursor: default; 253 cursor: default;
254 254
255 border-radius: 20px; 255 border-radius: 24px;
256 display: grid; 256 display: grid;
257 grid-template-rows: 20px 40px 40px; 257 grid-template-rows: 20px 40px 40px;
258} 258}
@@ -273,7 +273,7 @@ p>span.portal-count{font-weight: bold;font-size: 100px;vertical-align: -15%;}
273} 273}
274 274
275.difficulty-rating{ 275.difficulty-rating{
276 border-radius: 20px; 276 border-radius: 24px;
277 width: 40px; height: 3px; 277 width: 40px; height: 3px;
278 background-color: #2b2e46; 278 background-color: #2b2e46;
279} 279}
@@ -302,7 +302,7 @@ p>span.portal-count{font-weight: bold;font-size: 100px;vertical-align: -15%;}
302 margin: 4px; 302 margin: 4px;
303 float:right; 303 float:right;
304 border: 0; 304 border: 0;
305 border-radius: 20px; 305 border-radius: 24px;
306 width: 608px; height: 342px; 306 width: 608px; height: 342px;
307} 307}
308 308
@@ -315,6 +315,126 @@ p>span.portal-count{font-weight: bold;font-size: 100px;vertical-align: -15%;}
315#description-text>b{font-size: inherit;} 315#description-text>b{font-size: inherit;}
316#description-text>a{font-size: inherit;color: #3c91e6;} 316#description-text>a{font-size: inherit;color: #3c91e6;}
317 317
318
319/* Section 6: leaderboards */
320#section6{
321 margin: 40px 0 20px 0;
322 min-height: 600px;
323 background-color: #202232;
324
325 border-radius: 24px;
326 padding: 10px 10px 0 10px;
327
328}
329#leaderboard-top>span{
330 display: flex;
331 place-items: flex-end;
332}
333
334
335#section6>hr{border: 1px solid #2b2e46;margin: 8px 20px 0px 20px;}
336#leaderboard-top{
337 display: grid;
338 grid-template-columns: 7.5% 25% 7.5% 25% 20% 15%;
339 font-size: 20px;
340 height: 34px;
341 padding-left: 60px;
342 margin: 0 20px 0 20px;
343}
344#page-number{
345 display: flex;
346 width: auto;
347 flex-direction: row-reverse;
348}
349#page-number>div{
350width: 100px;
351place-items: center;
352display: grid;
353grid-template-columns: 1fr 1fr 1fr;
354text-align: center;
355}
356#page-number>div>button{
357 width: 30px;
358 height: 30px;
359 background-color: #202232;
360 border: 0;
361 padding: 0;
362 cursor: pointer;
363}
364
365.leaderboard-record{
366 margin: 10px 20px 0px 20px;
367 height: 44px; width: calc(100% - 40px);
368 width: auto;
369
370 color: inherit;
371 border-radius: 40px;
372 text-align: left;
373 padding: 0 0 0 60px;
374 font-size: 20px;
375 font-family: inherit;
376
377
378 grid-template-columns: 3% 4.5% 25% 4% 3.5% 25% 20% 15%;
379 display: grid;
380
381 border: 0;
382 transition: background-color .1s;
383 background-color: #2b2e46;
384}
385
386.leaderboard-record>span:nth-child(1){display: grid;}
387.leaderboard-record>span:nth-child(4){display: grid;}
388.leaderboard-record>span:last-child{flex-direction: row-reverse;}
389.leaderboard-record>span{
390 display: flex;
391 place-items: center;
392 height: 44px;
393}
394.leaderboard-record>span>img{
395 height: 36px;
396 border-radius: 50px;
397 padding: 0;
398 scale: .95;
399}
400.leaderboard-record>span>button{
401 background-color: #0000;
402 border: 0;
403 cursor: pointer;
404 transition: opacity 0.1s;
405}
406
407.hover-popup {
408 position: relative;
409 }
410
411 .hover-popup::after {
412 content: attr(popup-text);
413 position: absolute;
414 /* top: 0%; */
415 /* left: 80%; */
416 /* transform: translateX(-100%); */
417 /* padding: 5px; */
418 background-color: #2b2e46;
419 /* border: 1px solid #161723; */
420 border-radius: 8px;
421 visibility: hidden;
422 opacity: 0;
423 /* transition: visibility 0s, opacity 0.3s ease; */
424 }
425
426 .hover-popup:hover::after {
427 visibility: visible;
428 opacity: 1;
429 }
430
431.leaderboard-record:last-child{margin: 10px 20px 10px 20px;}
432
433
434
435
436
437
318.triangle{ 438.triangle{
319 display: inline-block; 439 display: inline-block;
320 width: 8px; height: 0; 440 width: 8px; height: 0;
diff --git a/frontend/src/components/pages/summary.js b/frontend/src/components/pages/summary.js
index 276389f..deb5fd2 100644
--- a/frontend/src/components/pages/summary.js
+++ b/frontend/src/components/pages/summary.js
@@ -7,6 +7,8 @@ import "./summary.css";
7import img4 from "../../imgs/4.png" 7import img4 from "../../imgs/4.png"
8import img5 from "../../imgs/5.png" 8import img5 from "../../imgs/5.png"
9import img6 from "../../imgs/6.png" 9import img6 from "../../imgs/6.png"
10import img12 from "../../imgs/12.png"
11import img13 from "../../imgs/13.png"
10import Modview from "./summary_modview.js" 12import Modview from "./summary_modview.js"
11 13
12export default function Summary(prop) { 14export default function Summary(prop) {
@@ -28,7 +30,15 @@ const fakedata={} //for debug
28 // eslint-disable-next-line 30 // eslint-disable-next-line
29 }, []); 31 }, []);
30 32
31 33 const [pageNumber, setPageNumber] = React.useState(1);
34 const [lbData, setLbData] = React.useState(null);
35 React.useEffect(() => {
36 fetch(`https://lp.ardapektezol.com/api/v1/maps/${location.pathname.split('/')[2]}/leaderboards?page=${pageNumber}`)
37 .then(r => r.json())
38 .then(d => setLbData(d))
39 console.log(lbData)
40 // eslint-disable-next-line
41 }, [pageNumber]);
32 42
33 43
34 44
@@ -40,6 +50,11 @@ function NavClick() {
40 const btn = document.querySelectorAll("#section2 button.nav-button"); 50 const btn = document.querySelectorAll("#section2 button.nav-button");
41 btn.forEach((e) => {e.style.backgroundColor = "#2b2e46"}); 51 btn.forEach((e) => {e.style.backgroundColor = "#2b2e46"});
42 btn[navState].style.backgroundColor = "#202232"; 52 btn[navState].style.backgroundColor = "#202232";
53
54 document.querySelectorAll("section").forEach((e,i)=>i>=2?e.style.display="none":"")
55 if(navState === 0){document.querySelectorAll(".summary1").forEach((e) => {e.style.display = "grid"});}
56 if(navState === 1){document.querySelectorAll(".summary2").forEach((e) => {e.style.display = "block"});}
57 if(navState === 2){document.querySelectorAll(".summary3").forEach((e) => {e.style.display = "block"});}
43}} 58}}
44 59
45 60
@@ -108,7 +123,6 @@ function graph(state) {
108 <span><br/></span> 123 <span><br/></span>
109 )) 124 ))
110 case 2: // graph 125 case 2: // graph
111 let i = 1
112 let g = 0 126 let g = 0
113 let h = 0 127 let h = 0
114 return graph_numbers.map((e,j)=>( 128 return graph_numbers.map((e,j)=>(
@@ -123,11 +137,11 @@ function graph(state) {
123 <td className='graph_ver' 137 <td className='graph_ver'
124 data-graph={ h++ } 138 data-graph={ h++ }
125 style={{outline: 139 style={{outline:
126 g==h-1 ? 140 g===h-1 ?
127 "1px solid #2b2e46" : g>=h ? "1px dashed white" : "0" }} 141 "1px solid #2b2e46" : g>=h ? "1px dashed white" : "0" }}
128 ></td> 142 ></td>
129 143
130 {g==h && graph_score.includes(graph_max-j) ? 144 {g===h && graph_score.includes(graph_max-j) ?
131 <button className='graph-button' 145 <button className='graph-button'
132 onClick={()=>{ 146 onClick={()=>{
133 selectRun(graph_dates.length-(i-1),catState); 147 selectRun(graph_dates.length-(i-1),catState);
@@ -189,6 +203,29 @@ function YouTubeGetID(url){
189 return (url[2] !== undefined) ? url[2].split(/[^0-9a-z_]/i)[0] : url[0]; 203 return (url[2] !== undefined) ? url[2].split(/[^0-9a-z_]/i)[0] : url[0];
190 } 204 }
191 205
206function TimeAgo(date) {
207 const seconds = Math.floor((new Date() - date) / 1000);
208
209 let interval = Math.floor(seconds / 31536000);
210 if (interval > 1) {return interval + ' years ago';}
211
212 interval = Math.floor(seconds / 2592000);
213 if (interval > 1) {return interval + ' months ago';}
214
215 interval = Math.floor(seconds / 86400);
216 if (interval > 1) {return interval + ' days ago';}
217
218 interval = Math.floor(seconds / 3600);
219 if (interval > 1) {return interval + ' hours ago';}
220
221 interval = Math.floor(seconds / 60);
222 if (interval > 1) {return interval + ' minutes ago';}
223
224 if(seconds < 10) return 'just now';
225
226 return Math.floor(seconds) + ' seconds ago';
227 };
228
192if(data!==null){ 229if(data!==null){
193return ( 230return (
194 <> 231 <>
@@ -213,8 +250,7 @@ return (
213 <button className='nav-button' onClick={()=>setNavState(1)}><img src={img5} alt="" /><span>Leaderboards</span></button> 250 <button className='nav-button' onClick={()=>setNavState(1)}><img src={img5} alt="" /><span>Leaderboards</span></button>
214 <button className='nav-button' onClick={()=>setNavState(2)}><img src={img6} alt="" /><span>Discussions</span></button> 251 <button className='nav-button' onClick={()=>setNavState(2)}><img src={img6} alt="" /><span>Discussions</span></button>
215 </section> 252 </section>
216 253 <section id='section3' className='summary1'>
217 <section id='section3'>
218 <div id='category' 254 <div id='category'
219 style={data.map.image===""?{backgroundColor:"#202232"}:{}}> 255 style={data.map.image===""?{backgroundColor:"#202232"}:{}}>
220 <img src={data.map.image} alt="" id='category-image'></img> 256 <img src={data.map.image} alt="" id='category-image'></img>
@@ -279,7 +315,7 @@ return (
279 315
280 316
281 </section> 317 </section>
282 <section id='section4'> 318 <section id='section4' className='summary1'>
283 <div id='difficulty'> 319 <div id='difficulty'>
284 <span>Difficulty</span> 320 <span>Difficulty</span>
285 {data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].rating === 0 ? (<span style={{color:"lime"}}>Very easy</span>):null} 321 {data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].rating === 0 ? (<span style={{color:"lime"}}>Very easy</span>):null}
@@ -301,7 +337,7 @@ return (
301 </div> 337 </div>
302 </section> 338 </section>
303 339
304 <section id='section5'> 340 <section id='section5' className='summary1'>
305 <div id='description'> 341 <div id='description'>
306 {data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].showcase!=="" ? 342 {data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].showcase!=="" ?
307 <iframe title='Showcase video' src={vid}> </iframe> 343 <iframe title='Showcase video' src={vid}> </iframe>
@@ -312,9 +348,59 @@ return (
312 {data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].description} 348 {data.summary.routes.sort((a,b)=>a.category.id - b.category.id)[selectedRun].description}
313 </ReactMarkdown> 349 </ReactMarkdown>
314 </span> 350 </span>
315 </div> 351 </div>
352 </section>
353
354 {lbData===null?(
355 <section id='section6' className='summary2'>
356 <h1 style={{textAlign:"center"}}>Map is not available for competitive boards.</h1>
357 </section>
358 ):lbData.data.records.length===0?(
359 <section id='section6' className='summary2'>
360 <h1 style={{textAlign:"center"}}>No records found.</h1>
361 </section>
362 ):(
363 <section id='section6' className='summary2'>
316 364
365 <div id='leaderboard-top'>
366 <span>Place</span>
367 <span>Runner</span>
368 <span>Portals</span>
369 <span>Time</span>
370 <span>Date</span>
371 <div id='page-number'>
372 <div>
373
374 <button onClick={() => pageNumber === 1 ? null : setPageNumber(prevPageNumber => prevPageNumber - 1)}
375 ><i className='triangle' style={{position:'relative',left:'-5px',}}></i> </button>
376 <span>{lbData.data.pagination.current_page}/{lbData.data.pagination.total_pages}</span>
377 <button onClick={() => pageNumber === lbData.data.pagination.total_pages ? null : setPageNumber(prevPageNumber => prevPageNumber + 1)}
378 ><i className='triangle' style={{position:'relative',left:'5px',transform:'rotate(180deg)'}}></i> </button>
379 </div>
380 </div>
381 </div>
382 <hr/>
383 <div id='leaderboard-records'>
384 {lbData.data.records.map((r, index) => (
385 <span className='leaderboard-record' key={index} >
386 <span>{r.placement}</span>
387 <span> </span>
388 <span><img src={r.user.avatar_link} alt='' /> &nbsp; {r.user.user_name}</span>
389 <span>{r.score_count}</span>
390 <span> </span>
391 <span>{r.score_time}</span>
392 <span className='hover-popup' popup-text={r.record_date.replace("T",' ').split(".")[0]}>{ TimeAgo(new Date(r.record_date.replace("T"," ").replace("Z",""))) }</span>
393 <span>
394 <button onClick={()=>{window.alert(r.demo_id)}}><img src={img13} alt="demo_id" /></button>
395 <button onClick={()=>window.location.href=`https://lp.ardapektezol.com/api/v1/demos?uuid=${r.demo_id}`}><img src={img12} alt="download" /></button>
396 </span>
397 </span>
398 ))}
399 {console.log(lbData.data.records.length)}
400 </div>
317 </section> 401 </section>
402 )}
403
318 </main> 404 </main>
319 </> 405 </>
320 ) 406 )
diff --git a/frontend/src/imgs/12.png b/frontend/src/imgs/12.png
new file mode 100644
index 0000000..abb7717
--- /dev/null
+++ b/frontend/src/imgs/12.png
Binary files differ
diff --git a/frontend/src/imgs/13.png b/frontend/src/imgs/13.png
new file mode 100644
index 0000000..28a67c5
--- /dev/null
+++ b/frontend/src/imgs/13.png
Binary files differ