idac: added "simple" ranking to frontend

This commit is contained in:
Dniel97
2023-11-21 22:51:10 +01:00
parent d1a7b898a7
commit 6ea8cca1a2
10 changed files with 641 additions and 160 deletions

File diff suppressed because one or more lines are too long

View File

@@ -2,130 +2,20 @@
{% block content %}
<h1 class="mb-3">頭文字D THE ARCADE</h1>
{% if sesh is defined and sesh["userId"] > 0 %}
<div class="card mb-3">
<div class="card-body">
<div class="card-title">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center">
<h3>{{ sesh["username"] }}'s Profile</h3>
<div class="btn-toolbar mb-2 mb-md-0">
<div class="btn-group me-2">
<!--<button type="button" class="btn btn-sm btn-outline-secondary">Share</button>-->
<button type="button" data-bs-toggle="modal" data-bs-target="#export"
class="btn btn-sm btn-outline-primary">Export</button>
</div>
</div>
</div>
</div>
<!--<h4 class="card-subtitle mb-2 text-body-secondary">Card subtitle</h4>-->
{% if profile is defined and profile is not none %}
<div class="row d-flex justify-content-center h-100">
<div class="col col-lg-3 col-12">
<div class="card mb-3">
<div class="card-body p-4">
<h5>Information</h5>
<hr class="mt-0 mb-4">
<h6>Username</h6>
<p class="text-muted">{{ profile.username }}</p>
<h6>Cash</h6>
<p class="text-muted">{{ profile.cash }} D</p>
<h6>Grade</h6>
<h4>
{% set grade = rank.grade %}
{% if grade >= 1 and grade <= 72 %}
{% set grade_number = (grade - 1) // 9 %}
{% set grade_letters = ['E', 'D', 'C', 'B', 'A', 'S', 'SS', 'X'] %}
{{ grade_letters[grade_number] }}{{ 9 - ((grade-1) % 9) }}
{% else %}
Unknown
{% endif %}
</h4>
</div>
</div>
</div>
<div class="col col-lg-9 col-12">
<div class="card mb-3">
<nav class="mb-3">
<ul class="nav nav-tabs">
<li class="nav-item">
<a class="nav-link {% if active_tab == 'ranking' %}active{% endif %}" aria-current="page" href="ranking">Ranking</a>
</li>
<li class="nav-item">
<a class="nav-link {% if active_tab == 'profile' %}active{% endif %}" href="profile">Profile</a>
</li>
</ul>
</nav>
<div class="card-body p-4">
<h5>Statistics</h5>
<hr class="mt-0 mb-4">
<div class="row pt-1">
<div class="col-lg-4 col-md-6 mb-3">
<h6>Total Plays</h6>
<p class="text-muted">{{ profile.total_play }}</p>
</div>
<div class="col-lg-4 col-md-6 mb-3">
<h6>Last Played</h6>
<p class="text-muted">{{ profile.last_play_date }}</p>
</div>
<div class="col-lg-4 col-md-6 mb-3">
<h6>Mileage</h6>
<p class="text-muted">{{ profile.mileage / 1000}} km</p>
</div>
</div>
{% if tickets is defined and tickets|length > 0 %}
<h5>Tokens/Tickets</h5>
<hr class="mt-0 mb-4">
<div class="row pt-1">
<div class="col-lg-3 col-md-6 mb-3">
<h6>Avatar Tokens</h6>
<p class="text-muted">{{ tickets.avatar_points }}/30</p>
</div>
<div class="col-lg-3 col-md-6 mb-3">
<h6>Car Dressup Tokens</h6>
<p class="text-muted">{{ tickets.car_dressup_points }}/30</p>
</div>
<div class="col-lg-3 col-md-6 mb-3">
<h6>FullTune Tickets</h6>
<p class="text-muted">{{ tickets.full_tune_tickets }}/99</p>
</div>
<div class="col-lg-3 col-md-6 mb-3">
<h6>FullTune Fragments</h6>
<p class="text-muted">{{ tickets.full_tune_fragments }}/10</p>
</div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% else %}
<div class="alert alert-warning" role="alert">
You need to play 頭文字D THE ARCADE first to view your profile.
</div>
{% endif %}
<!--<a href="#" data-bs-toggle="modal" data-bs-target="#card-add" class="card-link">Add Card</a>-->
</div>
</div>
{% else %}
<div class="alert alert-info" role="alert">
You need to be logged in to view this page. <a href="/gate">Login</a></a>
</div>
{% endif %}
{% block tab %}
<div class="modal fade" id="export" tabindex="-1" aria-labelledby="export-label" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exort-label">Export Profile</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Download your profile as a <strong>.json</strong> file in order to import it into your local ARTEMiS
database.
<div class="alert alert-warning mt-3" role="alert">
{% if profile is defined and profile is not none %}
Are you sure you want to export your profile with the username {{ profile.username }}?
{% endif %}
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="exportBtn">Download Profile</a>
</div>
</div>
</div>
</div>
{% endblock tab %}
<script type="text/javascript">
{% include "titles/idac/frontend/js/idac_scripts.js" %}

View File

@@ -1,10 +1,59 @@
$(document).ready(function () {
$('#exportBtn').click(function () {
window.location = "/game/idac/export";
// Declare a global variable to store the JSON data
var constData;
// appendAlert('Successfully exported the profile', 'success');
function formatGoalTime(milliseconds) {
// Convert the milliseconds to a time string
var minutes = Math.floor(milliseconds / 60000);
var seconds = Math.floor((milliseconds % 60000) / 1000);
milliseconds %= 1000;
// Close the modal on success
$('#export').modal('hide');
return `${parseInt(minutes)}'${seconds.toString().padStart(2, '0')}"${milliseconds.toString().padStart(3, '0')}`;
}
// Function to get style_name for a given style_car_id
function getCarName(style_car_id) {
// Find the car with the matching style_car_id
var foundCar = constData.car.find(function (style) {
return style.style_car_id === style_car_id;
});
});
// Return the style_name if found, otherwise return Unknown
return foundCar ? foundCar.style_name : "Unknown car";
}
$(document).ready(function () {
// Make an AJAX request to load the JSON file
$.ajax({
url: "/game/idac/ranking/const.get",
type: "GET",
dataType: "json",
success: function (data) {
// Check if the 'course' array exists in the JSON data
if (data && data.course) {
// Assign the JSON data to the global variable
constData = data;
// Get the select element
var selectElement = $("#course-select");
// Remove the Loading text
selectElement.empty();
// Loop through the 'course' array and add options to the select
$.each(constData.course, function (index, course) {
var option = '<option value="' + course.course_id + '"' + (index === 0 ? 'selected' : '') + '>' + course.course_name + '</option>';
selectElement.append(option);
});
// Simulate a change event on page load with the default value (0)
$("#course-select").val("0").change();
}
},
error: function (jqXHR, textStatus, errorThrown) {
// Print the error message as an option element
$("#course-select").html("<option value='0' selected disabled>" + textStatus + "</option>");
console.error("Error loading JSON file:", textStatus, errorThrown);
}
});
});

View File

@@ -0,0 +1,129 @@
{% extends "titles/idac/frontend/idac_index.jinja" %}
{% block tab %}
{% if sesh is defined and sesh["userId"] > 0 %}
<div class="card mb-3">
<div class="card-body">
<div class="card-title">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center">
<h3>{{ sesh["username"] }}'s Profile</h3>
<div class="btn-toolbar mb-2 mb-md-0">
<div class="btn-group me-2">
<!--<button type="button" class="btn btn-sm btn-outline-secondary">Share</button>-->
<button type="button" data-bs-toggle="modal" data-bs-target="#export"
class="btn btn-sm btn-outline-primary">Export</button>
</div>
</div>
</div>
</div>
<!--<h4 class="card-subtitle mb-2 text-body-secondary">Card subtitle</h4>-->
{% if profile is defined and profile is not none %}
<div class="row d-flex justify-content-center h-100">
<div class="col col-lg-3 col-12">
<div class="card mb-3">
<div class="card-body p-4">
<h5>Information</h5>
<hr class="mt-0 mb-4">
<h6>Username</h6>
<p class="text-muted">{{ profile.username }}</p>
<h6>Cash</h6>
<p class="text-muted">{{ profile.cash }} D</p>
<h6>Grade</h6>
<h4>
{% set grade = rank.grade %}
{% if grade >= 1 and grade <= 72 %} {% set grade_number=(grade - 1) // 9 %} {% set
grade_letters=['E', 'D' , 'C' , 'B' , 'A' , 'S' , 'SS' , 'X' ] %} {{
grade_letters[grade_number] }}{{ 9 - ((grade-1) % 9) }} {% else %} Unknown {% endif %}
</h4>
</div>
</div>
</div>
<div class="col col-lg-9 col-12">
<div class="card mb-3">
<div class="card-body p-4">
<h5>Statistics</h5>
<hr class="mt-0 mb-4">
<div class="row pt-1">
<div class="col-lg-4 col-md-6 mb-3">
<h6>Total Plays</h6>
<p class="text-muted">{{ profile.total_play }}</p>
</div>
<div class="col-lg-4 col-md-6 mb-3">
<h6>Last Played</h6>
<p class="text-muted">{{ profile.last_play_date }}</p>
</div>
<div class="col-lg-4 col-md-6 mb-3">
<h6>Mileage</h6>
<p class="text-muted">{{ profile.mileage / 1000}} km</p>
</div>
</div>
{% if tickets is defined and tickets|length > 0 %}
<h5>Tokens/Tickets</h5>
<hr class="mt-0 mb-4">
<div class="row pt-1">
<div class="col-lg-3 col-md-6 mb-3">
<h6>Avatar Tokens</h6>
<p class="text-muted">{{ tickets.avatar_points }}/30</p>
</div>
<div class="col-lg-3 col-md-6 mb-3">
<h6>Car Dressup Tokens</h6>
<p class="text-muted">{{ tickets.car_dressup_points }}/30</p>
</div>
<div class="col-lg-3 col-md-6 mb-3">
<h6>FullTune Tickets</h6>
<p class="text-muted">{{ tickets.full_tune_tickets }}/99</p>
</div>
<div class="col-lg-3 col-md-6 mb-3">
<h6>FullTune Fragments</h6>
<p class="text-muted">{{ tickets.full_tune_fragments }}/10</p>
</div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% else %}
<div class="alert alert-warning" role="alert">
You need to play 頭文字D THE ARCADE first to view your profile.
</div>
{% endif %}
<!--<a href="#" data-bs-toggle="modal" data-bs-target="#card-add" class="card-link">Add Card</a>-->
</div>
</div>
{% else %}
<div class="alert alert-info" role="alert">
You need to be logged in to view this page. <a href="/gate">Login</a></a>
</div>
{% endif %}
<div class="modal fade" id="export" tabindex="-1" aria-labelledby="export-label" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exort-label">Export Profile</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Download your profile as a <strong>.json</strong> file in order to import it into your local ARTEMiS
database.
<div class="alert alert-warning mt-3" role="alert">
{% if profile is defined and profile is not none %}
Are you sure you want to export your profile with the username {{ profile.username }}?
{% endif %}
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="exportBtn">Download Profile</a>
</div>
</div>
</div>
</div>
<script type="text/javascript">
{% include "titles/idac/frontend/profile/js/scripts.js" %}
</script>
{% endblock tab %}

View File

@@ -0,0 +1,10 @@
$(document).ready(function () {
$('#exportBtn').click(function () {
window.location = "/game/idac/profile/export.get";
// appendAlert('Successfully exported the profile', 'success');
// Close the modal on success
$('#export').modal('hide');
});
});

View File

@@ -0,0 +1,30 @@
{% extends "titles/idac/frontend/idac_index.jinja" %}
{% block tab %}
<div class="tab-content" id="nav-tabContent">
<!-- Ranking -->
<div class="tab-pane fade show active" id="nav-ranking" role="tabpanel" aria-labelledby="nav-ranking-tab"
tabindex="0">
<div class="row justify-content-md-center form-signin">
<div class="col col-lg-4">
<select class="form-select mb-3" id="course-select">
<option value="0" selected disabled>Loading Courses...</option>
</select>
</div>
</div>
<div class="card">
<div class="card-body">
<div id="table-ranking">
<div class="text-center">Loading Ranking...</div>
</div>
</div>
</div>
<div id="pagination-ranking"></div>
</div>
</div>
<script type="text/javascript">
{% include "titles/idac/frontend/ranking/js/scripts.js" %}
</script>
{% endblock tab %}

View File

@@ -0,0 +1,95 @@
// Function to load data based on the selected value
function loadRanking(courseId, pageNumber = 1) {
// Make a GET request to the server
$.ajax({
url: "/game/idac/ranking/ranking.get",
type: "GET",
data: { courseId: courseId, pageNumber: pageNumber },
dataType: "json",
success: function (data) {
// check if an error inside the json exists
if (!data.success) {
// Inject the table into the container
$("#table-ranking").html("<div class='text-center'>" + data.error + "</div>");
console.error("Error: " + data.error);
return;
}
// get the total number of pages
var total_pages = data.total_pages;
// Generate the HTML table
var tableHtml = '<div class="table-responsive"><table class="table table-hover"><thead><tr><th scope="col">#</th><th scope="col">Name</th><th scope="col">Car</th><th scope="col">Time</th><th scope="col" class="d-none d-lg-table-cell">Store</th><th scope="col" class="d-none d-lg-table-cell">Date</th></tr></thead><tbody>';
$.each(data.ranking, function (i, ranking) {
tableHtml += '<tr class="align-middle">';
tableHtml += '<td>' + ranking.rank + '</td>';
tableHtml += '<td>' + ranking.name + '</td>';
tableHtml += '<td>' + getCarName(ranking.style_car_id) + '</td>';
tableHtml += '<td>' + formatGoalTime(ranking.record) + '</td>';
// Ignore the Store and Date columns on small screens
tableHtml += '<td class="d-none d-lg-table-cell">' + ranking.store + '</td>';
tableHtml += '<td class="d-none d-lg-table-cell">' + ranking.update_date + '</td>';
tableHtml += '</tr>';
});
tableHtml += '</tbody></table></div>';
// Inject the table into the container
$("#table-ranking").html(tableHtml);
// Generate the Pagination HTML
var paginationHtml = '<nav class="mt-3"><ul class="pagination justify-content-center">';
// Deactivate the previous button if the current page is the first page
paginationHtml += '<li class="page-item ' + (pageNumber === 1 ? 'disabled' : '') + '">';
paginationHtml += '<a class="page-link" href="#" data-page="' + (pageNumber - 1) + '">Previous</a>';
paginationHtml += '</li>';
for (var i = 1; i <= total_pages; i++) {
// Set the active class to the current page
paginationHtml += '<li class="page-item ' + (pageNumber === i ? 'active disabled' : '') + '"><a class="page-link" href="#" data-page="' + i + '">' + i + '</a></li>';
}
// Deactivate the next button if the current page is the last page
paginationHtml += '<li class="page-item ' + (pageNumber === total_pages ? 'disabled' : '') + '">';
paginationHtml += '<a class="page-link" href="#" data-page="' + (pageNumber + 1) + '">Next</a>';
paginationHtml += '</li>';
paginationHtml += '</ul></nav>';
// Inject the pagination into the container
$("#pagination-ranking").html(paginationHtml);
},
error: function (jqXHR, textStatus, errorThrown) {
// Inject the table into the container
$("#table-ranking").html("<div class='text-center'>" + textStatus + "</div>");
console.error("Error: " + textStatus, errorThrown);
}
});
}
// Function to handle page changes
function changePage(pageNumber) {
// Get the selected value
var courseId = $("#course-select").val();
// Call the function to load data with the new page number
loadRanking(courseId, pageNumber);
}
$(document).ready(function () {
// Attach an event handler to the select element
$("#course-select").change(function () {
// Get the selected value
var courseId = $(this).val();
// Call the function to load data
loadRanking(courseId);
});
// Event delegation for pagination links
$("#pagination-ranking").on("click", "a.page-link", function (event) {
event.preventDefault(); // Prevent the default behavior of the link
var clickedPage = $(this).data("page");
// Check if the changePage function is not already in progress
if (!$(this).hasClass('disabled')) {
// Handle the page change here
changePage(clickedPage);
}
});
});