mirror of https://github.com/JohnHammond/CTFd.git
Add a construct for sortable columns
parent
24c3520685
commit
052911f930
|
@ -66,3 +66,11 @@ tbody tr:hover {
|
|||
tr[data-href] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sort-col {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import "./main";
|
||||
import CTFd from "core/CTFd";
|
||||
import $ from "jquery";
|
||||
import { ezQuery } from "core/ezq";
|
||||
|
||||
const api_func = {
|
||||
users: (x, y) => CTFd.api.patch_user_public({ userId: x }, y),
|
||||
|
@ -37,6 +38,58 @@ function toggleAccount() {
|
|||
});
|
||||
}
|
||||
|
||||
function toggleSelectedAccounts(accountIDs, action) {
|
||||
const params = {
|
||||
hidden: action === "hide" ? true : false
|
||||
};
|
||||
const reqs = [];
|
||||
for (var accId of accountIDs) {
|
||||
reqs.push(api_func[CTFd.config.userMode](accId, params));
|
||||
}
|
||||
Promise.all(reqs).then(responses => {
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
function hideSelectedAccounts(event) {
|
||||
let accountIDs = $("input[data-account-id]:checked").map(function() {
|
||||
return $(this).data("account-id");
|
||||
});
|
||||
let target = accountIDs.length === 1 ? "account" : "accounts";
|
||||
ezQuery({
|
||||
title: "Hide Accounts",
|
||||
body: `Are you sure you want to hide ${accountIDs.length} ${target}?`,
|
||||
success: function() {
|
||||
toggleSelectedAccounts(accountIDs, "hide");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showSelectedAccounts(event) {
|
||||
let accountIDs = $("input[data-account-id]:checked").map(function() {
|
||||
return $(this).data("account-id");
|
||||
});
|
||||
let target = accountIDs.length === 1 ? "account" : "accounts";
|
||||
ezQuery({
|
||||
title: "Unhide Accounts",
|
||||
body: `Are you sure you want to unhide ${accountIDs.length} ${target}?`,
|
||||
success: function() {
|
||||
toggleSelectedAccounts(accountIDs, "show");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function toggleScoreboardSelect(event) {
|
||||
const checked = $(this).prop("checked");
|
||||
$(this)
|
||||
.closest("table")
|
||||
.find("input[data-account-id]")
|
||||
.prop("checked", checked);
|
||||
}
|
||||
|
||||
$(() => {
|
||||
$(".scoreboard-toggle").click(toggleAccount);
|
||||
$("#scoreboard-bulk-select").click(toggleScoreboardSelect);
|
||||
$("#scoreboard-hide-button").click(hideSelectedAccounts);
|
||||
$("#scoreboard-show-button").click(showSelectedAccounts);
|
||||
});
|
||||
|
|
|
@ -40,6 +40,37 @@ function deleteCorrectSubmission(event) {
|
|||
});
|
||||
}
|
||||
|
||||
function deleteSelectedSubmissions(event) {
|
||||
let submissionIDs = $("input[data-submission-id]:checked").map(function() {
|
||||
return $(this).data("submission-id");
|
||||
});
|
||||
let target = submissionIDs.length === 1 ? "submission" : "submissions";
|
||||
|
||||
ezQuery({
|
||||
title: "Delete Submissions",
|
||||
body: `Are you sure you want to delete ${submissionIDs.length} ${target}?`,
|
||||
success: function() {
|
||||
const reqs = [];
|
||||
for (var subId of submissionIDs) {
|
||||
reqs.push(CTFd.api.delete_submission({ submissionId: subId }));
|
||||
}
|
||||
Promise.all(reqs).then(responses => {
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function toggleSubmissionSelect(event) {
|
||||
const checked = $(this).prop("checked");
|
||||
$(this)
|
||||
.closest("table")
|
||||
.find("input[data-submission-id]")
|
||||
.prop("checked", checked);
|
||||
}
|
||||
|
||||
$(() => {
|
||||
$(".delete-correct-submission").click(deleteCorrectSubmission);
|
||||
$("#submissions-bulk-select").change(toggleSubmissionSelect);
|
||||
$("#submission-delete-button").click(deleteSelectedSubmissions);
|
||||
});
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import "bootstrap/dist/js/bootstrap.bundle";
|
||||
import { makeSortableTables } from "core/utils";
|
||||
import $ from "jquery";
|
||||
|
||||
export default () => {
|
||||
|
@ -52,6 +53,7 @@ export default () => {
|
|||
}
|
||||
});
|
||||
|
||||
makeSortableTables();
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
html{position:relative;min-height:100%}body{margin-bottom:60px}.footer{position:absolute;bottom:1px;width:100%;height:60px;line-height:normal !important;z-index:-20}
|
||||
|
||||
#score-graph{height:450px;display:block;clear:both}#solves-graph{display:block;height:350px}#keys-pie-graph{height:400px;display:block}#categories-pie-graph{height:400px;display:block}#solve-percentages-graph{height:400px;display:block}.no-decoration{color:inherit !important;text-decoration:none !important}.no-decoration:hover{color:inherit !important;text-decoration:none !important}.table td,.table th{vertical-align:inherit}pre{white-space:pre-wrap;margin:0;padding:0}.form-control{position:relative;display:block;border-radius:0;font-weight:400;font-family:"Avenir Next", "Helvetica Neue", Helvetica, Arial, sans-serif;-webkit-appearance:none}tbody tr:hover{background-color:rgba(0,0,0,0.1) !important}tr[data-href]{cursor:pointer}
|
||||
#score-graph{height:450px;display:block;clear:both}#solves-graph{display:block;height:350px}#keys-pie-graph{height:400px;display:block}#categories-pie-graph{height:400px;display:block}#solve-percentages-graph{height:400px;display:block}.no-decoration{color:inherit !important;text-decoration:none !important}.no-decoration:hover{color:inherit !important;text-decoration:none !important}.table td,.table th{vertical-align:inherit}pre{white-space:pre-wrap;margin:0;padding:0}.form-control{position:relative;display:block;border-radius:0;font-weight:400;font-family:"Avenir Next", "Helvetica Neue", Helvetica, Arial, sans-serif;-webkit-appearance:none}tbody tr:hover{background-color:rgba(0,0,0,0.1) !important}tr[data-href]{cursor:pointer}th{cursor:pointer}input[type=checkbox]{cursor:pointer}
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -162,7 +162,7 @@
|
|||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
;
|
||||
eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/admin/assets/js/pages/main.js\");\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nvar api_func = {\n users: function users(x, y) {\n return _CTFd.default.api.patch_user_public({\n userId: x\n }, y);\n },\n teams: function teams(x, y) {\n return _CTFd.default.api.patch_team_public({\n teamId: x\n }, y);\n }\n};\n\nfunction toggleAccount() {\n var $btn = (0, _jquery.default)(this);\n var id = $btn.data(\"account-id\");\n var state = $btn.data(\"state\");\n var hidden = undefined;\n\n if (state === \"visible\") {\n hidden = true;\n } else if (state === \"hidden\") {\n hidden = false;\n }\n\n var params = {\n hidden: hidden\n };\n\n api_func[_CTFd.default.config.userMode](id, params).then(function (response) {\n if (response.success) {\n if (hidden) {\n $btn.data(\"state\", \"hidden\");\n $btn.addClass(\"btn-danger\").removeClass(\"btn-success\");\n $btn.text(\"Hidden\");\n } else {\n $btn.data(\"state\", \"visible\");\n $btn.addClass(\"btn-success\").removeClass(\"btn-danger\");\n $btn.text(\"Visible\");\n }\n }\n });\n}\n\n(0, _jquery.default)(function () {\n (0, _jquery.default)(\".scoreboard-toggle\").click(toggleAccount);\n});\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/scoreboard.js?");
|
||||
eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/admin/assets/js/pages/main.js\");\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nvar _ezq = __webpack_require__(/*! core/ezq */ \"./CTFd/themes/core/assets/js/ezq.js\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nvar api_func = {\n users: function users(x, y) {\n return _CTFd.default.api.patch_user_public({\n userId: x\n }, y);\n },\n teams: function teams(x, y) {\n return _CTFd.default.api.patch_team_public({\n teamId: x\n }, y);\n }\n};\n\nfunction toggleAccount() {\n var $btn = (0, _jquery.default)(this);\n var id = $btn.data(\"account-id\");\n var state = $btn.data(\"state\");\n var hidden = undefined;\n\n if (state === \"visible\") {\n hidden = true;\n } else if (state === \"hidden\") {\n hidden = false;\n }\n\n var params = {\n hidden: hidden\n };\n\n api_func[_CTFd.default.config.userMode](id, params).then(function (response) {\n if (response.success) {\n if (hidden) {\n $btn.data(\"state\", \"hidden\");\n $btn.addClass(\"btn-danger\").removeClass(\"btn-success\");\n $btn.text(\"Hidden\");\n } else {\n $btn.data(\"state\", \"visible\");\n $btn.addClass(\"btn-success\").removeClass(\"btn-danger\");\n $btn.text(\"Visible\");\n }\n }\n });\n}\n\nfunction toggleSelectedAccounts(accountIDs, action) {\n var params = {\n hidden: action === \"hide\" ? true : false\n };\n var reqs = [];\n var _iteratorNormalCompletion = true;\n var _didIteratorError = false;\n var _iteratorError = undefined;\n\n try {\n for (var _iterator = accountIDs[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {\n var accId = _step.value;\n reqs.push(api_func[_CTFd.default.config.userMode](accId, params));\n }\n } catch (err) {\n _didIteratorError = true;\n _iteratorError = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion && _iterator.return != null) {\n _iterator.return();\n }\n } finally {\n if (_didIteratorError) {\n throw _iteratorError;\n }\n }\n }\n\n Promise.all(reqs).then(function (responses) {\n window.location.reload();\n });\n}\n\nfunction hideSelectedAccounts(event) {\n var accountIDs = (0, _jquery.default)(\"input[data-account-id]:checked\").map(function () {\n return (0, _jquery.default)(this).data(\"account-id\");\n });\n var target = accountIDs.length === 1 ? \"account\" : \"accounts\";\n (0, _ezq.ezQuery)({\n title: \"Hide Accounts\",\n body: \"Are you sure you want to hide \".concat(accountIDs.length, \" \").concat(target, \"?\"),\n success: function success() {\n toggleSelectedAccounts(accountIDs, \"hide\");\n }\n });\n}\n\nfunction showSelectedAccounts(event) {\n var accountIDs = (0, _jquery.default)(\"input[data-account-id]:checked\").map(function () {\n return (0, _jquery.default)(this).data(\"account-id\");\n });\n var target = accountIDs.length === 1 ? \"account\" : \"accounts\";\n (0, _ezq.ezQuery)({\n title: \"Unhide Accounts\",\n body: \"Are you sure you want to unhide \".concat(accountIDs.length, \" \").concat(target, \"?\"),\n success: function success() {\n toggleSelectedAccounts(accountIDs, \"show\");\n }\n });\n}\n\nfunction toggleScoreboardSelect(event) {\n var checked = (0, _jquery.default)(this).prop(\"checked\");\n (0, _jquery.default)(this).closest(\"table\").find(\"input[data-account-id]\").prop(\"checked\", checked);\n}\n\n(0, _jquery.default)(function () {\n (0, _jquery.default)(\".scoreboard-toggle\").click(toggleAccount);\n (0, _jquery.default)(\"#scoreboard-bulk-select\").click(toggleScoreboardSelect);\n (0, _jquery.default)(\"#scoreboard-hide-button\").click(hideSelectedAccounts);\n (0, _jquery.default)(\"#scoreboard-show-button\").click(showSelectedAccounts);\n});\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/scoreboard.js?");
|
||||
|
||||
/***/ })
|
||||
|
||||
|
|
|
@ -162,7 +162,7 @@
|
|||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
;
|
||||
eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/admin/assets/js/pages/main.js\");\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nvar _utils = __webpack_require__(/*! core/utils */ \"./CTFd/themes/core/assets/js/utils.js\");\n\nvar _ezq = __webpack_require__(/*! core/ezq */ \"./CTFd/themes/core/assets/js/ezq.js\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction deleteCorrectSubmission(event) {\n var key_id = (0, _jquery.default)(this).data(\"submission-id\");\n var $elem = (0, _jquery.default)(this).parent().parent();\n var chal_name = $elem.find(\".chal\").text().trim();\n var team_name = $elem.find(\".team\").text().trim();\n var row = (0, _jquery.default)(this).parent().parent();\n (0, _ezq.ezQuery)({\n title: \"Delete Submission\",\n body: \"Are you sure you want to delete correct submission from {0} for challenge {1}\".format(\"<strong>\" + (0, _utils.htmlEntities)(team_name) + \"</strong>\", \"<strong>\" + (0, _utils.htmlEntities)(chal_name) + \"</strong>\"),\n success: function success() {\n _CTFd.default.api.delete_submission({\n submissionId: key_id\n }).then(function (response) {\n if (response.success) {\n row.remove();\n }\n });\n }\n });\n}\n\n(0, _jquery.default)(function () {\n (0, _jquery.default)(\".delete-correct-submission\").click(deleteCorrectSubmission);\n});\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/submissions.js?");
|
||||
eval("\n\n__webpack_require__(/*! ./main */ \"./CTFd/themes/admin/assets/js/pages/main.js\");\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd */ \"./CTFd/themes/core/assets/js/CTFd.js\"));\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nvar _utils = __webpack_require__(/*! core/utils */ \"./CTFd/themes/core/assets/js/utils.js\");\n\nvar _ezq = __webpack_require__(/*! core/ezq */ \"./CTFd/themes/core/assets/js/ezq.js\");\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction deleteCorrectSubmission(event) {\n var key_id = (0, _jquery.default)(this).data(\"submission-id\");\n var $elem = (0, _jquery.default)(this).parent().parent();\n var chal_name = $elem.find(\".chal\").text().trim();\n var team_name = $elem.find(\".team\").text().trim();\n var row = (0, _jquery.default)(this).parent().parent();\n (0, _ezq.ezQuery)({\n title: \"Delete Submission\",\n body: \"Are you sure you want to delete correct submission from {0} for challenge {1}\".format(\"<strong>\" + (0, _utils.htmlEntities)(team_name) + \"</strong>\", \"<strong>\" + (0, _utils.htmlEntities)(chal_name) + \"</strong>\"),\n success: function success() {\n _CTFd.default.api.delete_submission({\n submissionId: key_id\n }).then(function (response) {\n if (response.success) {\n row.remove();\n }\n });\n }\n });\n}\n\nfunction deleteSelectedSubmissions(event) {\n var submissionIDs = (0, _jquery.default)(\"input[data-submission-id]:checked\").map(function () {\n return (0, _jquery.default)(this).data(\"submission-id\");\n });\n var target = submissionIDs.length === 1 ? \"submission\" : \"submissions\";\n (0, _ezq.ezQuery)({\n title: \"Delete Submissions\",\n body: \"Are you sure you want to delete \".concat(submissionIDs.length, \" \").concat(target, \"?\"),\n success: function success() {\n var reqs = [];\n var _iteratorNormalCompletion = true;\n var _didIteratorError = false;\n var _iteratorError = undefined;\n\n try {\n for (var _iterator = submissionIDs[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {\n var subId = _step.value;\n reqs.push(_CTFd.default.api.delete_submission({\n submissionId: subId\n }));\n }\n } catch (err) {\n _didIteratorError = true;\n _iteratorError = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion && _iterator.return != null) {\n _iterator.return();\n }\n } finally {\n if (_didIteratorError) {\n throw _iteratorError;\n }\n }\n }\n\n Promise.all(reqs).then(function (responses) {\n window.location.reload();\n });\n }\n });\n}\n\nfunction toggleSubmissionSelect(event) {\n var checked = (0, _jquery.default)(this).prop(\"checked\");\n (0, _jquery.default)(this).closest(\"table\").find(\"input[data-submission-id]\").prop(\"checked\", checked);\n}\n\n(0, _jquery.default)(function () {\n (0, _jquery.default)(\".delete-correct-submission\").click(deleteCorrectSubmission);\n (0, _jquery.default)(\"#submissions-bulk-select\").change(toggleSubmissionSelect);\n (0, _jquery.default)(\"#submission-delete-button\").click(deleteSelectedSubmissions);\n});\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/pages/submissions.js?");
|
||||
|
||||
/***/ })
|
||||
|
||||
|
|
|
@ -9,19 +9,43 @@
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table id="scoreboard" class="table table-striped">
|
||||
<div class="float-right pb-3">
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-danger" id="scoreboard-hide-button" data-toggle="tooltip" title="Hide Accounts">
|
||||
<i class="btn-fa fas fa-eye-slash"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-success" id="scoreboard-show-button" data-toggle="tooltip" title="Unhide Accounts">
|
||||
<i class="btn-fa fas fa-eye"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table id="scoreboard" class="table table-striped border">
|
||||
<thead>
|
||||
<tr>
|
||||
<td width="10px"><b>Place</b></td>
|
||||
<td><b>Team</b></td>
|
||||
<td><b>Score</b></td>
|
||||
<td><b>Visibility</b></td>
|
||||
<td class="d-block border-right border-bottom text-center">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" id="scoreboard-bulk-select">
|
||||
</div>
|
||||
</td>
|
||||
<th class="sort-col text-center"><b>Place</b></th>
|
||||
<th class="sort-col"><b>Team</b></th>
|
||||
<th class="sort-col"><b>Score</b></th>
|
||||
<th class="sort-col"><b>Visibility</b></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for standing in standings %}
|
||||
<tr data-href="{{ generate_account_url(standing.account_id, admin=True) }}">
|
||||
<td>{{ loop.index }}</td>
|
||||
<td class="d-block border-right text-center">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input" value="{{ standing.account_id }}" data-account-id="{{ standing.account_id }}">
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-center" width="10%">{{ loop.index }}</td>
|
||||
<td>
|
||||
<a href="{{ generate_account_url(standing.account_id, admin=True) }}">
|
||||
{{ standing.name }}
|
||||
|
@ -40,15 +64,11 @@
|
|||
</td>
|
||||
<td>{{ standing.score }}</td>
|
||||
<td>
|
||||
{% if standing.hidden %}
|
||||
<button class="btn-sm btn-danger cursor-pointer scoreboard-toggle" type="submit"
|
||||
data-account-id="{{ standing.account_id }}" data-state="hidden">Hidden
|
||||
</button>
|
||||
{% else %}
|
||||
<button class="btn-sm btn-success cursor-pointer scoreboard-toggle" type="submit"
|
||||
data-account-id="{{ standing.account_id }}" data-state="visible">Visible
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if standing.hidden %}
|
||||
<span class="badge badge-danger">Hidden</span>
|
||||
{% else %}
|
||||
<span class="badge badge-success">Visible</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
@ -14,21 +14,41 @@
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table id="teamsboard" class=" table table-striped">
|
||||
<div class="float-right pb-3">
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-danger" id="submission-delete-button">
|
||||
<i class="btn-fa fas fa-trash-alt"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table id="teamsboard" class="table table-striped border">
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="text-center"><b>ID</b></td>
|
||||
<td><b>Team</b></td>
|
||||
<td><b>Challenge</b></td>
|
||||
<td><b>Type</b></td>
|
||||
<td><b>Submission</b></td>
|
||||
<td class="text-center"><b>Date</b></td>
|
||||
<td class="text-center"><b>Delete</b></td>
|
||||
<td class="d-block border-right border-bottom">
|
||||
<div class="form-check text-center">
|
||||
<input type="checkbox" class="form-check-input" id="submissions-bulk-select">
|
||||
</div>
|
||||
</td>
|
||||
<th class="text-center sort-col"><b>ID</b></th>
|
||||
<th class="sort-col"><b>Team</b></th>
|
||||
<th class="sort-col"><b>Challenge</b></th>
|
||||
<th class="sort-col"><b>Type</b></th>
|
||||
<th class="sort-col"><b>Submission</b></th>
|
||||
<th class="text-center sort-col"><b>Date</b></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for sub in submissions %}
|
||||
<tr>
|
||||
<td class="d-block border-right">
|
||||
<div class="form-check text-center">
|
||||
<input type="checkbox" class="form-check-input" value="{{ sub.id }}" data-submission-id="{{ sub.id }}">
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-center" id="{{ sub.id }}">
|
||||
{{ sub.id }}
|
||||
</td>
|
||||
|
@ -47,12 +67,6 @@
|
|||
<td class="text-center solve-time">
|
||||
<span data-time="{{ sub.date | isoformat }}"></span>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<span class="delete-correct-submission" data-toggle="tooltip"
|
||||
data-placement="top" title="Delete submission #{{ sub.id }}" data-submission-id="{{ sub.id }}">
|
||||
<i class="fas fa-times"></i>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
|
|
@ -256,3 +256,38 @@ export function copyToClipboard(event, selector) {
|
|||
$(event.target).tooltip("hide");
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
export function makeSortableTables() {
|
||||
$("th.sort-col").append(` <i class="fas fa-sort"></i>`);
|
||||
$("th.sort-col").click(function() {
|
||||
var table = $(this)
|
||||
.parents("table")
|
||||
.eq(0);
|
||||
var rows = table
|
||||
.find("tr:gt(0)")
|
||||
.toArray()
|
||||
.sort(comparer($(this).index()));
|
||||
this.asc = !this.asc;
|
||||
if (!this.asc) {
|
||||
rows = rows.reverse();
|
||||
}
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
table.append(rows[i]);
|
||||
}
|
||||
});
|
||||
function comparer(index) {
|
||||
return function(a, b) {
|
||||
var valA = getCellValue(a, index),
|
||||
valB = getCellValue(b, index);
|
||||
return $.isNumeric(valA) && $.isNumeric(valB)
|
||||
? valA - valB
|
||||
: valA.toString().localeCompare(valB);
|
||||
};
|
||||
}
|
||||
function getCellValue(row, index) {
|
||||
return $(row)
|
||||
.children("td")
|
||||
.eq(index)
|
||||
.text();
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue