Add full-calc
This commit is contained in:
17
main.go
17
main.go
@@ -28,6 +28,7 @@ func init() {
|
||||
"./templates/modals/delete_player.html",
|
||||
"./templates/modals/edit_player.html",
|
||||
"./templates/modals/settings.html",
|
||||
"./templates/modals/full_calc.html",
|
||||
"./templates/components/header.html",
|
||||
)
|
||||
if err != nil {
|
||||
@@ -51,7 +52,7 @@ func init() {
|
||||
}
|
||||
|
||||
utils.PlayerItemTemplate, err = template.ParseFiles(
|
||||
"./templates/player_list_item.html",
|
||||
"./templates/shards/player_list_item.html",
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -88,27 +89,29 @@ func main() {
|
||||
log.Println("Created first activation code with ADMIN role:\n" + firstCode)
|
||||
}
|
||||
|
||||
router.Static("/static", "./static")
|
||||
router.Static("/node_modules", "./node_modules")
|
||||
|
||||
f, _ := os.OpenFile("isc_rest.log", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0660)
|
||||
utils.GinWriter = io.MultiWriter(f, os.Stdout)
|
||||
logger := gin.LoggerWithWriter(utils.GinWriter, "/static/", "/node_modules/", "/favicon.ico")
|
||||
router.Use(
|
||||
gin.LoggerWithWriter(utils.GinWriter, "/static"),
|
||||
logger,
|
||||
gin.Recovery(),
|
||||
)
|
||||
reader.Use(
|
||||
gin.LoggerWithWriter(utils.GinWriter),
|
||||
logger,
|
||||
gin.Recovery(),
|
||||
)
|
||||
author.Use(
|
||||
gin.LoggerWithWriter(utils.GinWriter),
|
||||
logger,
|
||||
gin.Recovery(),
|
||||
)
|
||||
admin.Use(
|
||||
gin.LoggerWithWriter(utils.GinWriter),
|
||||
logger,
|
||||
gin.Recovery(),
|
||||
)
|
||||
|
||||
router.Static("/static", "./static")
|
||||
|
||||
router.GET("/login", loginPage)
|
||||
router.POST("/login", loginPost)
|
||||
router.GET("/logout", logout)
|
||||
|
||||
6
pages.go
6
pages.go
@@ -10,8 +10,14 @@ import (
|
||||
)
|
||||
|
||||
func mainPage(c *gin.Context) {
|
||||
username, ok := session.GetUsername(c)
|
||||
if !ok {
|
||||
username = ""
|
||||
}
|
||||
|
||||
data := map[string]interface{}{
|
||||
"UserRole": controllers.GetUserRoleByCtx(c),
|
||||
"Username": username,
|
||||
}
|
||||
|
||||
err := utils.MainPageTemplates.Execute(c.Writer, data)
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<div class="row justify-content-between border-top pt-4 position-relative mb-5">
|
||||
<div class="col-auto position-absolute start-0">
|
||||
<div class="btn-toolbar" role="toolbar">
|
||||
<a class="btn btn-lg btn-outline-secondary text-secondary-emphasis me-2" href="/logout" data-bs-toggle="tooltip" data-bs-title="Abmelden">
|
||||
<i class="bi bi-door-closed"></i>
|
||||
<a class="btn btn-lg btn-outline-secondary text-secondary-emphasis me-2" href="/logout">
|
||||
<i class="bi bi-door-closed" data-bs-toggle="tooltip" data-bs-title="Abmelden"></i>
|
||||
</a>
|
||||
<button class="btn btn-lg btn-outline-secondary text-secondary-emphasis me-2" data-bs-toggle="modal" data-bs-target="#settingsModal">
|
||||
<i class="bi bi-gear-fill"></i>
|
||||
<i class="bi bi-gear-fill" data-bs-toggle="tooltip" data-bs-title="Einstellungen"></i>
|
||||
</button>
|
||||
{{ if (eq .UserRole "ADMIN") }}
|
||||
<div class="dropdown">
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto position-absolute start-50 translate-middle-x">
|
||||
<button class="btn btn-lg btn-outline-primary" type="button" disabled>
|
||||
<button class="btn btn-lg btn-primary" id="fullCalcBtn" data-bs-toggle="modal" data-bs-target="#fullCalcModal">
|
||||
<i class="bi bi-calculator-fill me-2"></i>
|
||||
Berechnen
|
||||
</button>
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
|
||||
{{ template "settings" . }}
|
||||
|
||||
{{ template "full_calc" . }}
|
||||
|
||||
<script lang="javascript">
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
loadClans();
|
||||
|
||||
324
templates/modals/full_calc.html
Normal file
324
templates/modals/full_calc.html
Normal file
@@ -0,0 +1,324 @@
|
||||
{{ define "full_calc" }}
|
||||
|
||||
<div class="modal modal-lg fade" data-bs-backdrop="static" data-bs-keyboard="false" id="fullCalcModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h1 class="modal-title fs-3 text-secondary-emphasis fw-bold" id="fullCalcModalLabel">Infantryskill-Ergebnis</h1>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body px-0">
|
||||
<div class="row px-2">
|
||||
<div class="col-6 d-flex flex-column border-end border-secondary px-1 pb-2" id="home-clan-results">
|
||||
<h3 class="pb-3 text-center text-primary border-bottom border-primary-subtle fw-bold" id="home-clan-name">a</h3>
|
||||
<!-- Dynamically added -->
|
||||
</div>
|
||||
<div class="col-6 d-flex flex-column px-1 mb-2" id="opponent-clan-results">
|
||||
<h3 class="pb-3 text-center text-danger border-bottom border-danger-subtle fw-bold" id="opp-clan-name">b</h3>
|
||||
<!-- Dynamically added -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-light-emphasis">
|
||||
<hr class="mt-0">
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-3 text-center fs-5 me-4 ps-0">
|
||||
Avg. Score: <span id="home-avg-score"><i class="spinner-grow spinner-grow-sm text-secondary align-baseline mx-2" role="status"></i></span>
|
||||
</div>
|
||||
<div class="col-2 text-center fs-5">
|
||||
Diff: <span id="diff-score"><i class="spinner-grow spinner-grow-sm text-secondary align-baseline mx-2" role="status"></i></span>
|
||||
</div>
|
||||
<div class="col-3 text-center fs-5 ms-4 pe-0">
|
||||
Avg. Score: <span id="opp-avg-score"><i class="spinner-grow spinner-grow-sm text-secondary align-baseline mx-2" role="status"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer justify-content-between">
|
||||
<div class="text-center text-secondary ms-3" id="fullCalcInfo" hidden></div>
|
||||
<div class="progress w-100" style="height: 30px;" id="fullCalcProgressbar" role="progressbar">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" style="width: 0">Lade...</div>
|
||||
</div>
|
||||
<button type="submit" name="submit" class="btn btn-lg btn-outline-primary" onclick="downloadScreenshot()" hidden>
|
||||
<i class="bi bi-download"></i>
|
||||
</button>
|
||||
<!--<button type="button" class="btn btn-lg btn-secondary" data-bs-dismiss="modal">Schließen</button>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-group list-group-horizontal fs-5 px-2 visually-hidden" id="result-item">
|
||||
<li class="list-group-item border-0 rounded-0 border-bottom border-secondary-subtle w-75 text-light">
|
||||
<i class="bi bi-question-square text-secondary me-2"></i>
|
||||
<span id="username"></span>
|
||||
</li>
|
||||
<li class="list-group-item border-0 rounded-0 border-bottom border-secondary-subtle text-end w-25 ps-1 pe-3 text-warning">
|
||||
<i class="bi bi-trophy text-warning me-2" hidden></i>
|
||||
<span id="score"></span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<script lang="javascript">
|
||||
function getSelectedPlayers(listId) {
|
||||
const list = document.getElementById(listId);
|
||||
const selectedPlayerNodes = list.querySelectorAll('input[type="checkbox"]:checked');
|
||||
const selectedPlayers = [];
|
||||
selectedPlayerNodes.forEach(p => {
|
||||
p = p.parentElement.parentElement.parentElement.querySelector('span');
|
||||
selectedPlayers.push([p.parentElement.getAttribute("data-id"), p.innerText]);
|
||||
});
|
||||
return selectedPlayers;
|
||||
}
|
||||
|
||||
function getAndInsertScore(playerID, resultList) {
|
||||
return fetch('/score/' + playerID)
|
||||
.then(response => response.text())
|
||||
.then(text => {
|
||||
const score = resultList.querySelector('ul[data-id="' + playerID + '"] #score');
|
||||
score.innerHTML = text;
|
||||
if (!isNaN(parseFloat(text)))
|
||||
score.previousElementSibling.hidden = false;
|
||||
return text;
|
||||
});
|
||||
}
|
||||
|
||||
function downloadScreenshot() {
|
||||
const submitBtn = document.querySelector('#fullCalcModal button[name="submit"]');
|
||||
submitBtn.hidden = true;
|
||||
html2canvas(document.querySelector('#fullCalcModal .modal-content'), {
|
||||
backgroundColor: null,
|
||||
scale: 1
|
||||
}).then(canvas => {
|
||||
let link = document.createElement('a');
|
||||
let date = new Date().toLocaleString('de-DE', { dateStyle: 'short', timeStyle: 'short' }).replace(', ', '_').replace(':', '-');
|
||||
link.download = 'InfantrySkill_' + date + '.png';
|
||||
link.href = canvas.toDataURL();
|
||||
link.click();
|
||||
submitBtn.hidden = false;
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const fullCalcModal = document.getElementById('fullCalcModal');
|
||||
const downloadBtn = fullCalcModal.querySelector('button[name="submit"]');
|
||||
const progressbar = document.getElementById('fullCalcProgressbar');
|
||||
const resultItem = document.getElementById('result-item');
|
||||
const homeClanResults = document.getElementById('home-clan-results');
|
||||
const oppClanResults = document.getElementById('opponent-clan-results');
|
||||
const homeClanList = document.getElementById('home-clan');
|
||||
const oppClanList = document.getElementById('opponent-clan');
|
||||
const spinnerBorder = '<i class="spinner-border spinner-border-sm text-warning align-baseline me-2" style="margin-left: 0.91rem;" role="status"></i>';
|
||||
const spinnerGrow = '<i class="spinner-grow spinner-grow-sm text-secondary align-baseline mx-2" role="status"></i>';
|
||||
const dash = '<i class="bi bi-dash mx-2"></i>';
|
||||
|
||||
const homeAvgScore = document.getElementById('home-avg-score');
|
||||
const diffScore = document.getElementById('diff-score');
|
||||
const oppAvgScore = document.getElementById('opp-avg-score');
|
||||
const fullCalcInfo = document.getElementById('fullCalcInfo');
|
||||
|
||||
let calcMutex = false;
|
||||
let sortMutex = false;
|
||||
|
||||
function addResultItem(resultList, playerID, playerName) {
|
||||
let item = resultList.appendChild(resultItem.cloneNode(true));
|
||||
item.querySelector('#username').innerText = playerName;
|
||||
item.querySelector('#score').innerHTML = spinnerBorder;
|
||||
item.setAttribute('data-id', playerID);
|
||||
item.classList.remove('visually-hidden');
|
||||
}
|
||||
|
||||
function calcStatistics() {
|
||||
if (calcMutex) {
|
||||
setTimeout(calcStatistics, 100);
|
||||
return;
|
||||
}
|
||||
calcMutex = true;
|
||||
|
||||
let homeScores = [];
|
||||
let oppScores = [];
|
||||
homeClanResults.querySelectorAll('ul').forEach(e => {
|
||||
let score = parseFloat(e.querySelector('#score').innerText);
|
||||
if (!isNaN(score)) {
|
||||
homeScores.push(score);
|
||||
}
|
||||
});
|
||||
oppClanResults.querySelectorAll('ul').forEach(e => {
|
||||
let score = parseFloat(e.querySelector('#score').innerText);
|
||||
if (!isNaN(score)) {
|
||||
oppScores.push(score);
|
||||
}
|
||||
});
|
||||
|
||||
homeScores.sort((a, b) => a - b);
|
||||
oppScores.sort((a, b) => a - b);
|
||||
let homeAvg = homeScores.reduce((a, b) => a + b, 0) / homeScores.length;
|
||||
let oppAvg = oppScores.reduce((a, b) => a + b, 0) / oppScores.length;
|
||||
|
||||
if (!isNaN(homeAvg))
|
||||
homeAvgScore.innerText = homeAvg.toFixed(2);
|
||||
if (!isNaN(oppAvg))
|
||||
oppAvgScore.innerText = oppAvg.toFixed(2);
|
||||
if (!isNaN(homeAvg) && !isNaN(oppAvg))
|
||||
diffScore.innerText = (homeAvg - oppAvg).toFixed(2);
|
||||
|
||||
calcMutex = false;
|
||||
}
|
||||
|
||||
function sortPlayers() {
|
||||
if (sortMutex) {
|
||||
setTimeout(sortPlayers, 100);
|
||||
return;
|
||||
}
|
||||
sortMutex = true;
|
||||
|
||||
let homePlayers = Array.from(homeClanResults.querySelectorAll('ul'));
|
||||
let oppPlayers = Array.from(oppClanResults.querySelectorAll('ul'));
|
||||
homePlayers.sort((a, b) => {
|
||||
let aScore = parseFloat(a.querySelector('#score').innerText);
|
||||
let bScore = parseFloat(b.querySelector('#score').innerText);
|
||||
if (isNaN(aScore)) {
|
||||
return 1;
|
||||
} else if (isNaN(bScore)) {
|
||||
return -1;
|
||||
} else {
|
||||
return bScore - aScore;
|
||||
}
|
||||
});
|
||||
oppPlayers.sort((a, b) => {
|
||||
let aScore = parseFloat(a.querySelector('#score').innerText);
|
||||
let bScore = parseFloat(b.querySelector('#score').innerText);
|
||||
if (isNaN(aScore)) {
|
||||
return 1;
|
||||
} else if (isNaN(bScore)) {
|
||||
return -1;
|
||||
} else {
|
||||
return bScore - aScore;
|
||||
}
|
||||
});
|
||||
homePlayers.forEach(p => homeClanResults.appendChild(p));
|
||||
oppPlayers.forEach(p => oppClanResults.appendChild(p));
|
||||
|
||||
sortMutex = false;
|
||||
}
|
||||
|
||||
function setSquadNumbers() {
|
||||
let homePlayers = Array.from(homeClanResults.querySelectorAll('ul'));
|
||||
let oppPlayers = Array.from(oppClanResults.querySelectorAll('ul'));
|
||||
let playerCounter = 0;
|
||||
|
||||
function setSquadNumber(player, squadNum) {
|
||||
player.querySelector('#username').previousElementSibling.classList.replace('bi-question-square', 'bi-' + squadNum + '-square');
|
||||
}
|
||||
|
||||
homePlayers.forEach((p, _) => {
|
||||
if (p.querySelector('#score').innerText === '' && p.querySelector('#score').innerHTML !== spinnerBorder) {
|
||||
setSquadNumber(p, 'x');
|
||||
} else {
|
||||
let squadNum = Math.floor(playerCounter / 4) + 1;
|
||||
setSquadNumber(p, squadNum);
|
||||
playerCounter++;
|
||||
}
|
||||
});
|
||||
playerCounter = 0;
|
||||
oppPlayers.forEach((p, _) => {
|
||||
if (p.querySelector('#score').innerText === '' && p.querySelector('#score').innerHTML !== spinnerBorder) {
|
||||
setSquadNumber(p, 'x');
|
||||
} else {
|
||||
let squadNum = Math.floor(playerCounter / 4) + 1;
|
||||
setSquadNumber(p, squadNum);
|
||||
playerCounter++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function cleanupStatistic(statisticField) {
|
||||
if (statisticField.innerHTML === spinnerGrow)
|
||||
statisticField.innerHTML = dash;
|
||||
}
|
||||
|
||||
function updateScoreInClanList(clanListId, playerID, score) {
|
||||
let clanPlayers = document.getElementById(clanListId).querySelectorAll('div[data-id]');
|
||||
clanPlayers.forEach(p => {
|
||||
if (p.getAttribute('data-id') === playerID) {
|
||||
p.querySelector('#quickScore').innerHTML = score;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getClanName(clanList) {
|
||||
let clanName = clanList.options[clanList.selectedIndex].innerText;
|
||||
return clanList.selectedIndex <= 0 ? dash : clanName.replace(/\[.*]/, '').trim();
|
||||
}
|
||||
|
||||
function updateProgress(progress) {
|
||||
progressbar.firstElementChild.style.width = progress + '%';
|
||||
}
|
||||
|
||||
if (fullCalcModal) {
|
||||
fullCalcModal.addEventListener('show.bs.modal', _ => {
|
||||
let homePlayers = getSelectedPlayers('home-player-list');
|
||||
let oppPlayers = getSelectedPlayers('opponent-player-list');
|
||||
let promises = [];
|
||||
let doneCounter = 0;
|
||||
|
||||
homeClanResults.querySelector('#home-clan-name').innerHTML = getClanName(homeClanList);
|
||||
oppClanResults.querySelector('#opp-clan-name').innerHTML = getClanName(oppClanList);
|
||||
|
||||
homePlayers.forEach(p => {
|
||||
addResultItem(homeClanResults, p[0], p[1]);
|
||||
promises.push(
|
||||
getAndInsertScore(p[0], homeClanResults)
|
||||
.then(score => {
|
||||
calcStatistics();
|
||||
sortPlayers();
|
||||
updateScoreInClanList('home-player-list', p[0], score);
|
||||
updateProgress(100 * (++doneCounter / (homePlayers.length + oppPlayers.length)));
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
oppPlayers.forEach(p => {
|
||||
addResultItem(oppClanResults, p[0], p[1]);
|
||||
promises.push(
|
||||
getAndInsertScore(p[0], oppClanResults)
|
||||
.then(score => {
|
||||
calcStatistics();
|
||||
sortPlayers();
|
||||
updateScoreInClanList('opponent-player-list', p[0], score);
|
||||
updateProgress(100 * (++doneCounter / (homePlayers.length + oppPlayers.length)));
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
Promise.all(promises).then(_ => {
|
||||
setSquadNumbers();
|
||||
|
||||
cleanupStatistic(homeAvgScore);
|
||||
cleanupStatistic(diffScore);
|
||||
cleanupStatistic(oppAvgScore);
|
||||
|
||||
progressbar.hidden = true;
|
||||
downloadBtn.hidden = false;
|
||||
fullCalcInfo.hidden = false;
|
||||
fullCalcInfo.innerHTML = 'Erstellt von <b>{{ .Username }}</b> am ' +
|
||||
new Date().toLocaleString('de-DE', { dateStyle: 'short', timeStyle: 'short' }) + ' Uhr';
|
||||
});
|
||||
});
|
||||
|
||||
fullCalcModal.addEventListener('hidden.bs.modal', _ => {
|
||||
homeClanResults.querySelectorAll('ul').forEach(e => e.remove());
|
||||
oppClanResults.querySelectorAll('ul').forEach(e => e.remove());
|
||||
homeAvgScore.innerHTML = spinnerGrow;
|
||||
diffScore.innerHTML = spinnerGrow;
|
||||
oppAvgScore.innerHTML = spinnerGrow;
|
||||
downloadBtn.hidden = true;
|
||||
fullCalcInfo.hidden = true;
|
||||
progressbar.hidden = false;
|
||||
progressbar.firstElementChild.style.width = '0';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
{{ end }}
|
||||
Reference in New Issue
Block a user