Fix Commit history
BIN
TFR/server/static/background.png
Normal file
After Width: | Height: | Size: 570 KiB |
BIN
TFR/server/static/background.webp
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
TFR/server/static/error-images/1.jpg
Normal file
After Width: | Height: | Size: 400 KiB |
BIN
TFR/server/static/error-images/2.jpg
Normal file
After Width: | Height: | Size: 165 KiB |
BIN
TFR/server/static/error-images/3.jpg
Normal file
After Width: | Height: | Size: 708 KiB |
54
TFR/server/static/js/main.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
function addFlashMessage(message, type='success') {
|
||||
let flask = document.createElement('p');
|
||||
flask.onclick = () => flask.remove();
|
||||
flask.classList.add(type);
|
||||
flask.innerHTML = message;
|
||||
|
||||
let close = document.createElement('span');
|
||||
close.innerHTML = '<i class="ph-bold ph-x"></i>';
|
||||
|
||||
flask.appendChild(close);
|
||||
document.querySelector('.flash').appendChild(flask);
|
||||
}
|
||||
|
||||
function ajax(url, form, callback, method='POST') {
|
||||
console.log(form)
|
||||
fetch(url, {
|
||||
method: method,
|
||||
body: form,
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => callback(data))
|
||||
.catch(error => addFlashMessage(error.error, 'error'));
|
||||
}
|
||||
|
||||
function deleteToken(id) {
|
||||
let form = new FormData();
|
||||
form.append('token_id', id);
|
||||
|
||||
ajax('/api/tokens', form, (data) => {
|
||||
if (data.success) {
|
||||
addFlashMessage(data.success, 'success');
|
||||
document.querySelector(`#token-${id}`).remove();
|
||||
} else {
|
||||
addFlashMessage(data.error, 'error');
|
||||
}
|
||||
}, 'DELETE');
|
||||
}
|
||||
|
||||
function addToken() {
|
||||
ajax('/api/tokens', null, (data) => {
|
||||
if (data.success) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
addFlashMessage(data.error, 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function viewToken(id) {
|
||||
let token = document.querySelector(`#token-${id}`);
|
||||
let hidden = token.children[2];
|
||||
|
||||
hidden.classList.toggle('hidden');
|
||||
}
|
88
TFR/server/static/js/search.js
Normal file
|
@ -0,0 +1,88 @@
|
|||
function showHint() {
|
||||
let search = document.querySelector('.search > input');
|
||||
let searchPos = search.getBoundingClientRect();
|
||||
let hint = document.querySelector('.search-hint');
|
||||
|
||||
hint.style.width = search.offsetWidth + 'px';
|
||||
hint.style.left = searchPos.left + 'px';
|
||||
hint.style.top = searchPos.bottom + 'px';
|
||||
|
||||
hint.style.display = 'flex';
|
||||
}
|
||||
|
||||
|
||||
function hideHint() {
|
||||
let hint = document.querySelector('.search-hint');
|
||||
hint.style.display = 'none';
|
||||
}
|
||||
|
||||
|
||||
function updateHint() {
|
||||
let search = document.querySelector('.search > input');
|
||||
let searchPos = search.getBoundingClientRect();
|
||||
let hint = document.querySelector('.search-hint');
|
||||
|
||||
hint.style.width = search.offsetWidth + 'px';
|
||||
hint.style.left = searchPos.left + 'px';
|
||||
hint.style.top = searchPos.bottom + 'px';
|
||||
}
|
||||
|
||||
|
||||
function getSearch() {
|
||||
let search = document.querySelector('.search > input').value;
|
||||
let hint = document.querySelector('.search-hint');
|
||||
|
||||
if (search.length === 0) {
|
||||
hint.innerHTML = '<p>Start typing to see results...</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/api/users?search=' + search.toString(), {
|
||||
method: 'GET',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.length === 0) {
|
||||
hint.innerHTML = '<p>No results found...</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
hint.innerHTML = '';
|
||||
|
||||
data.forEach(user => {
|
||||
let el = document.createElement('button');
|
||||
el.innerHTML = user;
|
||||
el.onmousedown = function (event) {
|
||||
event.preventDefault();
|
||||
search = document.querySelector('.search > input');
|
||||
search.value = user.toString();
|
||||
}
|
||||
hint.appendChild(el);
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
hint.innerHTML = '<p>Something went wrong...</p>';
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
window.onload = () => {
|
||||
let typingTimer;
|
||||
let search = document.querySelector('.search > input');
|
||||
|
||||
if (search === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('resize', updateHint);
|
||||
|
||||
search.addEventListener('focus', showHint);
|
||||
search.addEventListener('blur', hideHint);
|
||||
search.addEventListener('keydown', () => {
|
||||
clearTimeout(typingTimer);
|
||||
});
|
||||
search.addEventListener('keyup', () => {
|
||||
clearTimeout(typingTimer);
|
||||
typingTimer = setTimeout(getSearch, 250);
|
||||
});
|
||||
}
|
87
TFR/server/static/sass/button.sass
Normal file
|
@ -0,0 +1,87 @@
|
|||
.button
|
||||
margin: auto 0
|
||||
padding: 0.5rem 0.7rem
|
||||
|
||||
display: flex
|
||||
align-items: center
|
||||
gap: 0.5rem
|
||||
|
||||
text-decoration: none
|
||||
white-space: nowrap
|
||||
font-size: 0.9em
|
||||
|
||||
background-color: RGBA($white, 0.02)
|
||||
color: RGB($white)
|
||||
|
||||
border-radius: 2px
|
||||
border: 0 solid transparent
|
||||
|
||||
transition: background-color 0.1s ease-in-out
|
||||
|
||||
&:hover
|
||||
background-color: RGBA($white, 0.3)
|
||||
|
||||
&.primary
|
||||
background-color: RGBA($primary, 0.02)
|
||||
color: RGB($primary)
|
||||
|
||||
&:hover
|
||||
background-color: RGBA($primary, 0.3)
|
||||
|
||||
&.secondary
|
||||
background-color: RGBA($secondary, 0.02)
|
||||
color: RGB($secondary)
|
||||
|
||||
&:hover
|
||||
background-color: RGBA($secondary, 0.3)
|
||||
|
||||
> i
|
||||
font-size: 1.25em
|
||||
display: block
|
||||
|
||||
.search
|
||||
margin: auto 0
|
||||
width: 100%
|
||||
|
||||
position: relative
|
||||
display: flex
|
||||
flex-direction: row
|
||||
|
||||
> label
|
||||
padding: 0.5rem 0.7rem
|
||||
|
||||
text-decoration: none
|
||||
white-space: nowrap
|
||||
font-size: 0.9em
|
||||
|
||||
background-color: RGBA($white, 0.02)
|
||||
color: RGB($white)
|
||||
border-radius: 2px 0 0 2px
|
||||
|
||||
> input
|
||||
margin: 0
|
||||
padding: 0.5rem 0.7rem
|
||||
|
||||
width: 100%
|
||||
|
||||
text-decoration: none
|
||||
white-space: nowrap
|
||||
font-size: 0.9em
|
||||
|
||||
background-color: RGBA($white, 0.02)
|
||||
color: RGB($white)
|
||||
|
||||
border: 0 solid transparent
|
||||
border-left: 1px solid RGBA($white, 0.1)
|
||||
border-radius: 0 2px 2px 0
|
||||
|
||||
&:hover
|
||||
background-color: RGBA($white, 0.1)
|
||||
border-left: 1px solid transparent
|
||||
|
||||
&:focus-visible, &:focus
|
||||
background-color: RGBA($white, 0.1)
|
||||
|
||||
outline: 0 solid transparent
|
||||
border-left: 1px solid transparent
|
||||
border-radius: 0 2px 0 0
|
51
TFR/server/static/sass/hint.sass
Normal file
|
@ -0,0 +1,51 @@
|
|||
.search-hint
|
||||
position: absolute
|
||||
|
||||
display: none
|
||||
flex-direction: column
|
||||
|
||||
text-decoration: none
|
||||
white-space: nowrap
|
||||
font-size: 0.9em
|
||||
|
||||
background-color: RGBA($white, 0.1)
|
||||
backdrop-filter: blur(10px)
|
||||
|
||||
border-top: 1px solid RGBA($white, 0.1)
|
||||
border-radius: 0 0 2px 2px
|
||||
|
||||
overflow: hidden
|
||||
z-index: 999
|
||||
|
||||
> p
|
||||
margin: 0
|
||||
padding: 0.5rem 0.75rem
|
||||
|
||||
width: 100%
|
||||
white-space: nowrap
|
||||
text-overflow: ellipsis
|
||||
|
||||
color: RGB($white)
|
||||
|
||||
overflow: hidden
|
||||
|
||||
> button
|
||||
padding: 0.5rem 0.75rem
|
||||
|
||||
width: 100%
|
||||
white-space: nowrap
|
||||
text-overflow: ellipsis
|
||||
|
||||
font-size: 0.9rem
|
||||
text-align: left
|
||||
|
||||
background-color: transparent
|
||||
color: RGB($white)
|
||||
border: 0 solid transparent
|
||||
|
||||
overflow: hidden
|
||||
|
||||
&:hover
|
||||
background-color: RGBA($white, 0.1)
|
||||
color: RGB($primary)
|
||||
border: 0 solid transparent
|
357
TFR/server/static/sass/style.sass
Normal file
|
@ -0,0 +1,357 @@
|
|||
$black: var(--black)
|
||||
$white: var(--white)
|
||||
$primary: var(--primary)
|
||||
$secondary: var(--secondary)
|
||||
$gold: var(--gold)
|
||||
$silver: var(--silver)
|
||||
$bronze: var(--bronze)
|
||||
|
||||
\:root
|
||||
--black: 31, 27, 21
|
||||
--white: 232, 227, 227
|
||||
--primary: 210, 206, 97
|
||||
--secondary: 185, 77, 77
|
||||
--gold: 255, 222, 70
|
||||
--silver: 229, 220, 206
|
||||
--bronze: 193, 145, 69
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Merriweather:ital,wght@0,300;0,400;0,700;0,900;1,300;1,400;1,700&display=swap')
|
||||
@import "button"
|
||||
@import "hint"
|
||||
|
||||
*
|
||||
box-sizing: border-box
|
||||
font-family: 'Merriweather', serif
|
||||
|
||||
html
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
body
|
||||
margin: 0
|
||||
padding: 0
|
||||
display: flex
|
||||
flex-direction: row
|
||||
background-color: RGB($black)
|
||||
color: RGB($white)
|
||||
|
||||
.background
|
||||
position: fixed
|
||||
inset: 0
|
||||
overflow: hidden
|
||||
z-index: 1
|
||||
|
||||
> img
|
||||
width: 100%
|
||||
height: 100%
|
||||
object-fit: cover
|
||||
|
||||
.app
|
||||
margin: 0 auto
|
||||
padding: 0
|
||||
|
||||
width: 800px
|
||||
min-height: 100vh
|
||||
|
||||
position: relative
|
||||
display: flex
|
||||
flex-direction: column
|
||||
|
||||
background-color: rgba($black, 0.4)
|
||||
backdrop-filter: blur(5px)
|
||||
z-index: 2
|
||||
|
||||
.table
|
||||
width: 100%
|
||||
height: auto
|
||||
background-color: rgba($black, 0.7)
|
||||
border-radius: 2px
|
||||
overflow: hidden
|
||||
|
||||
> table
|
||||
width: 100%
|
||||
border-collapse: collapse
|
||||
|
||||
> tbody
|
||||
> tr
|
||||
> th
|
||||
padding: 0.6rem
|
||||
background-image: linear-gradient(to bottom, RGBA($white, 0.05), transparent)
|
||||
text-align: left
|
||||
font-weight: bolder
|
||||
|
||||
> td
|
||||
padding: 0.5rem
|
||||
text-align: left
|
||||
|
||||
&.player
|
||||
color: RGB($secondary)
|
||||
text-shadow: 0 0 5px RGBA($secondary, 0.7)
|
||||
|
||||
i
|
||||
padding: 0.25rem
|
||||
color: RGB($black)
|
||||
border-radius: 2px
|
||||
|
||||
&.first
|
||||
background-color: RGBA($gold, 0.6)
|
||||
|
||||
&.second
|
||||
background-color: RGBA($silver, 0.6)
|
||||
|
||||
&.third
|
||||
background-color: RGBA($bronze, 0.6)
|
||||
|
||||
&:nth-child(odd) > td
|
||||
background-color: RGBA($white, 0.01)
|
||||
|
||||
header
|
||||
padding: 1rem 1rem 0
|
||||
display: flex
|
||||
flex-direction: column
|
||||
gap: 0.4rem
|
||||
background-image: linear-gradient(to bottom, RGB(var(--black)), transparent)
|
||||
|
||||
> hr
|
||||
margin: 0
|
||||
width: 100%
|
||||
border: none
|
||||
border-bottom: 1px solid RGBA($white, 0.1)
|
||||
|
||||
.title
|
||||
margin-bottom: 1rem
|
||||
height: auto
|
||||
> img
|
||||
width: 100%
|
||||
height: auto
|
||||
max-width: 100%
|
||||
|
||||
nav, nav > form
|
||||
padding: 0
|
||||
width: 100%
|
||||
display: flex
|
||||
flex-direction: row
|
||||
justify-content: center
|
||||
gap: 0.4rem
|
||||
|
||||
.spacer
|
||||
width: 100%
|
||||
|
||||
.flash
|
||||
display: flex
|
||||
flex-direction: column
|
||||
justify-content: center
|
||||
align-items: center
|
||||
|
||||
> p
|
||||
margin: 0
|
||||
padding: 0.75rem 1rem
|
||||
|
||||
width: 100%
|
||||
position: relative
|
||||
|
||||
background-color: RGB($black)
|
||||
color: RGB($primary)
|
||||
|
||||
transition: background-color 0.2s ease-in-out, padding 0.2s ease-in-out
|
||||
|
||||
> span
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
bottom: 0
|
||||
width: 0.25rem
|
||||
|
||||
display: flex
|
||||
justify-content: center
|
||||
align-items: center
|
||||
|
||||
background-color: RGB($primary)
|
||||
color: transparent
|
||||
|
||||
overflow: hidden
|
||||
transition: width 0.2s ease-in-out, color 0.2s ease-in-out, background-color 0.2s ease-in-out
|
||||
|
||||
> i
|
||||
font-size: 1.25em
|
||||
|
||||
&:hover
|
||||
padding: 0.75rem 1rem 0.75rem 4rem
|
||||
background-color: RGBA($primary, 0.1)
|
||||
cursor: pointer
|
||||
|
||||
> span
|
||||
width: 3rem
|
||||
color: RGB($black)
|
||||
|
||||
&.success
|
||||
color: RGB($primary)
|
||||
|
||||
> span
|
||||
background-color: RGB($primary)
|
||||
|
||||
&:hover
|
||||
background-color: RGBA($primary, 0.1)
|
||||
|
||||
&.error
|
||||
color: RGB($secondary)
|
||||
|
||||
> span
|
||||
background-color: RGB($secondary)
|
||||
|
||||
&:hover
|
||||
background-color: RGBA($secondary, 0.1)
|
||||
|
||||
main
|
||||
padding: 1rem
|
||||
height: 100%
|
||||
|
||||
display: flex
|
||||
flex-direction: column
|
||||
|
||||
> h2
|
||||
margin: 0 0 1rem 0
|
||||
font-size: 1.5em
|
||||
color: RGB($white)
|
||||
|
||||
.center-text
|
||||
height: 100%
|
||||
|
||||
display: flex
|
||||
flex-direction: column
|
||||
justify-content: center
|
||||
align-items: center
|
||||
|
||||
> h2
|
||||
margin: 0
|
||||
text-align: center
|
||||
font-size: 2em
|
||||
color: RGB($white)
|
||||
|
||||
> p
|
||||
margin: 0
|
||||
text-align: center
|
||||
font-size: 1em
|
||||
color: RGB($white)
|
||||
|
||||
> img
|
||||
margin: 1rem auto 0
|
||||
max-width: 100%
|
||||
max-height: 15rem
|
||||
border-radius: 2px
|
||||
|
||||
.block
|
||||
margin-bottom: 1rem
|
||||
padding: 1rem
|
||||
|
||||
display: flex
|
||||
flex-direction: column
|
||||
|
||||
background-color: rgba($black, 0.7)
|
||||
border-radius: 2px
|
||||
|
||||
> h2
|
||||
margin: 0 0 0.2rem 0
|
||||
font-size: 1.3em
|
||||
color: RGB($white)
|
||||
|
||||
> p
|
||||
margin: 0 0 1rem 0
|
||||
font-size: 1em
|
||||
|
||||
> table
|
||||
width: 100%
|
||||
border-collapse: collapse
|
||||
> tbody > tr > td
|
||||
padding: 0 0.5rem 0.5rem 0
|
||||
|
||||
text-align: left
|
||||
font-size: 0.9em
|
||||
|
||||
color: RGB($white)
|
||||
|
||||
transition: filter 0.2s ease-in-out
|
||||
|
||||
&:last-child
|
||||
width: 100%
|
||||
|
||||
&.hidden
|
||||
filter: blur(5px)
|
||||
|
||||
&.secondary
|
||||
border: 1px solid RGB($secondary)
|
||||
|
||||
> h2
|
||||
color: RGB($secondary)
|
||||
|
||||
form
|
||||
display: flex
|
||||
flex-direction: column
|
||||
|
||||
> input
|
||||
margin: 0 0 1rem 0
|
||||
padding: 0.7rem 1rem
|
||||
|
||||
border: 1px solid RGB($white)
|
||||
border-radius: 2px
|
||||
|
||||
background-color: RGB($black)
|
||||
color: RGB($white)
|
||||
|
||||
&:focus
|
||||
outline: none
|
||||
border-color: RGB($primary)
|
||||
|
||||
&.error
|
||||
border-color: RGB($secondary)
|
||||
|
||||
> button
|
||||
margin: 0
|
||||
padding: 0.75rem 1rem
|
||||
|
||||
font-weight: bolder
|
||||
|
||||
border: transparent
|
||||
border-radius: 2px
|
||||
|
||||
background-color: RGB($primary)
|
||||
color: RGB($black)
|
||||
|
||||
&:focus-visible, &:hover
|
||||
outline: none
|
||||
background-color: RGBA($primary, 0.3)
|
||||
color: RGB($primary)
|
||||
|
||||
&.disabled
|
||||
pointer-events: none
|
||||
opacity: 0.5
|
||||
|
||||
&.secondary
|
||||
background-color: RGB($secondary)
|
||||
color: RGB($black)
|
||||
|
||||
&:focus-visible, &:hover
|
||||
background-color: RGBA($secondary, 0.3)
|
||||
color: RGB($secondary)
|
||||
|
||||
footer
|
||||
padding: 0.5rem 1rem
|
||||
width: 100%
|
||||
display: flex
|
||||
flex-direction: row
|
||||
background-image: linear-gradient(to top, RGB(var(--black)), transparent)
|
||||
|
||||
> p
|
||||
margin: 0
|
||||
width: 100%
|
||||
text-align: center
|
||||
font-size: 0.8em
|
||||
white-space: nowrap
|
||||
color: RGB($white)
|
||||
|
||||
> a
|
||||
color: RGB($secondary)
|
||||
text-decoration: none
|
||||
|
||||
&:hover
|
||||
text-decoration: underline
|
BIN
TFR/server/static/title.png
Normal file
After Width: | Height: | Size: 152 KiB |
BIN
TFR/server/static/title.webp
Normal file
After Width: | Height: | Size: 44 KiB |