Merge pull request #1373 from CTFd/bulk-table-profile-actions

* Convert Admin Panel User/Team submission actions to be bulk actions
* Add "Mark Missing" feature for Teams
2.4.0-dev
Kevin Chung 2020-05-03 01:10:35 -04:00 committed by GitHub
commit f559c7d8fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 636 additions and 427 deletions

View File

@ -220,14 +220,4 @@ $(() => {
} }
}); });
}); });
if (window.location.hash) {
let hash = window.location.hash.replace("<>[]'\"", "");
$('nav a[href="' + hash + '"]').tab("show");
}
$(".nav-tabs a").click(function(event) {
$(this).tab("show");
window.location.hash = this.hash;
});
}); });

View File

@ -447,16 +447,6 @@ $(() => {
$("#challenge-create-options form").submit(handleChallengeOptions); $("#challenge-create-options form").submit(handleChallengeOptions);
$(".nav-tabs a").click(function(e) {
$(this).tab("show");
window.location.hash = this.hash;
});
if (window.location.hash) {
let hash = window.location.hash.replace("<>[]'\"", "");
$('nav a[href="' + hash + '"]').tab("show");
}
$("#tags-add-input").keyup(addTag); $("#tags-add-input").keyup(addTag);
$(".delete-tag").click(deleteTag); $(".delete-tag").click(deleteTag);

View File

@ -220,10 +220,6 @@ function exportConfig(event) {
window.location.href = $(this).attr("href"); window.location.href = $(this).attr("href");
} }
function showTab(event) {
window.location.hash = this.hash;
}
function insertTimezones(target) { function insertTimezones(target) {
let current = $("<option>").text(moment.tz.guess()); let current = $("<option>").text(moment.tz.guess());
$(target).append(current); $(target).append(current);
@ -258,7 +254,6 @@ $(() => {
$("#remove-logo").click(removeLogo); $("#remove-logo").click(removeLogo);
$("#export-button").click(exportConfig); $("#export-button").click(exportConfig);
$("#import-button").click(importConfig); $("#import-button").click(importConfig);
$(".nav-pills a").click(showTab);
$("#config-color-update").click(function() { $("#config-color-update").click(function() {
const hex_code = $("#config-color-picker").val(); const hex_code = $("#config-color-picker").val();
const user_css = $("#theme-header").val(); const user_css = $("#theme-header").val();
@ -287,12 +282,6 @@ $(() => {
loadDateValues("freeze"); loadDateValues("freeze");
}); });
let hash = window.location.hash;
if (hash) {
hash = hash.replace("<>[]'\"", "");
$('ul.nav a[href="' + hash + '"]').tab("show");
}
const start = $("#start").val(); const start = $("#start").val();
const end = $("#end").val(); const end = $("#end").val();
const freeze = $("#freeze").val(); const freeze = $("#freeze").val();

View File

@ -2,7 +2,7 @@ import "./main";
import $ from "jquery"; import $ from "jquery";
import CTFd from "core/CTFd"; import CTFd from "core/CTFd";
import { htmlEntities } from "core/utils"; import { htmlEntities } from "core/utils";
import { ezQuery, ezBadge } from "core/ezq"; import { ezAlert, ezQuery, ezBadge } from "core/ezq";
import { createGraph, updateGraph } from "core/graphs"; import { createGraph, updateGraph } from "core/graphs";
function createTeam(event) { function createTeam(event) {
@ -80,6 +80,132 @@ function updateTeam(event) {
}); });
} }
function deleteSelectedSubmissions(event, target) {
let submissions;
let type;
let title;
switch (target) {
case "solves":
submissions = $("input[data-submission-type=correct]:checked");
type = "solve";
title = "Solves";
break;
case "fails":
submissions = $("input[data-submission-type=incorrect]:checked");
type = "fail";
title = "Fails";
break;
default:
break;
}
let submissionIDs = submissions.map(function() {
return $(this).data("submission-id");
});
let target_string = submissionIDs.length === 1 ? type : type + "s";
ezQuery({
title: `Delete ${title}`,
body: `Are you sure you want to delete ${
submissionIDs.length
} ${target_string}?`,
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 deleteSelectedAwards(event) {
let awardIDs = $("input[data-award-id]:checked").map(function() {
return $(this).data("award-id");
});
let target = awardIDs.length === 1 ? "award" : "awards";
ezQuery({
title: `Delete Awards`,
body: `Are you sure you want to delete ${awardIDs.length} ${target}?`,
success: function() {
const reqs = [];
for (var awardID of awardIDs) {
let req = CTFd.fetch("/api/v1/awards/" + awardID, {
method: "DELETE",
credentials: "same-origin",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
}
});
reqs.push(req);
}
Promise.all(reqs).then(responses => {
window.location.reload();
});
}
});
}
function solveSelectedMissingChallenges(event) {
event.preventDefault();
let challengeIDs = $("input[data-missing-challenge-id]:checked").map(
function() {
return $(this).data("missing-challenge-id");
}
);
let target = challengeIDs.length === 1 ? "challenge" : "challenges";
ezQuery({
title: `Mark Correct`,
body: `Are you sure you want to mark ${
challengeIDs.length
} challenges correct for ${htmlEntities(TEAM_NAME)}?`,
success: function() {
ezAlert({
title: `User Attribution`,
body: `
Which user on ${htmlEntities(TEAM_NAME)} solved these challenges?
<div class="pb-3" id="query-team-member-solve">
${$("#team-member-select").html()}
</div>
`,
button: "Mark Correct",
success: function() {
const USER_ID = $("#query-team-member-solve > select").val();
const reqs = [];
for (var challengeID of challengeIDs) {
let params = {
provided: "MARKED AS SOLVED BY ADMIN",
user_id: USER_ID,
team_id: TEAM_ID,
challenge_id: challengeID,
type: "correct"
};
let req = CTFd.fetch("/api/v1/submissions", {
method: "POST",
credentials: "same-origin",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(params)
});
reqs.push(req);
}
Promise.all(reqs).then(responses => {
window.location.reload();
});
}
});
}
});
}
const api_funcs = { const api_funcs = {
team: [ team: [
x => CTFd.api.get_team_solves({ teamId: x }), x => CTFd.api.get_team_solves({ teamId: x }),
@ -335,82 +461,20 @@ $(() => {
}); });
}); });
$(".delete-submission").click(function(e) { $("#solves-delete-button").click(function(e) {
e.preventDefault(); deleteSelectedSubmissions(e, "solves");
const submission_id = $(this).attr("submission-id");
const submission_type = $(this).attr("submission-type");
const submission_challenge = $(this).attr("submission-challenge");
const body = "<span>Are you sure you want to delete <strong>{0}</strong> submission from <strong>{1}</strong> for <strong>{2}</strong>?</span>".format(
htmlEntities(submission_type),
htmlEntities(TEAM_NAME),
htmlEntities(submission_challenge)
);
const row = $(this)
.parent()
.parent();
ezQuery({
title: "Delete Submission",
body: body,
success: function() {
CTFd.fetch("/api/v1/submissions/" + submission_id, {
method: "DELETE",
credentials: "same-origin",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
}
})
.then(function(response) {
return response.json();
})
.then(function(response) {
if (response.success) {
row.remove();
}
});
}
});
}); });
$(".delete-award").click(function(e) { $("#fails-delete-button").click(function(e) {
e.preventDefault(); deleteSelectedSubmissions(e, "fails");
const award_id = $(this).attr("award-id"); });
const award_name = $(this).attr("award-name");
const body = "<span>Are you sure you want to delete the <strong>{0}</strong> award from <strong>{1}</strong>?".format( $("#awards-delete-button").click(function(e) {
htmlEntities(award_name), deleteSelectedAwards(e);
htmlEntities(TEAM_NAME) });
);
const row = $(this) $("#missing-solve-button").click(function(e) {
.parent() solveSelectedMissingChallenges(e);
.parent();
ezQuery({
title: "Delete Award",
body: body,
success: function() {
CTFd.fetch("/api/v1/awards/" + award_id, {
method: "DELETE",
credentials: "same-origin",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
}
})
.then(function(response) {
return response.json();
})
.then(function(response) {
if (response.success) {
row.remove();
}
});
}
});
}); });
$("#team-info-create-form").submit(createTeam); $("#team-info-create-form").submit(createTeam);

View File

@ -189,128 +189,115 @@ function emailUser(event) {
}); });
} }
function deleteUserSubmission(event) { function deleteSelectedSubmissions(event, target) {
event.preventDefault(); let submissions;
const submission_id = $(this).attr("submission-id"); let type;
const submission_type = $(this).attr("submission-type"); let title;
const submission_challenge = $(this).attr("submission-challenge"); switch (target) {
case "solves":
submissions = $("input[data-submission-type=correct]:checked");
type = "solve";
title = "Solves";
break;
case "fails":
submissions = $("input[data-submission-type=incorrect]:checked");
type = "fail";
title = "Fails";
break;
default:
break;
}
const body = "<span>Are you sure you want to delete <strong>{0}</strong> submission from <strong>{1}</strong> for <strong>{2}</strong>?</span>".format( let submissionIDs = submissions.map(function() {
htmlEntities(submission_type), return $(this).data("submission-id");
htmlEntities(USER_NAME), });
htmlEntities(submission_challenge) let target_string = submissionIDs.length === 1 ? type : type + "s";
);
const row = $(this)
.parent()
.parent();
ezQuery({ ezQuery({
title: "Delete Submission", title: `Delete ${title}`,
body: body, body: `Are you sure you want to delete ${
submissionIDs.length
} ${target_string}?`,
success: function() { success: function() {
CTFd.fetch("/api/v1/submissions/" + submission_id, { const reqs = [];
method: "DELETE", for (var subId of submissionIDs) {
credentials: "same-origin", reqs.push(CTFd.api.delete_submission({ submissionId: subId }));
headers: { }
Accept: "application/json", Promise.all(reqs).then(responses => {
"Content-Type": "application/json" window.location.reload();
} });
})
.then(function(response) {
return response.json();
})
.then(function(response) {
if (response.success) {
row.remove();
}
});
} }
}); });
} }
function deleteUserAward(event) { function deleteSelectedAwards(event) {
event.preventDefault(); let awardIDs = $("input[data-award-id]:checked").map(function() {
const award_id = $(this).attr("award-id"); return $(this).data("award-id");
const award_name = $(this).attr("award-name"); });
let target = awardIDs.length === 1 ? "award" : "awards";
const body = "<span>Are you sure you want to delete the <strong>{0}</strong> award from <strong>{1}</strong>?".format(
htmlEntities(award_name),
htmlEntities(USER_NAME)
);
const row = $(this)
.parent()
.parent();
ezQuery({ ezQuery({
title: "Delete Award", title: `Delete Awards`,
body: body, body: `Are you sure you want to delete ${awardIDs.length} ${target}?`,
success: function() { success: function() {
CTFd.fetch("/api/v1/awards/" + award_id, { const reqs = [];
method: "DELETE", for (var awardID of awardIDs) {
credentials: "same-origin", let req = CTFd.fetch("/api/v1/awards/" + awardID, {
headers: { method: "DELETE",
Accept: "application/json", credentials: "same-origin",
"Content-Type": "application/json" headers: {
} Accept: "application/json",
}) "Content-Type": "application/json"
.then(function(response) {
return response.json();
})
.then(function(response) {
if (response.success) {
row.remove();
} }
}); });
reqs.push(req);
}
Promise.all(reqs).then(responses => {
window.location.reload();
});
} }
}); });
} }
function correctUserSubmission(event) { function solveSelectedMissingChallenges(event) {
event.preventDefault(); event.preventDefault();
const challenge_id = $(this).attr("challenge-id"); let challengeIDs = $("input[data-missing-challenge-id]:checked").map(
const challenge_name = $(this).attr("challenge-name"); function() {
const row = $(this) return $(this).data("missing-challenge-id");
.parent() }
.parent();
const body = "<span>Are you sure you want to mark <strong>{0}</strong> solved for from <strong>{1}</strong>?".format(
htmlEntities(challenge_name),
htmlEntities(USER_NAME)
); );
let target = challengeIDs.length === 1 ? "challenge" : "challenges";
const params = {
provided: "MARKED AS SOLVED BY ADMIN",
user_id: USER_ID,
team_id: TEAM_ID,
challenge_id: challenge_id,
type: "correct"
};
ezQuery({ ezQuery({
title: "Mark Correct", title: `Mark Correct`,
body: body, body: `Are you sure you want to mark ${
challengeIDs.length
} correct for ${htmlEntities(USER_NAME)}?`,
success: function() { success: function() {
CTFd.fetch("/api/v1/submissions", { const reqs = [];
method: "POST", for (var challengeID of challengeIDs) {
credentials: "same-origin", let params = {
headers: { provided: "MARKED AS SOLVED BY ADMIN",
Accept: "application/json", user_id: USER_ID,
"Content-Type": "application/json" team_id: TEAM_ID,
}, challenge_id: challengeID,
body: JSON.stringify(params) type: "correct"
}) };
.then(function(response) {
return response.json(); let req = CTFd.fetch("/api/v1/submissions", {
}) method: "POST",
.then(function(response) { credentials: "same-origin",
if (response.success) { headers: {
// TODO: Refresh missing and solves instead of reloading Accept: "application/json",
row.remove(); "Content-Type": "application/json"
window.location.reload(); },
} body: JSON.stringify(params)
}); });
reqs.push(req);
}
Promise.all(reqs).then(responses => {
window.location.reload();
});
} }
}); });
} }
@ -425,9 +412,21 @@ $(() => {
$("#user-mail-form").submit(emailUser); $("#user-mail-form").submit(emailUser);
$(".delete-submission").click(deleteUserSubmission); $("#solves-delete-button").click(function(e) {
$(".delete-award").click(deleteUserAward); deleteSelectedSubmissions(e, "solves");
$(".correct-submission").click(correctUserSubmission); });
$("#fails-delete-button").click(function(e) {
deleteSelectedSubmissions(e, "fails");
});
$("#awards-delete-button").click(function(e) {
deleteSelectedAwards(e);
});
$("#missing-solve-button").click(function(e) {
solveSelectedMissingChallenges(e);
});
$("#user-info-create-form").submit(createUser); $("#user-info-create-form").submit(createUser);

View File

@ -80,6 +80,22 @@ export default () => {
window.location.href = url.toString(); window.location.href = url.toString();
}); });
$('a[data-toggle="tab"]').on("shown.bs.tab", function(e) {
sessionStorage.setItem("activeTab", $(e.target).attr("href"));
});
let activeTab = sessionStorage.getItem("activeTab");
if (activeTab) {
let target = $(
`.nav-tabs a[href="${activeTab}"], .nav-pills a[href="${activeTab}"]`
);
if (target.length) {
target.tab("show");
} else {
sessionStorage.removeItem("activeTab");
}
}
makeSortableTables(); makeSortableTables();
$('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').tooltip();
}); });

View File

@ -20,7 +20,7 @@ eval("\n\nvar _CTFd = _interopRequireDefault(__webpack_require__(/*! core/CTFd *
/***/ (function(module, exports, __webpack_require__) { /***/ (function(module, exports, __webpack_require__) {
; ;
eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\n__webpack_require__(/*! bootstrap/dist/js/bootstrap.bundle */ \"./node_modules/bootstrap/dist/js/bootstrap.bundle.js\");\n\nvar _utils = __webpack_require__(/*! core/utils */ \"./CTFd/themes/core/assets/js/utils.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 _default = function _default() {\n // TODO: This is kind of a hack to mimic a React-like state construct.\n // It should be removed once we have a real front-end framework in place.\n (0, _jquery.default)(\":input\").each(function () {\n (0, _jquery.default)(this).data(\"initial\", (0, _jquery.default)(this).val());\n });\n (0, _jquery.default)(\".form-control\").bind({\n focus: function focus() {\n (0, _jquery.default)(this).addClass(\"input-filled-valid\");\n },\n blur: function blur() {\n if ((0, _jquery.default)(this).val() === \"\") {\n (0, _jquery.default)(this).removeClass(\"input-filled-valid\");\n }\n }\n });\n (0, _jquery.default)(\".modal\").on(\"show.bs.modal\", function (e) {\n (0, _jquery.default)(\".form-control\").each(function () {\n if ((0, _jquery.default)(this).val()) {\n (0, _jquery.default)(this).addClass(\"input-filled-valid\");\n }\n });\n });\n (0, _jquery.default)(function () {\n (0, _jquery.default)(\".form-control\").each(function () {\n if ((0, _jquery.default)(this).val()) {\n (0, _jquery.default)(this).addClass(\"input-filled-valid\");\n }\n });\n (0, _jquery.default)(\"tr[data-href]\").click(function () {\n var sel = getSelection().toString();\n\n if (!sel) {\n var href = (0, _jquery.default)(this).attr(\"data-href\");\n\n if (href) {\n window.location = href;\n }\n }\n\n return false;\n });\n (0, _jquery.default)(\"[data-checkbox]\").click(function (e) {\n if ((0, _jquery.default)(e.target).is(\"input[type=checkbox]\")) {\n e.stopImmediatePropagation();\n return;\n }\n\n var checkbox = (0, _jquery.default)(this).find(\"input[type=checkbox]\"); // Doing it this way with an event allows data-checkbox-all to work\n\n checkbox.click();\n e.stopImmediatePropagation();\n });\n (0, _jquery.default)(\"[data-checkbox-all]\").on(\"click change\", function (e) {\n var checked = (0, _jquery.default)(this).prop(\"checked\");\n var idx = (0, _jquery.default)(this).index() + 1;\n (0, _jquery.default)(this).closest(\"table\").find(\"tr td:nth-child(\".concat(idx, \") input[type=checkbox]\")).prop(\"checked\", checked);\n e.stopImmediatePropagation();\n });\n (0, _jquery.default)(\"tr[data-href] a, tr[data-href] button\").click(function (e) {\n // TODO: This is a hack to allow modal close buttons to work\n if (!(0, _jquery.default)(this).attr(\"data-dismiss\")) {\n e.stopPropagation();\n }\n });\n (0, _jquery.default)(\".page-select\").change(function () {\n var url = new URL(window.location);\n url.searchParams.set(\"page\", this.value);\n window.location.href = url.toString();\n });\n (0, _utils.makeSortableTables)();\n (0, _jquery.default)('[data-toggle=\"tooltip\"]').tooltip();\n });\n};\n\nexports.default = _default;\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/styles.js?"); eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = void 0;\n\n__webpack_require__(/*! bootstrap/dist/js/bootstrap.bundle */ \"./node_modules/bootstrap/dist/js/bootstrap.bundle.js\");\n\nvar _utils = __webpack_require__(/*! core/utils */ \"./CTFd/themes/core/assets/js/utils.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 _default = function _default() {\n // TODO: This is kind of a hack to mimic a React-like state construct.\n // It should be removed once we have a real front-end framework in place.\n (0, _jquery.default)(\":input\").each(function () {\n (0, _jquery.default)(this).data(\"initial\", (0, _jquery.default)(this).val());\n });\n (0, _jquery.default)(\".form-control\").bind({\n focus: function focus() {\n (0, _jquery.default)(this).addClass(\"input-filled-valid\");\n },\n blur: function blur() {\n if ((0, _jquery.default)(this).val() === \"\") {\n (0, _jquery.default)(this).removeClass(\"input-filled-valid\");\n }\n }\n });\n (0, _jquery.default)(\".modal\").on(\"show.bs.modal\", function (e) {\n (0, _jquery.default)(\".form-control\").each(function () {\n if ((0, _jquery.default)(this).val()) {\n (0, _jquery.default)(this).addClass(\"input-filled-valid\");\n }\n });\n });\n (0, _jquery.default)(function () {\n (0, _jquery.default)(\".form-control\").each(function () {\n if ((0, _jquery.default)(this).val()) {\n (0, _jquery.default)(this).addClass(\"input-filled-valid\");\n }\n });\n (0, _jquery.default)(\"tr[data-href]\").click(function () {\n var sel = getSelection().toString();\n\n if (!sel) {\n var href = (0, _jquery.default)(this).attr(\"data-href\");\n\n if (href) {\n window.location = href;\n }\n }\n\n return false;\n });\n (0, _jquery.default)(\"[data-checkbox]\").click(function (e) {\n if ((0, _jquery.default)(e.target).is(\"input[type=checkbox]\")) {\n e.stopImmediatePropagation();\n return;\n }\n\n var checkbox = (0, _jquery.default)(this).find(\"input[type=checkbox]\"); // Doing it this way with an event allows data-checkbox-all to work\n\n checkbox.click();\n e.stopImmediatePropagation();\n });\n (0, _jquery.default)(\"[data-checkbox-all]\").on(\"click change\", function (e) {\n var checked = (0, _jquery.default)(this).prop(\"checked\");\n var idx = (0, _jquery.default)(this).index() + 1;\n (0, _jquery.default)(this).closest(\"table\").find(\"tr td:nth-child(\".concat(idx, \") input[type=checkbox]\")).prop(\"checked\", checked);\n e.stopImmediatePropagation();\n });\n (0, _jquery.default)(\"tr[data-href] a, tr[data-href] button\").click(function (e) {\n // TODO: This is a hack to allow modal close buttons to work\n if (!(0, _jquery.default)(this).attr(\"data-dismiss\")) {\n e.stopPropagation();\n }\n });\n (0, _jquery.default)(\".page-select\").change(function () {\n var url = new URL(window.location);\n url.searchParams.set(\"page\", this.value);\n window.location.href = url.toString();\n });\n (0, _jquery.default)('a[data-toggle=\"tab\"]').on(\"shown.bs.tab\", function (e) {\n sessionStorage.setItem(\"activeTab\", (0, _jquery.default)(e.target).attr(\"href\"));\n });\n var activeTab = sessionStorage.getItem(\"activeTab\");\n\n if (activeTab) {\n var target = (0, _jquery.default)(\".nav-tabs a[href=\\\"\".concat(activeTab, \"\\\"], .nav-pills a[href=\\\"\").concat(activeTab, \"\\\"]\"));\n\n if (target.length) {\n target.tab(\"show\");\n } else {\n sessionStorage.removeItem(\"activeTab\");\n }\n }\n\n (0, _utils.makeSortableTables)();\n (0, _jquery.default)('[data-toggle=\"tooltip\"]').tooltip();\n });\n};\n\nexports.default = _default;\n\n//# sourceURL=webpack:///./CTFd/themes/admin/assets/js/styles.js?");
/***/ }), /***/ }),

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -61,6 +61,15 @@
</div> </div>
</div> </div>
<template id="team-member-select">
<select class="form-control custom-select">
<option value=""> -- </option>
{% for member in members %}
<option value="{{ member.id }}">{{ member.name }}</option>
{% endfor %}
</select>
</template>
<div id="team-addresses-modal" class="modal fade"> <div id="team-addresses-modal" class="modal fade">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
@ -235,127 +244,178 @@
<a class="nav-item nav-link" id="nav-awards-tab" data-toggle="tab" href="#nav-awards" role="tab" <a class="nav-item nav-link" id="nav-awards-tab" data-toggle="tab" href="#nav-awards" role="tab"
aria-controls="nav-awards" aria-selected="false">Awards</a> aria-controls="nav-awards" aria-selected="false">Awards</a>
<a class="nav-item nav-link" id="nav-missing-tab" data-toggle="tab" href="#nav-missing" role="tab"
aria-controls="nav-missing" aria-selected="false">Missing</a>
</nav> </nav>
<div class="tab-content min-vh-25" id="nav-tabContent"> <div class="tab-content min-vh-50" id="nav-tabContent">
<div class="tab-pane fade show active" id="nav-solves" role="tabpanel" aria-labelledby="nav-solves-tab"> <div class="tab-pane fade show active" id="nav-solves" role="tabpanel" aria-labelledby="nav-solves-tab">
<h3 class="text-center pt-5 d-block">Solves</h3>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<table class="table table-striped"> <div class="float-right pb-3">
<h3 class="text-center py-3 d-block">Solves</h3> <div class="btn-group" role="group">
<thead> <button type="button" class="btn btn-outline-danger" id="solves-delete-button">
<tr> <i class="btn-fa fas fa-trash-alt"></i>
<td class="text-center"><b>Challenge</b></td> </button>
<td class="text-center"><b>User</b></td> </div>
<td class="text-center"><b>Submitted</b></td> </div>
<td class="text-center"><b>Category</b></td> </div>
<td class="text-center"><b>Value</b></td> </div>
<td class="text-center"><b>Time</b></td> <div class="row">
<td class="text-center"><b>Delete</b></td> <div class="col-md-12">
</tr> <table class="table table-striped border">
</thead> <thead>
<tbody> <tr>
{% for solve in solves %} <th class="border-right" data-checkbox>
<tr class="chal-solve" data-href="{{ url_for("admin.challenges_detail", challenge_id=solve.challenge_id) }}"> <div class="form-check text-center">
<td class="text-center chal" id="{{ solve.challenge_id }}"> <input type="checkbox" class="form-check-input" data-checkbox-all>&nbsp;
<a href="{{ url_for("admin.challenges_detail", challenge_id=solve.challenge_id) }}"> </div>
{{ solve.challenge.name }} </th>
</a> <th class="sort-col text-center"><b>Challenge</b></th>
</td> <th class="sort-col text-center"><b>User</b></th>
<td class="text-center"> <th class="sort-col text-center"><b>Submitted</b></th>
<a href="{{ url_for("admin.users_detail", user_id=solve.user_id) }}"> <th class="sort-col text-center"><b>Category</b></th>
{{ solve.user.name }} <th class="sort-col text-center"><b>Value</b></th>
</a> <th class="sort-col text-center"><b>Time</b></th>
</td> </tr>
<td class="flag" id="{{ solve.id }}"><pre>{{ solve.provided }}</pre></td> </thead>
<td class="text-center">{{ solve.challenge.category }}</td> <tbody>
<td class="text-center">{{ solve.challenge.value }}</td> {% for solve in solves %}
<td class="text-center solve-time"> <tr class="chal-solve" data-href="{{ url_for("admin.challenges_detail", challenge_id=solve.challenge_id) }}">
<span data-time="{{ solve.date | isoformat }}"></span> <td class="border-right" data-checkbox>
</td> <div class="form-check text-center">
<td class="text-center"> <input type="checkbox" class="form-check-input" value="{{ solve.id }}" data-submission-id="{{ solve.id }}"
<span class="delete-submission" submission-id="{{ solve.id }}" data-submission-type="{{ solve.type }}"
submission-type="{{ solve.type }}" data-submission-challenge="{{ solve.challenge.name }}">&nbsp;
submission-challenge="{{ solve.challenge.name }}" data-toggle="tooltip" </div>
data-placement="top" title="Delete solve #{{ solve.id }}"> </td>
<i class="fas fa-times"></i> <td class="text-center chal" id="{{ solve.challenge_id }}">
</span> <a href="{{ url_for("admin.challenges_detail", challenge_id=solve.challenge_id) }}">
</td> {{ solve.challenge.name }}
</tr> </a>
{% endfor %} </td>
</tbody> <td class="text-center">
</table> <a href="{{ url_for("admin.users_detail", user_id=solve.user_id) }}">
{{ solve.user.name }}
</a>
</td>
<td class="flag" id="{{ solve.id }}"><pre>{{ solve.provided }}</pre></td>
<td class="text-center">{{ solve.challenge.category }}</td>
<td class="text-center">{{ solve.challenge.value }}</td>
<td class="text-center solve-time">
<span data-time="{{ solve.date | isoformat }}"></span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div> </div>
</div> </div>
</div> </div>
<div class="tab-pane fade" id="nav-wrong" role="tabpanel" aria-labelledby="nav-wrong-tab"> <div class="tab-pane fade" id="nav-wrong" role="tabpanel" aria-labelledby="nav-wrong-tab">
<h3 class="text-center pt-5 d-block">Fails</h3>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<table class="table table-striped"> <div class="float-right pb-3">
<h3 class="text-center py-3 d-block">Fails</h3> <div class="btn-group" role="group">
<thead> <button type="button" class="btn btn-outline-danger" id="fails-delete-button">
<tr> <i class="btn-fa fas fa-trash-alt"></i>
<td class="text-center"><b>Challenge</b></td> </button>
<td class="text-center"><b>User</b></td> </div>
<td class="text-center"><b>Submitted</b></td> </div>
<td class="text-center"><b>Time</b></td> </div>
<td class="text-center"><b>Delete</b></td> </div>
</tr> <div class="row">
</thead> <div class="col-md-12">
<tbody> <table class="table table-striped border">
{% for fail in fails %} <thead>
<tr class="chal-wrong" data-href="{{ url_for("admin.challenges_detail", challenge_id=fail.challenge_id) }}"> <tr>
<td class="text-center chal" id="{{ fail.challenge_id }}"> <th class="border-right" data-checkbox>
<a href="{{ url_for("admin.challenges_detail", challenge_id=fail.challenge_id) }}"> <div class="form-check text-center">
{{ fail.challenge.name }} <input type="checkbox" class="form-check-input" data-checkbox-all>&nbsp;
</a> </div>
</td> </th>
<td class="text-center"> <th class="sort-col text-center"><b>Challenge</b></th>
<a href="{{ url_for("admin.users_detail", user_id=fail.user_id) }}"> <th class="sort-col text-center"><b>User</b></th>
{{ fail.user.name }} <th class="sort-col text-center"><b>Submitted</b></th>
</a> <th class="sort-col text-center"><b>Time</b></th>
</td> </tr>
<td class="flag" id="{{ fail.id }}"><pre>{{ fail.provided }}</pre></td> </thead>
<td class="text-center solve-time"> <tbody>
<span data-time="{{ fail.date | isoformat }}"></span> {% for fail in fails %}
</td> <tr class="chal-wrong" data-href="{{ url_for("admin.challenges_detail", challenge_id=fail.challenge_id) }}">
<td class="text-center"> <td class="border-right" data-checkbox>
<span class="delete-submission" submission-id="{{ fail.id }}" <div class="form-check text-center">
submission-type="{{ fail.type }}" <input type="checkbox" class="form-check-input" value="{{ fail.id }}" data-submission-id="{{ fail.id }}"
submission-challenge="{{ fail.challenge.name }}" data-toggle="tooltip" data-submission-type="{{ fail.type }}"
data-placement="top" title="Delete fail #{{ fail.id }}"> data-submission-challenge="{{ fail.challenge.name }}">&nbsp;
<i class="fas fa-times"></i> </div>
</span> </td>
</td> <td class="text-center chal" id="{{ fail.challenge_id }}">
</tr> <a href="{{ url_for("admin.challenges_detail", challenge_id=fail.challenge_id) }}">
{% endfor %} {{ fail.challenge.name }}
</tbody> </a>
</table> </td>
<td class="text-center">
<a href="{{ url_for("admin.users_detail", user_id=fail.user_id) }}">
{{ fail.user.name }}
</a>
</td>
<td class="flag" id="{{ fail.id }}"><pre>{{ fail.provided }}</pre></td>
<td class="text-center solve-time">
<span data-time="{{ fail.date | isoformat }}"></span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div> </div>
</div> </div>
</div> </div>
<div class="tab-pane fade" id="nav-awards" role="tabpanel" aria-labelledby="nav-awards-tab"> <div class="tab-pane fade" id="nav-awards" role="tabpanel" aria-labelledby="nav-awards-tab">
<h3 class="text-center pt-5 d-block">Awards</h3>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<table class="table table-striped"> <div class="float-right pb-3">
<h3 class="text-center py-3 d-block">Awards</h3> <div class="btn-group" role="group">
<button type="button" class="btn btn-outline-danger" id="awards-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 class="table table-striped border">
<thead> <thead>
<tr> <tr>
<td class="text-center"><b>Name</b></td> <th class="border-right" data-checkbox>
<td class="text-center"><b>User</b></td> <div class="form-check text-center">
<td class="text-center"><b>Description</b></td> <input type="checkbox" class="form-check-input" data-checkbox-all>&nbsp;
<td class="text-center"><b>Date</b></td> </div>
<td class="text-center"><b>Value</b></td> </th>
<td class="text-center"><b>Category</b></td> <th class="sort-col text-center"><b>Name</b></th>
<td class="text-center"><b>Icon</b></td> <th class="sort-col text-center"><b>User</b></th>
<td class="text-center"><b>Delete</b></td> <th class="sort-col text-center"><b>Description</b></th>
<th class="sort-col text-center"><b>Date</b></th>
<th class="sort-col text-center"><b>Value</b></th>
<th class="sort-col text-center"><b>Category</b></th>
<th class="sort-col text-center"><b>Icon</b></th>
</tr> </tr>
</thead> </thead>
<tbody id="awards-body"> <tbody id="awards-body">
{% for award in awards %} {% for award in awards %}
<tr class="award-row"> <tr class="award-row">
<td class="border-right" data-checkbox>
<div class="form-check text-center">
<input type="checkbox" class="form-check-input" value="{{ award.id }}" data-award-id="{{ award.id }}" data-award-name="{{ award.name }}">&nbsp;
</div>
</td>
<td class="text-center chal" id="{{ award.id }}">{{ award.name }}</td> <td class="text-center chal" id="{{ award.id }}">{{ award.name }}</td>
<td class="text-center"> <td class="text-center">
<a href="{{ url_for("admin.users_detail", user_id=award.user_id) }}"> <a href="{{ url_for("admin.users_detail", user_id=award.user_id) }}">
@ -368,14 +428,7 @@
</td> </td>
<td class="text-center">{{ award.value }}</td> <td class="text-center">{{ award.value }}</td>
<td class="text-center">{{ award.category }}</td> <td class="text-center">{{ award.category }}</td>
<td class="text-center">{{ award.icon }}</td> <td class="text-center"><i class="award-icon award-{{ award.icon }}"></i> {{ award.icon }}</td>
<td class="text-center">
<span class="delete-award" award-id="{{ award.id }}" award-name="{{ award.name }}"
data-toggle="tooltip" data-placement="top" title="Delete award #{{ award.id }}">
<i class="fas fa-times"></i>
</span>
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -383,6 +436,58 @@
</div> </div>
</div> </div>
</div> </div>
<div class="tab-pane fade" id="nav-missing" role="tabpanel" aria-labelledby="nav-missing-tab">
<h3 class="text-center pt-5 d-block">Missing</h3>
<div class="row">
<div class="col-md-12">
<div class="float-right pb-3">
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-success" id="missing-solve-button">
<i class="btn-fa fas fa-check"></i>
</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table class="table table-striped border">
<thead>
<tr>
<th class="border-right" data-checkbox>
<div class="form-check text-center">
<input type="checkbox" class="form-check-input" data-checkbox-all>&nbsp;
</div>
</th>
<th class="sort-col text-center"><b>Challenge</b></th>
<th class="sort-col text-center"><b>Category</b></th>
<th class="sort-col text-center"><b>Value</b></th>
</tr>
</thead>
<tbody>
{% for challenge in missing %}
<tr class="chal-solve" data-href="{{ url_for("admin.challenges_detail", challenge_id=challenge.id) }}">
<td class="border-right" data-checkbox>
<div class="form-check text-center">
<input type="checkbox" class="form-check-input" value="{{ challenge.id }}" data-missing-challenge-id="{{ challenge.id }}"
data-missing-challenge-name="{{ challenge.name }}">&nbsp;
</div>
</td>
<td class="text-center chal" id="{{ challenge.id }}">
<a href="{{ url_for("admin.challenges_detail", challenge_id=challenge.id) }}">
{{ challenge.name }}
</a>
</td>
<td class="text-center">{{ challenge.category }}</td>
<td class="text-center">{{ challenge.value }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div> </div>
</div> </div>

View File

@ -195,25 +195,47 @@
aria-controls="nav-missing" aria-selected="false">Missing</a> aria-controls="nav-missing" aria-selected="false">Missing</a>
</nav> </nav>
<div class="tab-content min-vh-25" id="nav-tabContent"> <div class="tab-content min-vh-50" id="nav-tabContent">
<div class="tab-pane fade show active" id="nav-solves" role="tabpanel" aria-labelledby="nav-solves-tab"> <div class="tab-pane fade show active" id="nav-solves" role="tabpanel" aria-labelledby="nav-solves-tab">
<h3 class="text-center pt-5 d-block">Solves</h3>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<table class="table table-striped"> <div class="float-right pb-3">
<h3 class="text-center py-3 d-block">Solves</h3> <div class="btn-group" role="group">
<button type="button" class="btn btn-outline-danger" id="solves-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 class="table table-striped border">
<thead> <thead>
<tr> <tr>
<td class="text-center"><b>Challenge</b></td> <th class="border-right" data-checkbox>
<td class="text-center"><b>Submitted</b></td> <div class="form-check text-center">
<td class="text-center"><b>Category</b></td> <input type="checkbox" class="form-check-input" data-checkbox-all>&nbsp;
<td class="text-center"><b>Value</b></td> </div>
<td class="text-center"><b>Time</b></td> </th>
<td class="text-center"><b>Delete</b></td> <th class="sort-col text-center"><b>Challenge</b></th>
</tr> <th class="sort-col text-center"><b>Submitted</b></th>
<th class="sort-col text-center"><b>Category</b></th>
<th class="sort-col text-center"><b>Value</b></th>
<th class="sort-col text-center"><b>Time</b></th>
</tr>
</thead> </thead>
<tbody> <tbody>
{% for solve in solves %} {% for solve in solves %}
<tr class="chal-solve" data-href="{{ url_for("admin.challenges_detail", challenge_id=solve.challenge_id) }}"> <tr class="chal-solve" data-href="{{ url_for("admin.challenges_detail", challenge_id=solve.challenge_id) }}">
<td class="border-right" data-checkbox>
<div class="form-check text-center">
<input type="checkbox" class="form-check-input" value="{{ solve.id }}" data-submission-id="{{ solve.id }}"
data-submission-type="{{ solve.type }}"
data-submission-challenge="{{ solve.challenge.name }}">&nbsp;
</div>
</td>
<td class="text-center chal" id="{{ solve.challenge_id }}"> <td class="text-center chal" id="{{ solve.challenge_id }}">
<a href="{{ url_for("admin.challenges_detail", challenge_id=solve.challenge_id) }}"> <a href="{{ url_for("admin.challenges_detail", challenge_id=solve.challenge_id) }}">
{{ solve.challenge.name }} {{ solve.challenge.name }}
@ -225,13 +247,6 @@
<td class="text-center solve-time"> <td class="text-center solve-time">
<span data-time="{{ solve.date | isoformat }}"></span> <span data-time="{{ solve.date | isoformat }}"></span>
</td> </td>
<td class="text-center">
<span class="delete-submission" submission-id="{{ solve.id }}"
submission-type="{{ solve.type }}" submission-challenge="{{ solve.challenge.name }}" data-toggle="tooltip"
data-placement="top" title="Delete solve #{{ solve.id }}">
<i class="btn-fa fas fa-times"></i>
</span>
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -241,81 +256,53 @@
</div> </div>
<div class="tab-pane fade" id="nav-wrong" role="tabpanel" aria-labelledby="nav-wrong-tab"> <div class="tab-pane fade" id="nav-wrong" role="tabpanel" aria-labelledby="nav-wrong-tab">
<h3 class="text-center pt-5 d-block">Fails</h3>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<table class="table table-striped"> <div class="float-right pb-3">
<h3 class="text-center py-3 d-block">Fails</h3> <div class="btn-group" role="group">
<thead> <button type="button" class="btn btn-outline-danger" id="fails-delete-button">
<tr> <i class="btn-fa fas fa-trash-alt"></i>
<td class="text-center"><b>Challenge</b></td> </button>
<td class="text-center"><b>Submitted</b></td> </div>
<td class="text-center"><b>Time</b></td> </div>
<td class="text-center"><b>Delete</b></td>
</tr>
</thead>
<tbody>
{% for fail in fails %}
<tr class="chal-wrong" data-href="{{ url_for("admin.challenges_detail", challenge_id=fail.challenge_id) }}">
<td class="text-center chal" id="{{ fail.challenge_id }}">
<a href="{{ url_for("admin.challenges_detail", challenge_id=fail.challenge_id) }}">
{{ fail.challenge.name }}
</a>
</td>
<td class="flag" id="{{ fail.id }}">
<pre>{{ fail.provided }}</pre>
</td>
<td class="text-center solve-time">
<span data-time="{{ fail.date | isoformat }}"></span>
</td>
<td class="text-center">
<a class="delete-submission" submission-id="{{ fail.id }}"
submission-type="{{ fail.type }}" submission-challenge="{{ fail.challenge.name }}" data-toggle="tooltip"
data-placement="top" title="Delete fail #{{ fail.id }}">
<i class="fas fa-times"></i>
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div> </div>
</div> </div>
</div>
<div class="tab-pane fade" id="nav-awards" role="tabpanel" aria-labelledby="nav-awards-tab">
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<table class="table table-striped"> <table class="table table-striped border">
<h3 class="text-center py-3 d-block">Awards</h3>
<thead> <thead>
<tr> <tr>
<td class="text-center"><b>Name</b></td> <th class="border-right" data-checkbox>
<td class="text-center"><b>Description</b></td> <div class="form-check text-center">
<td class="text-center"><b>Date</b></td> <input type="checkbox" class="form-check-input" data-checkbox-all>&nbsp;
<td class="text-center"><b>Value</b></td> </div>
<td class="text-center"><b>Category</b></td> </th>
<td class="text-center"><b>Icon</b></td> <th class="sort-col text-center"><b>Challenge</b></th>
<td class="text-center"><b>Delete</b></td> <th class="sort-col text-center"><b>Submitted</b></th>
</tr> <th class="sort-col text-center"><b>Time</b></th>
</tr>
</thead> </thead>
<tbody id="awards-body"> <tbody>
{% for award in awards %} {% for fail in fails %}
<tr class="award-row"> <tr class="chal-wrong" data-href="{{ url_for("admin.challenges_detail", challenge_id=fail.challenge_id) }}">
<td class="text-center chal" id="{{ award.id }}">{{ award.name }}</td> <td class="border-right" data-checkbox>
<td class=""><pre>{{ award.description }}</pre></td> <div class="form-check text-center">
<td class="text-center solve-time"> <input type="checkbox" class="form-check-input" value="{{ fail.id }}" data-submission-id="{{ fail.id }}"
<span data-time="{{ award.date | isoformat }}"></span> data-submission-type="{{ fail.type }}"
data-submission-challenge="{{ fail.challenge.name }}">&nbsp;
</div>
</td> </td>
<td class="text-center">{{ award.value }}</td> <td class="text-center chal" id="{{ fail.challenge_id }}">
<td class="text-center">{{ award.category }}</td> <a href="{{ url_for("admin.challenges_detail", challenge_id=fail.challenge_id) }}">
<td class="text-center">{{ award.icon }}</td> {{ fail.challenge.name }}
</a>
<td class="text-center"> </td>
<span class="delete-award" data-toggle="tooltip" <td class="flag" id="{{ fail.id }}">
data-placement="top" award-id="{{ award.id }}" award-name="{{ award.name }}" <pre>{{ fail.provided }}</pre>
title="Delete award #{{ award.id }}"> </td>
<i class="fas fa-times"></i> <td class="text-center solve-time">
</span> <span data-time="{{ fail.date | isoformat }}"></span>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
@ -325,22 +312,98 @@
</div> </div>
</div> </div>
<div class="tab-pane fade" id="nav-missing" role="tabpanel" aria-labelledby="nav-missing-tab"> <div class="tab-pane fade" id="nav-awards" role="tabpanel" aria-labelledby="nav-awards-tab">
<h3 class="text-center pt-5 d-block">Awards</h3>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<table class="table table-striped"> <div class="float-right pb-3">
<h3 class="text-center py-3 d-block">Missing</h3> <div class="btn-group" role="group">
<button type="button" class="btn btn-outline-danger" id="awards-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 class="table table-striped border">
<thead>
<tr>
<th class="border-right" data-checkbox>
<div class="form-check text-center">
<input type="checkbox" class="form-check-input" data-checkbox-all>&nbsp;
</div>
</th>
<th class="sort-col text-center"><b>Name</b></th>
<th class="sort-col text-center"><b>Description</b></th>
<th class="sort-col text-center"><b>Date</b></th>
<th class="sort-col text-center"><b>Value</b></th>
<th class="sort-col text-center"><b>Category</b></th>
<th class="sort-col text-center"><b>Icon</b></th>
</tr>
</thead>
<tbody id="awards-body">
{% for award in awards %}
<tr class="award-row">
<td class="border-right" data-checkbox>
<div class="form-check text-center">
<input type="checkbox" class="form-check-input" value="{{ award.id }}" data-award-id="{{ award.id }}" data-award-name="{{ award.name }}">&nbsp;
</div>
</td>
<td class="text-center chal" id="{{ award.id }}">{{ award.name }}</td>
<td class=""><pre>{{ award.description }}</pre></td>
<td class="text-center solve-time">
<span data-time="{{ award.date | isoformat }}"></span>
</td>
<td class="text-center">{{ award.value }}</td>
<td class="text-center">{{ award.category }}</td>
<td class="text-center"> <i class="award-icon award-{{ award.icon }}"></i> {{ award.icon }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<div class="tab-pane fade" id="nav-missing" role="tabpanel" aria-labelledby="nav-missing-tab">
<h3 class="text-center pt-5 d-block">Missing</h3>
<div class="row">
<div class="col-md-12">
<div class="float-right pb-3">
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-success" id="missing-solve-button">
<i class="btn-fa fas fa-check"></i>
</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table class="table table-striped border">
<thead> <thead>
<tr> <tr>
<td class="text-center"><b>Challenge</b></td> <th class="border-right" data-checkbox>
<td class="text-center"><b>Category</b></td> <div class="form-check text-center">
<td class="text-center"><b>Value</b></td> <input type="checkbox" class="form-check-input" data-checkbox-all>&nbsp;
<td class="text-center"><b>Mark Solved</b></td> </div>
</th>
<th class="sort-col text-center"><b>Challenge</b></th>
<th class="sort-col text-center"><b>Category</b></th>
<th class="sort-col text-center"><b>Value</b></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for challenge in missing %} {% for challenge in missing %}
<tr class="chal-solve" data-href="{{ url_for("admin.challenges_detail", challenge_id=challenge.id) }}"> <tr class="chal-solve" data-href="{{ url_for("admin.challenges_detail", challenge_id=challenge.id) }}">
<td class="border-right" data-checkbox>
<div class="form-check text-center">
<input type="checkbox" class="form-check-input" value="{{ challenge.id }}" data-missing-challenge-id="{{ challenge.id }}"
data-missing-challenge-name="{{ challenge.name }}">&nbsp;
</div>
</td>
<td class="text-center chal" id="{{ challenge.id }}"> <td class="text-center chal" id="{{ challenge.id }}">
<a href="{{ url_for("admin.challenges_detail", challenge_id=challenge.id) }}"> <a href="{{ url_for("admin.challenges_detail", challenge_id=challenge.id) }}">
{{ challenge.name }} {{ challenge.name }}
@ -348,13 +411,6 @@
</td> </td>
<td class="text-center">{{ challenge.category }}</td> <td class="text-center">{{ challenge.category }}</td>
<td class="text-center">{{ challenge.value }}</td> <td class="text-center">{{ challenge.value }}</td>
<td class="text-center">
<a class="correct-submission" data-toggle="tooltip" challenge-id="{{ challenge.id }}"
challenge-name="{{ challenge.name }}"
data-placement="top" title="Mark {{ challenge.name }} correct for {{ user.name }}">
<i class="fas fa-check"></i>
</a>
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>