More smoothness and persistence
This commit is contained in:
1 parent
f924016478
commit
aeb9173825
10 files changed
+2017
-1680
No files matched your search
+564
-537
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,87 @@
|
|||||||
|
import pathlib
|
||||||
|
|
||||||
|
def is_item_ignored(item_name, ignored_directories_list, ignored_extensions_list):
|
||||||
|
if item_name in ignored_directories_list:
|
||||||
|
return True
|
||||||
|
for extension_string in ignored_extensions_list:
|
||||||
|
if item_name.endswith(extension_string):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def format_tree_line(entry_name, prefix_string, is_last_element):
|
||||||
|
connector_string = '└── ' if is_last_element else '├── '
|
||||||
|
return prefix_string + connector_string + entry_name
|
||||||
|
|
||||||
|
def format_file_section(relative_path_string, file_content_string):
|
||||||
|
header_string = '\n--- FILE START: ' + relative_path_string + ' ---\n'
|
||||||
|
footer_string = '\n--- FILE END: ' + relative_path_string + ' ---\n'
|
||||||
|
return header_string + file_content_string + footer_string
|
||||||
|
|
||||||
|
def build_final_output_string(tree_lines_list, code_contents_list):
|
||||||
|
tree_string = '\n'.join(tree_lines_list)
|
||||||
|
code_string = ''.join(code_contents_list)
|
||||||
|
return 'PROJECT STRUCTURE:\n' + tree_string + '\n\n' + code_string
|
||||||
|
|
||||||
|
def collect_file_system_nodes(root_path, ignored_directories_list, ignored_extensions_list):
|
||||||
|
valid_nodes_list = []
|
||||||
|
try:
|
||||||
|
directory_items_list = list(root_path.iterdir())
|
||||||
|
except PermissionError:
|
||||||
|
return valid_nodes_list
|
||||||
|
|
||||||
|
for item_path in directory_items_list:
|
||||||
|
if not is_item_ignored(item_path.name, ignored_directories_list, ignored_extensions_list):
|
||||||
|
valid_nodes_list.append(item_path)
|
||||||
|
|
||||||
|
valid_nodes_list.sort(key=lambda path_object: (not path_object.is_dir(), path_object.name.lower()))
|
||||||
|
return valid_nodes_list
|
||||||
|
|
||||||
|
def read_file_content_safely(file_path_object):
|
||||||
|
try:
|
||||||
|
return file_path_object.read_text(encoding='utf-8')
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
return '[BINARY_OR_UNSUPPORTED_ENCODING]'
|
||||||
|
except PermissionError:
|
||||||
|
return '[PERMISSION_DENIED]'
|
||||||
|
|
||||||
|
def write_output_file(output_file_path, content_string):
|
||||||
|
output_file_path.write_text(content_string, encoding='utf-8')
|
||||||
|
|
||||||
|
def process_directory_recursively(current_path, base_directory_path, prefix_string, ignored_directories_list, ignored_extensions_list, tree_lines_list, code_contents_list):
|
||||||
|
nodes_list = collect_file_system_nodes(current_path, ignored_directories_list, ignored_extensions_list)
|
||||||
|
total_nodes_count = len(nodes_list)
|
||||||
|
|
||||||
|
for index_number, node_path in enumerate(nodes_list):
|
||||||
|
is_last_node = index_number == total_nodes_count - 1
|
||||||
|
formatted_tree_line = format_tree_line(node_path.name, prefix_string, is_last_node)
|
||||||
|
tree_lines_list.append(formatted_tree_line)
|
||||||
|
|
||||||
|
if node_path.is_dir():
|
||||||
|
new_prefix_string = prefix_string + (' ' if is_last_node else '│ ')
|
||||||
|
process_directory_recursively(node_path, base_directory_path, new_prefix_string, ignored_directories_list, ignored_extensions_list, tree_lines_list, code_contents_list)
|
||||||
|
else:
|
||||||
|
relative_file_path_string = str(node_path.relative_to(base_directory_path))
|
||||||
|
file_content_string = read_file_content_safely(node_path)
|
||||||
|
formatted_file_section = format_file_section(relative_file_path_string, file_content_string)
|
||||||
|
code_contents_list.append(formatted_file_section)
|
||||||
|
|
||||||
|
def execute_bundler_script():
|
||||||
|
target_output_file_name_string = 'output_fuer_ki.txt'
|
||||||
|
ignored_directories_list = ['.git', '__pycache__', 'venv', 'env', '.venv', 'node_modules', '.idea', '.vscode', 'instance']
|
||||||
|
ignored_extensions_list = ['.pyc', '.sqlite', '.db', '.png', '.jpg', '.jpeg', '.svg', '.ico', '.exe', '.pdf', '.zip', '.tar.gz', '.mp3', '.mp4', target_output_file_name_string]
|
||||||
|
|
||||||
|
current_working_directory_path = pathlib.Path.cwd()
|
||||||
|
output_file_path = current_working_directory_path / target_output_file_name_string
|
||||||
|
|
||||||
|
tree_lines_list = [current_working_directory_path.name]
|
||||||
|
code_contents_list = []
|
||||||
|
|
||||||
|
process_directory_recursively(current_working_directory_path, current_working_directory_path, '', ignored_directories_list, ignored_extensions_list, tree_lines_list, code_contents_list)
|
||||||
|
|
||||||
|
final_output_string = build_final_output_string(tree_lines_list, code_contents_list)
|
||||||
|
write_output_file(output_file_path, final_output_string)
|
||||||
|
|
||||||
|
print('Erfolg! Die Datei ' + target_output_file_name_string + ' wurde generiert.')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
execute_bundler_script()
|
||||||
Binary file not shown.
+381
-296
@@ -1,330 +1,415 @@
|
|||||||
let websocket_connection;
|
const application_state = {
|
||||||
let pending_administrator_form = null;
|
websocket_connection: null,
|
||||||
let stopwatch_interval_identifier = null;
|
pending_administrator_form: null,
|
||||||
|
stopwatch_interval_identifier: null
|
||||||
|
};
|
||||||
|
|
||||||
function initialize_lucide_icons() {
|
/* --- Deterministische Funktionen ("ehrliche" Funktionen) --- */
|
||||||
if (typeof lucide !== "undefined") {
|
|
||||||
lucide.createIcons();
|
function calculate_time_difference(start_time_milliseconds, current_time_milliseconds) {
|
||||||
}
|
return current_time_milliseconds - start_time_milliseconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
function update_game_stopwatch() {
|
function format_stopwatch_string(difference_in_milliseconds) {
|
||||||
const stopwatch_element = document.getElementById("game-stopwatch");
|
if (difference_in_milliseconds < 0) {
|
||||||
if (!stopwatch_element) {
|
return '00:00 Minuten';
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const start_time_string = stopwatch_element.getAttribute("data-start-time");
|
const total_seconds = Math.floor(difference_in_milliseconds / 1000);
|
||||||
if (!start_time_string) {
|
const days_passed = Math.floor(total_seconds / 86400);
|
||||||
return;
|
const hours_passed = Math.floor((total_seconds % 86400) / 3600);
|
||||||
}
|
const minutes_passed = Math.floor((total_seconds % 3600) / 60);
|
||||||
|
const seconds_passed = total_seconds % 60;
|
||||||
|
|
||||||
const start_date = new Date(start_time_string);
|
let output_string = '';
|
||||||
if (isNaN(start_date.getTime())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const current_date = new Date();
|
if (days_passed === 1) {
|
||||||
const difference_in_milliseconds = current_date.getTime() - start_date.getTime();
|
output_string += '1 Tag und ';
|
||||||
|
} else if (days_passed > 1) {
|
||||||
|
output_string += days_passed + ' Tage und ';
|
||||||
|
}
|
||||||
|
|
||||||
if (difference_in_milliseconds < 0) {
|
const padded_minutes = String(minutes_passed).padStart(2, '0');
|
||||||
stopwatch_element.textContent = "00:00 Minuten";
|
const padded_seconds = String(seconds_passed).padStart(2, '0');
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const total_seconds = Math.floor(difference_in_milliseconds / 1000);
|
if (hours_passed === 1) {
|
||||||
const days_passed = Math.floor(total_seconds / 86400);
|
output_string += '1:' + padded_minutes + ':' + padded_seconds + ' Stunde';
|
||||||
const hours_passed = Math.floor((total_seconds % 86400) / 3600);
|
} else if (hours_passed > 1) {
|
||||||
const minutes_passed = Math.floor((total_seconds % 3600) / 60);
|
output_string += hours_passed + ':' + padded_minutes + ':' + padded_seconds + ' Stunden';
|
||||||
const seconds_passed = total_seconds % 60;
|
} else {
|
||||||
|
output_string += padded_minutes + ':' + padded_seconds + ' Minuten';
|
||||||
|
}
|
||||||
|
|
||||||
let output_string = "";
|
return output_string;
|
||||||
|
|
||||||
if (days_passed === 1) {
|
|
||||||
output_string += "1 Tag und ";
|
|
||||||
} else if (days_passed > 1) {
|
|
||||||
output_string += days_passed + " Tage und ";
|
|
||||||
}
|
|
||||||
|
|
||||||
const padded_minutes = String(minutes_passed).padStart(2, "0");
|
|
||||||
const padded_seconds = String(seconds_passed).padStart(2, "0");
|
|
||||||
|
|
||||||
if (hours_passed === 1) {
|
|
||||||
output_string += "1:" + padded_minutes + ":" + padded_seconds + " Stunde";
|
|
||||||
} else if (hours_passed > 1) {
|
|
||||||
output_string += hours_passed + ":" + padded_minutes + ":" + padded_seconds + " Stunden";
|
|
||||||
} else {
|
|
||||||
output_string += padded_minutes + ":" + padded_seconds + " Minuten";
|
|
||||||
}
|
|
||||||
|
|
||||||
stopwatch_element.textContent = output_string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function initialize_application_behavior() {
|
function synchronize_element_nodes(current_node, updated_node) {
|
||||||
initialize_lucide_icons();
|
if (current_node.nodeType !== updated_node.nodeType) {
|
||||||
|
current_node.replaceWith(updated_node.cloneNode(true));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (current_node.nodeType === Node.TEXT_NODE) {
|
||||||
|
if (current_node.nodeValue !== updated_node.nodeValue) {
|
||||||
|
current_node.nodeValue = updated_node.nodeValue;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (current_node.nodeType !== Node.ELEMENT_NODE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (stopwatch_interval_identifier) {
|
if (current_node.tagName !== updated_node.tagName) {
|
||||||
clearInterval(stopwatch_interval_identifier);
|
current_node.replaceWith(updated_node.cloneNode(true));
|
||||||
}
|
return;
|
||||||
stopwatch_interval_identifier = setInterval(update_game_stopwatch, 1000);
|
}
|
||||||
update_game_stopwatch();
|
|
||||||
|
|
||||||
const tab_buttons = document.querySelectorAll(".tab-button");
|
if (current_node.namespaceURI !== updated_node.namespaceURI) {
|
||||||
tab_buttons.forEach(function(current_button) {
|
current_node.replaceWith(updated_node.cloneNode(true));
|
||||||
current_button.addEventListener("click", function() {
|
return;
|
||||||
const target_identifier = current_button.getAttribute("data-target");
|
}
|
||||||
const parent_container = current_button.closest(".application-content");
|
|
||||||
|
|
||||||
parent_container.querySelectorAll(".tab-button").forEach(function(button_element) {
|
const is_active_input = document.activeElement === current_node;
|
||||||
button_element.classList.remove("active-tab");
|
const is_input_element = ['INPUT', 'TEXTAREA', 'SELECT'].includes(current_node.tagName);
|
||||||
});
|
|
||||||
parent_container.querySelectorAll(".tab-content").forEach(function(content_element) {
|
|
||||||
content_element.classList.remove("active-content");
|
|
||||||
});
|
|
||||||
|
|
||||||
current_button.classList.add("active-tab");
|
if (is_input_element && !is_active_input) {
|
||||||
const target_panel = document.getElementById(target_identifier);
|
if (current_node.value !== updated_node.value) {
|
||||||
if (target_panel) {
|
current_node.value = updated_node.value;
|
||||||
target_panel.classList.add("active-content");
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const player_forms = document.querySelectorAll(".asynchronous-form");
|
const current_attributes = current_node.attributes;
|
||||||
player_forms.forEach(function(current_form) {
|
const updated_attributes = updated_node.attributes;
|
||||||
current_form.addEventListener("submit", function(form_submission_event) {
|
|
||||||
form_submission_event.preventDefault();
|
|
||||||
execute_form_submission(current_form);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const administrator_forms = document.querySelectorAll(".admin-asynchronous-form");
|
for (let index = 0; index < updated_attributes.length; index++) {
|
||||||
administrator_forms.forEach(function(current_form) {
|
const attribute = updated_attributes[index];
|
||||||
current_form.addEventListener("submit", function(form_submission_event) {
|
if (current_node.getAttribute(attribute.name) !== attribute.value) {
|
||||||
form_submission_event.preventDefault();
|
if (!(is_input_element && is_active_input && attribute.name === 'value')) {
|
||||||
pending_administrator_form = current_form;
|
current_node.setAttribute(attribute.name, attribute.value);
|
||||||
const administrator_modal = document.getElementById("admin-confirmation-modal");
|
}
|
||||||
if (administrator_modal) {
|
}
|
||||||
const time_input = document.getElementById("modal_execution_time");
|
}
|
||||||
if (time_input) {
|
|
||||||
time_input.value = "";
|
|
||||||
}
|
|
||||||
administrator_modal.classList.add("active-modal");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const confirm_administrator_button = document.getElementById("confirm-admin-action");
|
for (let index = current_attributes.length - 1; index >= 0; index--) {
|
||||||
if (confirm_administrator_button) {
|
const attribute = current_attributes[index];
|
||||||
confirm_administrator_button.addEventListener("click", function() {
|
if (!updated_node.hasAttribute(attribute.name)) {
|
||||||
if (pending_administrator_form) {
|
current_node.removeAttribute(attribute.name);
|
||||||
const time_input = document.getElementById("modal_execution_time");
|
}
|
||||||
if (time_input && time_input.value) {
|
}
|
||||||
let hidden_time_input = pending_administrator_form.querySelector("input[name='execution_time']");
|
|
||||||
if (!hidden_time_input) {
|
|
||||||
hidden_time_input = document.createElement("input");
|
|
||||||
hidden_time_input.type = "hidden";
|
|
||||||
hidden_time_input.name = "execution_time";
|
|
||||||
pending_administrator_form.appendChild(hidden_time_input);
|
|
||||||
}
|
|
||||||
hidden_time_input.value = time_input.value;
|
|
||||||
} else {
|
|
||||||
let hidden_time_input = pending_administrator_form.querySelector("input[name='execution_time']");
|
|
||||||
if (hidden_time_input) {
|
|
||||||
hidden_time_input.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
execute_form_submission(pending_administrator_form);
|
|
||||||
pending_administrator_form = null;
|
|
||||||
const administrator_modal = document.getElementById("admin-confirmation-modal");
|
|
||||||
if (administrator_modal) {
|
|
||||||
administrator_modal.classList.remove("active-modal");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const cancel_administrator_button = document.getElementById("cancel-admin-action");
|
const current_children = Array.from(current_node.childNodes);
|
||||||
if (cancel_administrator_button) {
|
const updated_children = Array.from(updated_node.childNodes);
|
||||||
cancel_administrator_button.addEventListener("click", function() {
|
const max_length = Math.max(current_children.length, updated_children.length);
|
||||||
pending_administrator_form = null;
|
|
||||||
const administrator_modal = document.getElementById("admin-confirmation-modal");
|
|
||||||
if (administrator_modal) {
|
|
||||||
administrator_modal.classList.remove("active-modal");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const trigger_elimination_button = document.getElementById("trigger-elimination");
|
for (let index = 0; index < max_length; index++) {
|
||||||
if (trigger_elimination_button) {
|
const current_child = current_children[index];
|
||||||
trigger_elimination_button.addEventListener("click", function() {
|
const updated_child = updated_children[index];
|
||||||
const elimination_modal = document.getElementById("elimination-confirm-modal");
|
|
||||||
if (elimination_modal) {
|
|
||||||
elimination_modal.classList.add("active-modal");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const proceed_elimination_button = document.getElementById("proceed-elimination");
|
if (!current_child && updated_child) {
|
||||||
if (proceed_elimination_button) {
|
current_node.appendChild(updated_child.cloneNode(true));
|
||||||
proceed_elimination_button.addEventListener("click", function() {
|
} else if (current_child && !updated_child) {
|
||||||
websocket_connection.emit("request_elimination");
|
current_node.removeChild(current_child);
|
||||||
const elimination_modal = document.getElementById("elimination-confirm-modal");
|
} else if (current_child && updated_child) {
|
||||||
if (elimination_modal) {
|
synchronize_element_nodes(current_child, updated_child);
|
||||||
elimination_modal.classList.remove("active-modal");
|
}
|
||||||
}
|
}
|
||||||
const waiting_modal = document.getElementById("waiting-modal");
|
|
||||||
if (waiting_modal) {
|
|
||||||
waiting_modal.classList.add("active-modal");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const cancel_elimination_button = document.getElementById("cancel-elimination");
|
|
||||||
if (cancel_elimination_button) {
|
|
||||||
cancel_elimination_button.addEventListener("click", function() {
|
|
||||||
const elimination_modal = document.getElementById("elimination-confirm-modal");
|
|
||||||
if (elimination_modal) {
|
|
||||||
elimination_modal.classList.remove("active-modal");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const confirm_button = document.getElementById("confirm-elimination");
|
|
||||||
if (confirm_button) {
|
|
||||||
confirm_button.addEventListener("click", function() {
|
|
||||||
websocket_connection.emit("confirm_elimination");
|
|
||||||
const modal_element = document.getElementById("verification-modal");
|
|
||||||
if (modal_element) {
|
|
||||||
modal_element.classList.remove("active-modal");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const reject_button = document.getElementById("reject-elimination");
|
|
||||||
if (reject_button) {
|
|
||||||
reject_button.addEventListener("click", function() {
|
|
||||||
websocket_connection.emit("reject_elimination");
|
|
||||||
const modal_element = document.getElementById("verification-modal");
|
|
||||||
if (modal_element) {
|
|
||||||
modal_element.classList.remove("active-modal");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const acknowledge_rejection_button = document.getElementById("acknowledge-rejection");
|
|
||||||
if (acknowledge_rejection_button) {
|
|
||||||
acknowledge_rejection_button.addEventListener("click", function() {
|
|
||||||
const rejection_modal = document.getElementById("rejection-modal");
|
|
||||||
if (rejection_modal) {
|
|
||||||
rejection_modal.classList.remove("active-modal");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const open_name_modal_button = document.getElementById("trigger-name-change-modal");
|
|
||||||
if (open_name_modal_button) {
|
|
||||||
open_name_modal_button.addEventListener("click", function() {
|
|
||||||
const name_modal = document.getElementById("name-change-modal");
|
|
||||||
if (name_modal) {
|
|
||||||
name_modal.classList.add("active-modal");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const close_name_modal_button = document.getElementById("cancel-name-change");
|
|
||||||
if (close_name_modal_button) {
|
|
||||||
close_name_modal_button.addEventListener("click", function() {
|
|
||||||
const name_modal = document.getElementById("name-change-modal");
|
|
||||||
if (name_modal) {
|
|
||||||
name_modal.classList.remove("active-modal");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function execute_form_submission(target_form) {
|
/* --- Architektur & State-Management ("unehrliche" Funktionen) --- */
|
||||||
const active_tab_element = document.querySelector(".active-tab");
|
|
||||||
let active_target = "control-panel";
|
|
||||||
if (active_tab_element) {
|
|
||||||
active_target = active_tab_element.getAttribute("data-target");
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch(target_form.action, {
|
function trigger_dom_update(markup_text) {
|
||||||
method: target_form.method,
|
const document_parser = new DOMParser();
|
||||||
body: new FormData(target_form)
|
const parsed_document = document_parser.parseFromString(markup_text, 'text/html');
|
||||||
})
|
const current_container = document.querySelector('.application-content');
|
||||||
.then(function(network_response) { return network_response.text(); })
|
const new_container = parsed_document.querySelector('.application-content');
|
||||||
.then(function(markup_text) {
|
|
||||||
replace_main_content(markup_text);
|
if (!current_container || !new_container) {
|
||||||
const new_tab_button = document.querySelector(".tab-button[data-target='" + active_target + "']");
|
return;
|
||||||
if (new_tab_button) {
|
}
|
||||||
new_tab_button.click();
|
|
||||||
}
|
const current_active_tab = document.querySelector('.active-tab');
|
||||||
});
|
const active_tab_target = current_active_tab ? current_active_tab.getAttribute('data-target') : null;
|
||||||
|
|
||||||
|
const active_modals = [];
|
||||||
|
document.querySelectorAll('.active-modal').forEach(function(modal) {
|
||||||
|
active_modals.push(modal.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronize_element_nodes(current_container, new_container);
|
||||||
|
|
||||||
|
if (active_tab_target) {
|
||||||
|
document.querySelectorAll('.tab-button').forEach(function(button) { button.classList.remove('active-tab'); });
|
||||||
|
document.querySelectorAll('.tab-content').forEach(function(content) { content.classList.remove('active-content'); });
|
||||||
|
|
||||||
|
const restored_tab = document.querySelector('.tab-button[data-target="' + active_tab_target + '"]');
|
||||||
|
const restored_content = document.getElementById(active_tab_target);
|
||||||
|
if (restored_tab && restored_content) {
|
||||||
|
restored_tab.classList.add('active-tab');
|
||||||
|
restored_content.classList.add('active-content');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
active_modals.forEach(function(modal_identifier) {
|
||||||
|
const restored_modal = document.getElementById(modal_identifier);
|
||||||
|
if (restored_modal) {
|
||||||
|
restored_modal.classList.add('active-modal');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (typeof lucide !== 'undefined') {
|
||||||
|
lucide.createIcons();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function replace_main_content(markup_text) {
|
function fetch_and_synchronize_dom() {
|
||||||
const document_parser = new DOMParser();
|
fetch(window.location.href)
|
||||||
const parsed_document = document_parser.parseFromString(markup_text, "text/html");
|
.then(function(network_response) {
|
||||||
const current_container = document.querySelector(".application-content");
|
if (!network_response.ok) {
|
||||||
const new_container = parsed_document.querySelector(".application-content");
|
throw new Error('Network response failure');
|
||||||
if (current_container && new_container) {
|
}
|
||||||
current_container.innerHTML = new_container.innerHTML;
|
return network_response.text();
|
||||||
initialize_application_behavior();
|
})
|
||||||
}
|
.then(function(markup_text) {
|
||||||
|
trigger_dom_update(markup_text);
|
||||||
|
})
|
||||||
|
.catch(function(error_object) {
|
||||||
|
console.error('DOM Synchronization failed:', error_object);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
function execute_asynchronous_form_submission(target_form) {
|
||||||
if ("serviceWorker" in navigator) {
|
fetch(target_form.action, {
|
||||||
navigator.serviceWorker.register("/service_worker.js");
|
method: target_form.method,
|
||||||
}
|
body: new FormData(target_form)
|
||||||
|
})
|
||||||
|
.then(function(network_response) {
|
||||||
|
if (!network_response.ok) {
|
||||||
|
throw new Error('Form submission failure');
|
||||||
|
}
|
||||||
|
return network_response.text();
|
||||||
|
})
|
||||||
|
.then(function(markup_text) {
|
||||||
|
trigger_dom_update(markup_text);
|
||||||
|
})
|
||||||
|
.catch(function(error_object) {
|
||||||
|
console.error('Form execution failed:', error_object);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof io !== "undefined") {
|
function update_game_stopwatch_ui() {
|
||||||
websocket_connection = io();
|
const stopwatch_element = document.getElementById('game-stopwatch');
|
||||||
|
if (!stopwatch_element) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
websocket_connection.on("game_state_changed", function() {
|
const start_time_string = stopwatch_element.getAttribute('data-start-time');
|
||||||
if (document.querySelector(".game-section") || document.querySelector(".administrator-section")) {
|
if (!start_time_string) {
|
||||||
fetch(window.location.href)
|
return;
|
||||||
.then(function(network_response) { return network_response.text(); })
|
}
|
||||||
.then(function(markup_text) {
|
|
||||||
replace_main_content(markup_text);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
websocket_connection.on("admin_state_changed", function() {
|
const start_date = new Date(start_time_string);
|
||||||
if (document.querySelector(".administrator-section")) {
|
if (isNaN(start_date.getTime())) {
|
||||||
fetch(window.location.href)
|
return;
|
||||||
.then(function(network_response) { return network_response.text(); })
|
}
|
||||||
.then(function(markup_text) {
|
|
||||||
replace_main_content(markup_text);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
websocket_connection.on("elimination_verification_requested", function() {
|
const current_date = new Date();
|
||||||
fetch(window.location.href)
|
const difference = calculate_time_difference(start_date.getTime(), current_date.getTime());
|
||||||
.then(function(network_response) { return network_response.text(); })
|
stopwatch_element.textContent = format_stopwatch_string(difference);
|
||||||
.then(function(markup_text) {
|
}
|
||||||
replace_main_content(markup_text);
|
|
||||||
const new_modal_element = document.getElementById("verification-modal");
|
|
||||||
if (new_modal_element) {
|
|
||||||
new_modal_element.classList.add("active-modal");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
websocket_connection.on("elimination_rejected", function() {
|
function modify_modal_state(modal_identifier, is_active) {
|
||||||
const waiting_modal = document.getElementById("waiting-modal");
|
const modal = document.getElementById(modal_identifier);
|
||||||
if (waiting_modal) {
|
if (modal) {
|
||||||
waiting_modal.classList.remove("active-modal");
|
if (is_active) {
|
||||||
}
|
modal.classList.add('active-modal');
|
||||||
const rejection_modal = document.getElementById("rejection-modal");
|
} else {
|
||||||
if (rejection_modal) {
|
modal.classList.remove('active-modal');
|
||||||
rejection_modal.classList.add("active-modal");
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
|
||||||
initialize_application_behavior();
|
function write_text_to_clipboard(text_payload) {
|
||||||
|
if (!navigator.clipboard) {
|
||||||
|
console.error('Clipboard API unvailable.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
navigator.clipboard.writeText(text_payload).catch(function(error_object) {
|
||||||
|
console.error('Clipboard action failed:', error_object);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Globale Event-Delegation --- */
|
||||||
|
|
||||||
|
function handle_global_clicks(mouse_event) {
|
||||||
|
const target_element = mouse_event.target;
|
||||||
|
|
||||||
|
const tab_button = target_element.closest('.tab-button');
|
||||||
|
if (tab_button) {
|
||||||
|
const target_identifier = tab_button.getAttribute('data-target');
|
||||||
|
const parent_container = tab_button.closest('.application-content');
|
||||||
|
|
||||||
|
parent_container.querySelectorAll('.tab-button').forEach(function(button) {
|
||||||
|
button.classList.remove('active-tab');
|
||||||
|
});
|
||||||
|
parent_container.querySelectorAll('.tab-content').forEach(function(content) {
|
||||||
|
content.classList.remove('active-content');
|
||||||
|
});
|
||||||
|
|
||||||
|
tab_button.classList.add('active-tab');
|
||||||
|
const target_panel = document.getElementById(target_identifier);
|
||||||
|
if (target_panel) {
|
||||||
|
target_panel.classList.add('active-content');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const action_mapping = {
|
||||||
|
'trigger-elimination': function() { modify_modal_state('elimination-confirm-modal', true); },
|
||||||
|
'cancel-elimination': function() { modify_modal_state('elimination-confirm-modal', false); },
|
||||||
|
'proceed-elimination': function() {
|
||||||
|
if (application_state.websocket_connection) {
|
||||||
|
application_state.websocket_connection.emit('request_elimination');
|
||||||
|
}
|
||||||
|
modify_modal_state('elimination-confirm-modal', false);
|
||||||
|
},
|
||||||
|
'trigger-revoke-elimination': function() {
|
||||||
|
if (application_state.websocket_connection) {
|
||||||
|
application_state.websocket_connection.emit('revoke_elimination_request');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'confirm-elimination': function() {
|
||||||
|
if (application_state.websocket_connection) {
|
||||||
|
application_state.websocket_connection.emit('confirm_elimination');
|
||||||
|
}
|
||||||
|
modify_modal_state('verification-modal', false);
|
||||||
|
},
|
||||||
|
'reject-elimination': function() {
|
||||||
|
if (application_state.websocket_connection) {
|
||||||
|
application_state.websocket_connection.emit('reject_elimination');
|
||||||
|
}
|
||||||
|
modify_modal_state('verification-modal', false);
|
||||||
|
},
|
||||||
|
'acknowledge-rejection': function() { modify_modal_state('rejection-modal', false); },
|
||||||
|
'trigger-name-change-modal': function() { modify_modal_state('name-change-modal', true); },
|
||||||
|
'cancel-name-change-modal': function() { modify_modal_state('name-change-modal', false); },
|
||||||
|
'trigger-help-modal': function() { modify_modal_state('help-modal', true); },
|
||||||
|
'close-help-modal': function() { modify_modal_state('help-modal', false); },
|
||||||
|
'copy-identifier-button': function(button_element) {
|
||||||
|
const identifier = button_element.getAttribute('data-identifier');
|
||||||
|
write_text_to_clipboard(identifier);
|
||||||
|
const icon = button_element.querySelector('i, svg');
|
||||||
|
if (icon) {
|
||||||
|
icon.setAttribute('data-lucide', 'check');
|
||||||
|
if (typeof lucide !== 'undefined') {
|
||||||
|
lucide.createIcons();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'confirm-admin-action': function() {
|
||||||
|
if (application_state.pending_administrator_form) {
|
||||||
|
const time_input = document.getElementById('modal_execution_time');
|
||||||
|
if (time_input && time_input.value) {
|
||||||
|
let hidden_input = application_state.pending_administrator_form.querySelector('input[name="execution_time"]');
|
||||||
|
if (!hidden_input) {
|
||||||
|
hidden_input = document.createElement('input');
|
||||||
|
hidden_input.type = 'hidden';
|
||||||
|
hidden_input.name = 'execution_time';
|
||||||
|
application_state.pending_administrator_form.appendChild(hidden_input);
|
||||||
|
}
|
||||||
|
hidden_input.value = time_input.value;
|
||||||
|
} else {
|
||||||
|
const hidden_input = application_state.pending_administrator_form.querySelector('input[name="execution_time"]');
|
||||||
|
if (hidden_input) {
|
||||||
|
hidden_input.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
execute_asynchronous_form_submission(application_state.pending_administrator_form);
|
||||||
|
application_state.pending_administrator_form = null;
|
||||||
|
modify_modal_state('admin-confirmation-modal', false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'cancel-admin-action': function() {
|
||||||
|
application_state.pending_administrator_form = null;
|
||||||
|
modify_modal_state('admin-confirmation-modal', false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const [identifier, action_function] of Object.entries(action_mapping)) {
|
||||||
|
const button = target_element.closest('#' + identifier);
|
||||||
|
if (button) {
|
||||||
|
action_function(button);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_global_submits(submit_event) {
|
||||||
|
const target_form = submit_event.target;
|
||||||
|
|
||||||
|
if (target_form.classList.contains('asynchronous-form')) {
|
||||||
|
submit_event.preventDefault();
|
||||||
|
execute_asynchronous_form_submission(target_form);
|
||||||
|
} else if (target_form.classList.contains('admin-asynchronous-form')) {
|
||||||
|
submit_event.preventDefault();
|
||||||
|
application_state.pending_administrator_form = target_form;
|
||||||
|
const time_input = document.getElementById('modal_execution_time');
|
||||||
|
if (time_input) {
|
||||||
|
time_input.value = '';
|
||||||
|
}
|
||||||
|
modify_modal_state('admin-confirmation-modal', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Initialisierung --- */
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
navigator.serviceWorker.register('/service_worker.js').catch(function(error_object) {
|
||||||
|
console.error('Service Worker Registration failed:', error_object);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof lucide !== 'undefined') {
|
||||||
|
lucide.createIcons();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (application_state.stopwatch_interval_identifier) {
|
||||||
|
clearInterval(application_state.stopwatch_interval_identifier);
|
||||||
|
}
|
||||||
|
application_state.stopwatch_interval_identifier = setInterval(update_game_stopwatch_ui, 1000);
|
||||||
|
update_game_stopwatch_ui();
|
||||||
|
|
||||||
|
document.addEventListener('click', handle_global_clicks);
|
||||||
|
document.addEventListener('submit', handle_global_submits);
|
||||||
|
|
||||||
|
if (typeof io !== 'undefined') {
|
||||||
|
application_state.websocket_connection = io();
|
||||||
|
|
||||||
|
application_state.websocket_connection.on('game_state_changed', function() {
|
||||||
|
if (document.querySelector('.game-section') || document.querySelector('.administrator-section')) {
|
||||||
|
fetch_and_synchronize_dom();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
application_state.websocket_connection.on('admin_state_changed', function() {
|
||||||
|
if (document.querySelector('.administrator-section')) {
|
||||||
|
fetch_and_synchronize_dom();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
application_state.websocket_connection.on('elimination_verification_requested', function() {
|
||||||
|
fetch_and_synchronize_dom();
|
||||||
|
setTimeout(function() {
|
||||||
|
modify_modal_state('verification-modal', true);
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
|
||||||
|
application_state.websocket_connection.on('elimination_rejected', function() {
|
||||||
|
modify_modal_state('rejection-modal', true);
|
||||||
|
});
|
||||||
|
|
||||||
|
application_state.websocket_connection.on('elimination_revoked', function() {
|
||||||
|
modify_modal_state('verification-modal', false);
|
||||||
|
fetch_and_synchronize_dom();
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
+9
-2
@@ -1,2 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="465 137 354 385" width="354" height="385">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" overflow="hidden" viewBox="465.8766784667969 137.75999450683594 353.4313049316406 384.6400146484375"><path d="M811.26 275.62 819.31 380.82 714.1 388.88 712.67 370.11 685.88 388.45 696.13 522.4 612.86 522.4 603.1 394.79 564.72 376.36 561.05 382.31C552.25 394.18 538.55 402.31 522.66 403.53 493.61 405.75 468.26 384 466.03 354.95 463.81 325.9 485.56 300.55 514.61 298.32 536.4 296.65 556.11 308.47 565.54 326.74L567.55 331.82 708.91 321 706.05 283.67ZM659.98 137.76 690.31 188.35 668.03 242.97 658.84 243.67 663.52 304.85 612.47 308.76 607.79 247.58 598.6 248.28 568.27 197.69 590.54 143.07Z" fill="oklch(85% 0.15 110)"/></svg>
|
<style>
|
||||||
|
path {
|
||||||
|
fill: hsl(60 60% 60%);
|
||||||
|
fill: oklch(85% 0.15 110);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<path d="M811.26 275.62 819.31 380.82 714.1 388.88 712.67 370.11 685.88 388.45 696.13 522.4 612.86 522.4 603.1 394.79 564.72 376.36 561.05 382.31C552.25 394.18 538.55 402.31 522.66 403.53 493.61 405.75 468.26 384 466.03 354.95 463.81 325.9 485.56 300.55 514.61 298.32 536.4 296.65 556.11 308.47 565.54 326.74L567.55 331.82 708.91 321 706.05 283.67ZM659.98 137.76 690.31 188.35 668.03 242.97 658.84 243.67 663.52 304.85 612.47 308.76 607.79 247.58 598.6 248.28 568.27 197.69 590.54 143.07Z"/>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 747 B After Width: | Height: | Size: 718 B |
+359
-250
@@ -1,443 +1,552 @@
|
|||||||
:root {
|
:root {
|
||||||
--lightness-primary: 0.35;
|
--fallback-primary: hsl(15, 80%, 45%);
|
||||||
--chroma-primary: 0.15;
|
--fallback-background: hsl(15, 20%, 95%);
|
||||||
--hue-primary: 15;
|
--fallback-surface: hsl(0, 0%, 100%);
|
||||||
|
--fallback-text: hsl(250, 20%, 15%);
|
||||||
|
--fallback-static-white: hsl(0, 0%, 100%);
|
||||||
|
--fallback-static-black: hsl(250, 20%, 15%);
|
||||||
|
--fallback-text-inverted: hsl(0, 0%, 100%);
|
||||||
|
--fallback-success: hsl(140, 50%, 45%);
|
||||||
|
--fallback-danger: hsl(25, 80%, 50%);
|
||||||
|
--fallback-warning: hsl(50, 90%, 65%);
|
||||||
|
--fallback-border: hsla(250, 20%, 15%, 0.2);
|
||||||
|
--fallback-border-light: hsla(250, 20%, 15%, 0.1);
|
||||||
|
--fallback-primary-transparent: hsla(15, 80%, 45%, 0.1);
|
||||||
|
--fallback-shadow-primary: hsla(15, 80%, 45%, 0.3);
|
||||||
|
--fallback-overlay: rgba(0, 0, 0, 0.6);
|
||||||
|
|
||||||
--lightness-background: 0.9;
|
--lightness-primary: 0.35;
|
||||||
--chroma-background: 0.01;
|
--chroma-primary: 0.15;
|
||||||
--hue-background: var(--hue-primary);
|
--hue-primary: 15;
|
||||||
|
|
||||||
--lightness-surface: 1;
|
--lightness-background: 0.95;
|
||||||
--chroma-surface: 0;
|
--chroma-background: 0.01;
|
||||||
--hue-surface: 0;
|
--hue-background: var(--hue-primary);
|
||||||
|
|
||||||
--lightness-text: 0.15;
|
--lightness-surface: 1;
|
||||||
--chroma-text: 0.05;
|
--chroma-surface: 0;
|
||||||
--hue-text: 250;
|
--hue-surface: 0;
|
||||||
|
|
||||||
--lightness-success: 0.60;
|
--lightness-text: 0.15;
|
||||||
--chroma-success: 0.18;
|
--chroma-text: 0.05;
|
||||||
--hue-success: 140;
|
--hue-text: 250;
|
||||||
|
|
||||||
--lightness-danger: 0.50;
|
--lightness-static-white: 1;
|
||||||
--chroma-danger: 0.22;
|
--chroma-static-white: 0;
|
||||||
--hue-danger: 25;
|
--hue-static-white: 0;
|
||||||
|
|
||||||
--lightness-warning: 0.75;
|
--lightness-static-black: 0.15;
|
||||||
--chroma-warning: 0.18;
|
--chroma-static-black: 0.05;
|
||||||
--hue-warning: 70;
|
--hue-static-black: 250;
|
||||||
|
|
||||||
--dimension-multiplier: 1;
|
--lightness-text-inverted: 1;
|
||||||
--spacing-small: calc(0.5rem * var(--dimension-multiplier));
|
--chroma-text-inverted: 0;
|
||||||
--spacing-medium: calc(1rem * var(--dimension-multiplier));
|
--hue-text-inverted: 0;
|
||||||
--spacing-large: calc(1.5rem * var(--dimension-multiplier));
|
|
||||||
|
|
||||||
--border-radius-small: calc(0.5rem * var(--dimension-multiplier));
|
--lightness-success: 0.60;
|
||||||
--border-radius-large: calc(1.25rem * var(--dimension-multiplier));
|
--chroma-success: 0.18;
|
||||||
--border-radius-pill: calc(999rem * var(--dimension-multiplier));
|
--hue-success: 140;
|
||||||
|
|
||||||
|
--lightness-danger: 0.50;
|
||||||
|
--chroma-danger: 0.22;
|
||||||
|
--hue-danger: 25;
|
||||||
|
|
||||||
|
--lightness-warning: 0.85;
|
||||||
|
--chroma-warning: 0.15;
|
||||||
|
--hue-warning: 80;
|
||||||
|
|
||||||
|
--dimension-multiplier: 1;
|
||||||
|
--spacing-scale-base: 0.25rem;
|
||||||
|
--spacing-small: calc(var(--spacing-scale-base) * 2 * var(--dimension-multiplier));
|
||||||
|
--spacing-medium: calc(var(--spacing-scale-base) * 4 * var(--dimension-multiplier));
|
||||||
|
--spacing-large: calc(var(--spacing-scale-base) * 6 * var(--dimension-multiplier));
|
||||||
|
|
||||||
|
--border-radius-small: calc(0.5rem * var(--dimension-multiplier));
|
||||||
|
--border-radius-large: calc(1.25rem * var(--dimension-multiplier));
|
||||||
|
--border-radius-pill: calc(999rem * var(--dimension-multiplier));
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root {
|
:root {
|
||||||
--lightness-primary: 0.65;
|
--fallback-primary: hsl(15, 80%, 60%);
|
||||||
--chroma-primary: 0.20;
|
--fallback-background: hsl(250, 15%, 15%);
|
||||||
|
--fallback-surface: hsl(250, 15%, 20%);
|
||||||
|
--fallback-text: hsl(250, 20%, 90%);
|
||||||
|
--fallback-text-inverted: hsl(0, 0%, 0%);
|
||||||
|
--fallback-border: hsla(250, 20%, 90%, 0.2);
|
||||||
|
--fallback-border-light: hsla(250, 20%, 90%, 0.1);
|
||||||
|
--fallback-primary-transparent: hsla(15, 80%, 60%, 0.1);
|
||||||
|
--fallback-shadow-primary: hsla(15, 80%, 60%, 0.3);
|
||||||
|
|
||||||
--lightness-background: 0.2;
|
--lightness-primary: 0.65;
|
||||||
--chroma-background: 0.02;
|
--chroma-primary: 0.20;
|
||||||
|
|
||||||
--lightness-surface: 0.18;
|
--lightness-background: 0.15;
|
||||||
--chroma-surface: 0.02;
|
--chroma-background: 0.02;
|
||||||
|
|
||||||
--lightness-text: 0.90;
|
--lightness-surface: 0.20;
|
||||||
--chroma-text: 0.02;
|
--chroma-surface: 0.02;
|
||||||
}
|
|
||||||
|
--lightness-text: 0.90;
|
||||||
|
--chroma-text: 0.02;
|
||||||
|
|
||||||
|
--lightness-text-inverted: 0;
|
||||||
|
--chroma-text-inverted: 0;
|
||||||
|
--hue-text-inverted: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
* {
|
* {
|
||||||
transition-duration: 0s !important;
|
transition-duration: 0s !important;
|
||||||
animation-duration: 0s !important;
|
animation-duration: 0s !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: oklch(var(--lightness-background) var(--chroma-background) var(--hue-background));
|
background-color: var(--fallback-background);
|
||||||
color: oklch(var(--lightness-text) var(--chroma-text) var(--hue-text));
|
background-color: oklch(var(--lightness-background) var(--chroma-background) var(--hue-background));
|
||||||
font-family: system-ui, -apple-system, sans-serif;
|
color: var(--fallback-text);
|
||||||
font-size: calc(1rem * var(--dimension-multiplier));
|
color: oklch(var(--lightness-text) var(--chroma-text) var(--hue-text));
|
||||||
line-height: 1.6;
|
font-family: system-ui, -apple-system, sans-serif;
|
||||||
margin: 0;
|
font-size: calc(1rem * var(--dimension-multiplier));
|
||||||
padding: 0;
|
line-height: 1.6;
|
||||||
display: flex;
|
margin: 0;
|
||||||
flex-direction: column;
|
padding: 0;
|
||||||
min-height: 100vh;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
|
transition: background-color 0.3s ease, color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-link {
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
display: block;
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand-link:hover {
|
||||||
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.application-header {
|
.application-header {
|
||||||
background-color: oklch(var(--lightness-primary) var(--chroma-primary) var(--hue-primary));
|
background-color: var(--fallback-primary);
|
||||||
color: oklch(1 0 0);
|
background-color: oklch(var(--lightness-primary) var(--chroma-primary) var(--hue-primary));
|
||||||
padding: var(--spacing-medium) var(--spacing-large);
|
color: var(--fallback-text-inverted);
|
||||||
display: flex;
|
color: oklch(var(--lightness-text-inverted) var(--chroma-text-inverted) var(--hue-text-inverted));
|
||||||
justify-content: space-between;
|
padding: var(--spacing-medium) var(--spacing-large);
|
||||||
align-items: center;
|
display: flex;
|
||||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
justify-content: space-between;
|
||||||
position: sticky;
|
align-items: center;
|
||||||
top: 0;
|
box-shadow: 0 4px 12px var(--fallback-border-light);
|
||||||
z-index: 100;
|
box-shadow: 0 4px 12px oklch(var(--lightness-text) var(--chroma-text) var(--hue-text) / 0.1);
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
.brand-container {
|
.brand-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--spacing-small);
|
gap: var(--spacing-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
.brand-logo {
|
.brand-logo {
|
||||||
width: calc(2rem * var(--dimension-multiplier));
|
width: calc(2rem * var(--dimension-multiplier));
|
||||||
height: calc(2rem * var(--dimension-multiplier));
|
height: calc(2rem * var(--dimension-multiplier));
|
||||||
color: currentColor;
|
color: currentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
.application-title {
|
.application-title {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: calc(1.25rem * var(--dimension-multiplier));
|
font-size: calc(1.25rem * var(--dimension-multiplier));
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
letter-spacing: -0.02em;
|
letter-spacing: -0.02em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.application-content {
|
.application-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: var(--spacing-large);
|
padding: var(--spacing-large);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: calc(48rem * var(--dimension-multiplier));
|
max-width: calc(48rem * var(--dimension-multiplier));
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-card {
|
.content-card {
|
||||||
background-color: oklch(var(--lightness-surface) var(--chroma-surface) var(--hue-surface));
|
background-color: var(--fallback-surface);
|
||||||
padding: var(--spacing-large);
|
background-color: oklch(var(--lightness-surface) var(--chroma-surface) var(--hue-surface));
|
||||||
border-radius: var(--border-radius-large);
|
padding: var(--spacing-large);
|
||||||
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.05), 0 8px 10px -6px rgba(0, 0, 0, 0.01);
|
border-radius: var(--border-radius-large);
|
||||||
margin-bottom: var(--spacing-large);
|
box-shadow: 0 10px 25px -5px var(--fallback-border-light), 0 8px 10px -6px var(--fallback-border-light);
|
||||||
|
box-shadow: 0 10px 25px -5px oklch(var(--lightness-text) var(--chroma-text) var(--hue-text) / 0.05), 0 8px 10px -6px oklch(var(--lightness-text) var(--chroma-text) var(--hue-text) / 0.01);
|
||||||
|
margin-bottom: var(--spacing-large);
|
||||||
|
transition: background-color 0.3s ease, box-shadow 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: var(--spacing-medium);
|
margin-bottom: var(--spacing-medium);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--spacing-small);
|
gap: var(--spacing-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-subtitle {
|
.card-subtitle {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: var(--spacing-small);
|
margin-bottom: var(--spacing-small);
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-size: calc(0.875rem * var(--dimension-multiplier));
|
font-size: calc(0.875rem * var(--dimension-multiplier));
|
||||||
letter-spacing: 0.05em;
|
letter-spacing: 0.05em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-form {
|
.input-form {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--spacing-medium);
|
gap: var(--spacing-medium);
|
||||||
}
|
}
|
||||||
|
|
||||||
.inline-form {
|
.inline-form {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flexible_form_container {
|
.flexible_form_container {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: calc(0.5rem * var(--dimension-multiplier));
|
gap: calc(0.5rem * var(--dimension-multiplier));
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-group {
|
.form-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: calc(0.25rem * var(--dimension-multiplier));
|
gap: calc(0.25rem * var(--dimension-multiplier));
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-label {
|
.input-label {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: calc(0.875rem * var(--dimension-multiplier));
|
font-size: calc(0.875rem * var(--dimension-multiplier));
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-input {
|
.text-input {
|
||||||
padding: var(--spacing-medium);
|
padding: var(--spacing-medium);
|
||||||
border: 2px solid oklch(0.5 0 0 / 0.2);
|
border: 2px solid var(--fallback-border);
|
||||||
border-radius: var(--border-radius-small);
|
border: 2px solid oklch(var(--lightness-text) var(--chroma-text) var(--hue-text) / 0.2);
|
||||||
background-color: oklch(var(--lightness-background) var(--chroma-background) var(--hue-background));
|
border-radius: var(--border-radius-small);
|
||||||
color: inherit;
|
background-color: var(--fallback-background);
|
||||||
font-size: inherit;
|
background-color: oklch(var(--lightness-background) var(--chroma-background) var(--hue-background));
|
||||||
transition: border-color 0.2s;
|
color: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
transition: border-color 0.2s ease, background-color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-input:focus {
|
.text-input:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: oklch(var(--lightness-primary) var(--chroma-primary) var(--hue-primary));
|
border-color: var(--fallback-primary);
|
||||||
|
border-color: oklch(var(--lightness-primary) var(--chroma-primary) var(--hue-primary));
|
||||||
}
|
}
|
||||||
|
|
||||||
.small-input {
|
.small-input {
|
||||||
width: calc(12rem * var(--dimension-multiplier));
|
width: calc(12rem * var(--dimension-multiplier));
|
||||||
padding: var(--spacing-small);
|
padding: var(--spacing-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
.time-input {
|
.time-input {
|
||||||
width: calc(13rem * var(--dimension-multiplier));
|
width: calc(13rem * var(--dimension-multiplier));
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-button {
|
.action-button {
|
||||||
padding: var(--spacing-medium) var(--spacing-large);
|
padding: var(--spacing-medium) var(--spacing-large);
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: var(--border-radius-pill);
|
border-radius: var(--border-radius-pill);
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: var(--spacing-small);
|
gap: var(--spacing-small);
|
||||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
color: oklch(1 0 0);
|
color: var(--fallback-text-inverted);
|
||||||
|
color: oklch(var(--lightness-text-inverted) var(--chroma-text-inverted) var(--hue-text-inverted));
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-button:hover, .action-button:focus {
|
.action-button:hover, .action-button:focus {
|
||||||
filter: brightness(1.1);
|
filter: brightness(1.1);
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-button {
|
.icon-button {
|
||||||
padding: calc(0.5rem * var(--dimension-multiplier));
|
padding: calc(0.5rem * var(--dimension-multiplier));
|
||||||
border-radius: var(--border-radius-small);
|
border-radius: var(--border-radius-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary-button {
|
.primary-button {
|
||||||
background-color: oklch(var(--lightness-primary) var(--chroma-primary) var(--hue-primary));
|
background-color: var(--fallback-primary);
|
||||||
box-shadow: 0 4px 12px oklch(var(--lightness-primary) var(--chroma-primary) var(--hue-primary) / 0.3);
|
background-color: oklch(var(--lightness-primary) var(--chroma-primary) var(--hue-primary));
|
||||||
|
box-shadow: 0 4px 12px var(--fallback-shadow-primary);
|
||||||
|
box-shadow: 0 4px 12px oklch(var(--lightness-primary) var(--chroma-primary) var(--hue-primary) / 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.success-button {
|
.success-button {
|
||||||
background-color: oklch(var(--lightness-success) var(--chroma-success) var(--hue-success));
|
background-color: var(--fallback-success);
|
||||||
|
background-color: oklch(var(--lightness-success) var(--chroma-success) var(--hue-success));
|
||||||
}
|
}
|
||||||
|
|
||||||
.danger-button {
|
.danger-button {
|
||||||
background-color: oklch(var(--lightness-danger) var(--chroma-danger) var(--hue-danger));
|
background-color: var(--fallback-danger);
|
||||||
|
background-color: oklch(var(--lightness-danger) var(--chroma-danger) var(--hue-danger));
|
||||||
}
|
}
|
||||||
|
|
||||||
.warning-button {
|
.warning-button {
|
||||||
background-color: oklch(var(--lightness-warning) var(--chroma-warning) var(--hue-warning));
|
background-color: var(--fallback-warning);
|
||||||
color: oklch(0.2 0 0);
|
background-color: oklch(var(--lightness-warning) var(--chroma-warning) var(--hue-warning));
|
||||||
|
color: var(--fallback-static-black);
|
||||||
|
color: oklch(var(--lightness-static-black) var(--chroma-static-black) var(--hue-static-black));
|
||||||
}
|
}
|
||||||
|
|
||||||
.secondary-button {
|
.secondary-button {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: oklch(var(--lightness-text) var(--chroma-text) var(--hue-text));
|
color: var(--fallback-text);
|
||||||
border: 2px solid oklch(var(--lightness-text) var(--chroma-text) var(--hue-text) / 0.2);
|
color: oklch(var(--lightness-text) var(--chroma-text) var(--hue-text));
|
||||||
|
border: 2px solid var(--fallback-border);
|
||||||
|
border: 2px solid oklch(var(--lightness-text) var(--chroma-text) var(--hue-text) / 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.large-button {
|
.large-button {
|
||||||
padding: var(--spacing-large) calc(2.5rem * var(--dimension-multiplier));
|
padding: var(--spacing-large) calc(2.5rem * var(--dimension-multiplier));
|
||||||
font-size: calc(1.125rem * var(--dimension-multiplier));
|
font-size: calc(1.125rem * var(--dimension-multiplier));
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.small-button {
|
.small-button {
|
||||||
padding: calc(0.25rem * var(--dimension-multiplier)) calc(0.75rem * var(--dimension-multiplier));
|
padding: calc(0.25rem * var(--dimension-multiplier)) calc(0.75rem * var(--dimension-multiplier));
|
||||||
font-size: calc(0.875rem * var(--dimension-multiplier));
|
font-size: calc(0.875rem * var(--dimension-multiplier));
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-container {
|
.tab-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
background-color: oklch(var(--lightness-surface) var(--chroma-surface) var(--hue-surface));
|
background-color: var(--fallback-surface);
|
||||||
border-radius: var(--border-radius-pill);
|
background-color: oklch(var(--lightness-surface) var(--chroma-surface) var(--hue-surface));
|
||||||
padding: calc(0.375rem * var(--dimension-multiplier));
|
border-radius: var(--border-radius-pill);
|
||||||
gap: calc(0.25rem * var(--dimension-multiplier));
|
padding: calc(0.375rem * var(--dimension-multiplier));
|
||||||
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
|
gap: calc(0.25rem * var(--dimension-multiplier));
|
||||||
margin-bottom: var(--spacing-large);
|
box-shadow: 0 4px 12px var(--fallback-border-light);
|
||||||
overflow-x: auto;
|
box-shadow: 0 4px 12px oklch(var(--lightness-text) var(--chroma-text) var(--hue-text) / 0.05);
|
||||||
|
margin-bottom: var(--spacing-large);
|
||||||
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-button {
|
.tab-button {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
border-radius: var(--border-radius-pill);
|
border-radius: var(--border-radius-pill);
|
||||||
padding: calc(0.625rem * var(--dimension-multiplier)) var(--spacing-medium);
|
padding: calc(0.625rem * var(--dimension-multiplier)) var(--spacing-medium);
|
||||||
border: none;
|
border: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: calc(0.5rem * var(--dimension-multiplier));
|
gap: calc(0.5rem * var(--dimension-multiplier));
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.active-tab {
|
.active-tab {
|
||||||
background-color: oklch(var(--lightness-primary) var(--chroma-primary) var(--hue-primary));
|
background-color: var(--fallback-primary);
|
||||||
color: oklch(1 0 0);
|
background-color: oklch(var(--lightness-primary) var(--chroma-primary) var(--hue-primary));
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
color: var(--fallback-text-inverted);
|
||||||
|
color: oklch(var(--lightness-text-inverted) var(--chroma-text-inverted) var(--hue-text-inverted));
|
||||||
|
box-shadow: 0 2px 8px var(--fallback-border-light);
|
||||||
|
box-shadow: 0 2px 8px oklch(var(--lightness-text) var(--chroma-text) var(--hue-text) / 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-content {
|
.tab-content {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.active-content {
|
.active-content {
|
||||||
display: block;
|
display: block;
|
||||||
|
animation: fade-in 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
from { opacity: 0; transform: translateY(calc(0.25rem * var(--dimension-multiplier))); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-pill {
|
.status-pill {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: calc(0.375rem * var(--dimension-multiplier));
|
gap: calc(0.375rem * var(--dimension-multiplier));
|
||||||
padding: calc(0.25rem * var(--dimension-multiplier)) calc(0.875rem * var(--dimension-multiplier));
|
padding: calc(0.25rem * var(--dimension-multiplier)) calc(0.875rem * var(--dimension-multiplier));
|
||||||
border-radius: var(--border-radius-pill);
|
border-radius: var(--border-radius-pill);
|
||||||
font-size: calc(0.875rem * var(--dimension-multiplier));
|
font-size: calc(0.875rem * var(--dimension-multiplier));
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: oklch(1 0 0);
|
color: var(--fallback-static-white);
|
||||||
margin-left: var(--spacing-small);
|
color: oklch(var(--lightness-static-white) var(--chroma-static-white) var(--hue-static-white));
|
||||||
|
margin-left: var(--spacing-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
.warning-pill {
|
.warning-pill {
|
||||||
background-color: oklch(var(--lightness-warning) var(--chroma-warning) var(--hue-warning));
|
background-color: var(--fallback-warning);
|
||||||
color: oklch(0.2 0 0);
|
background-color: oklch(var(--lightness-warning) var(--chroma-warning) var(--hue-warning));
|
||||||
|
color: var(--fallback-static-black);
|
||||||
|
color: oklch(var(--lightness-static-black) var(--chroma-static-black) var(--hue-static-black));
|
||||||
}
|
}
|
||||||
|
|
||||||
.success-pill {
|
.success-pill {
|
||||||
background-color: oklch(var(--lightness-success) var(--chroma-success) var(--hue-success));
|
background-color: var(--fallback-success);
|
||||||
|
background-color: oklch(var(--lightness-success) var(--chroma-success) var(--hue-success));
|
||||||
}
|
}
|
||||||
|
|
||||||
.danger-pill {
|
.danger-pill {
|
||||||
background-color: oklch(var(--lightness-danger) var(--chroma-danger) var(--hue-danger));
|
background-color: var(--fallback-danger);
|
||||||
|
background-color: oklch(var(--lightness-danger) var(--chroma-danger) var(--hue-danger));
|
||||||
}
|
}
|
||||||
|
|
||||||
.notice-card {
|
.notice-card {
|
||||||
border-left: 6px solid oklch(var(--lightness-primary) var(--chroma-primary) var(--hue-primary));
|
border-left: calc(0.375rem * var(--dimension-multiplier)) solid var(--fallback-primary);
|
||||||
|
border-left: calc(0.375rem * var(--dimension-multiplier)) solid oklch(var(--lightness-primary) var(--chroma-primary) var(--hue-primary));
|
||||||
}
|
}
|
||||||
|
|
||||||
.danger-card {
|
.danger-card {
|
||||||
border-left-color: oklch(var(--lightness-danger) var(--chroma-danger) var(--hue-danger));
|
border-left-color: var(--fallback-danger);
|
||||||
|
border-left-color: oklch(var(--lightness-danger) var(--chroma-danger) var(--hue-danger));
|
||||||
}
|
}
|
||||||
|
|
||||||
.success-card {
|
.success-card {
|
||||||
border-left-color: oklch(var(--lightness-success) var(--chroma-success) var(--hue-success));
|
border-left-color: var(--fallback-success);
|
||||||
|
border-left-color: oklch(var(--lightness-success) var(--chroma-success) var(--hue-success));
|
||||||
}
|
}
|
||||||
|
|
||||||
.warning-card {
|
.warning-card {
|
||||||
border-left-color: oklch(var(--lightness-warning) var(--chroma-warning) var(--hue-warning));
|
border-left-color: var(--fallback-warning);
|
||||||
|
border-left-color: oklch(var(--lightness-warning) var(--chroma-warning) var(--hue-warning));
|
||||||
}
|
}
|
||||||
|
|
||||||
.target-name {
|
.target-name {
|
||||||
font-size: calc(2.5rem * var(--dimension-multiplier));
|
font-size: calc(2.5rem * var(--dimension-multiplier));
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: var(--spacing-large) 0;
|
margin: var(--spacing-large) 0;
|
||||||
color: oklch(var(--lightness-primary) var(--chroma-primary) var(--hue-primary));
|
color: var(--fallback-primary);
|
||||||
font-weight: 800;
|
color: oklch(var(--lightness-primary) var(--chroma-primary) var(--hue-primary));
|
||||||
|
font-weight: 800;
|
||||||
}
|
}
|
||||||
|
|
||||||
.data-table {
|
.data-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
|
|
||||||
.data-table th, .data-table td {
|
.data-table th, .data-table td {
|
||||||
padding: var(--spacing-medium);
|
padding: var(--spacing-medium);
|
||||||
border-bottom: 1px solid oklch(0.5 0 0 / 0.1);
|
border-bottom: 1px solid var(--fallback-border-light);
|
||||||
text-align: left;
|
border-bottom: 1px solid oklch(var(--lightness-text) var(--chroma-text) var(--hue-text) / 0.1);
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.data-table th {
|
.data-table th {
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
font-size: calc(0.75rem * var(--dimension-multiplier));
|
font-size: calc(0.75rem * var(--dimension-multiplier));
|
||||||
letter-spacing: 0.05em;
|
letter-spacing: 0.05em;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.highlighted-row {
|
.highlighted-row {
|
||||||
background-color: oklch(var(--lightness-primary) var(--chroma-primary) var(--hue-primary) / 0.1);
|
background-color: var(--fallback-primary-transparent);
|
||||||
|
background-color: oklch(var(--lightness-primary) var(--chroma-primary) var(--hue-primary) / 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.highlighted-row td {
|
.highlighted-row td {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.unregistered-row {
|
.unregistered-row {
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown-container h1, .markdown-container h2, .markdown-container h3 {
|
.markdown-container h1, .markdown-container h2, .markdown-container h3 {
|
||||||
color: oklch(var(--lightness-primary) var(--chroma-primary) var(--hue-primary));
|
color: var(--fallback-primary);
|
||||||
|
color: oklch(var(--lightness-primary) var(--chroma-primary) var(--hue-primary));
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown-container ul {
|
.markdown-container ul {
|
||||||
padding-left: var(--spacing-large);
|
padding-left: var(--spacing-large);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-overlay {
|
.modal-overlay {
|
||||||
display: none;
|
display: none;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: rgba(0,0,0,0.6);
|
background-color: var(--fallback-overlay);
|
||||||
backdrop-filter: blur(4px);
|
background-color: oklch(0 0 0 / 0.6);
|
||||||
justify-content: center;
|
backdrop-filter: blur(4px);
|
||||||
align-items: center;
|
justify-content: center;
|
||||||
z-index: 1000;
|
align-items: center;
|
||||||
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.active-modal {
|
.active-modal {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-dialog {
|
.modal-dialog {
|
||||||
background-color: oklch(var(--lightness-surface) var(--chroma-surface) var(--hue-surface));
|
background-color: var(--fallback-surface);
|
||||||
padding: var(--spacing-large);
|
background-color: oklch(var(--lightness-surface) var(--chroma-surface) var(--hue-surface));
|
||||||
border-radius: var(--border-radius-large);
|
padding: var(--spacing-large);
|
||||||
max-width: calc(32rem * var(--dimension-multiplier));
|
border-radius: var(--border-radius-large);
|
||||||
width: 90%;
|
max-width: calc(32rem * var(--dimension-multiplier));
|
||||||
box-shadow: 0 20px 40px rgba(0,0,0,0.2);
|
width: 90%;
|
||||||
|
box-shadow: 0 20px 40px var(--fallback-border);
|
||||||
|
box-shadow: 0 20px 40px oklch(0 0 0 / 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-title {
|
.modal-title {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--spacing-small);
|
gap: var(--spacing-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-actions {
|
.modal-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: var(--spacing-medium);
|
gap: var(--spacing-medium);
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
margin-top: var(--spacing-large);
|
margin-top: var(--spacing-large);
|
||||||
}
|
}
|
||||||
|
|
||||||
.qr-code-container {
|
.qr-code-container {
|
||||||
background-color: oklch(1 0 0);
|
background-color: var(--fallback-static-white);
|
||||||
padding: var(--spacing-medium);
|
background-color: oklch(var(--lightness-static-white) var(--chroma-static-white) var(--hue-static-white));
|
||||||
border-radius: var(--border-radius-large);
|
padding: var(--spacing-medium);
|
||||||
display: inline-block;
|
border-radius: var(--border-radius-large);
|
||||||
margin-bottom: var(--spacing-medium);
|
display: inline-block;
|
||||||
box-shadow: inset 0 0 0 2px oklch(0.5 0 0 / 0.1);
|
margin-bottom: var(--spacing-medium);
|
||||||
|
box-shadow: inset 0 0 0 2px var(--fallback-border-light);
|
||||||
|
box-shadow: inset 0 0 0 2px oklch(var(--lightness-text) var(--chroma-text) var(--hue-text) / 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.qr-code-container svg {
|
.qr-code-container svg {
|
||||||
display: block;
|
display: block;
|
||||||
width: calc(14rem * var(--dimension-multiplier));
|
width: calc(14rem * var(--dimension-multiplier));
|
||||||
height: calc(14rem * var(--dimension-multiplier));
|
height: calc(14rem * var(--dimension-multiplier));
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code-container svg path {
|
||||||
|
fill: var(--fallback-static-black);
|
||||||
|
fill: oklch(var(--lightness-static-black) var(--chroma-static-black) var(--hue-static-black));
|
||||||
}
|
}
|
||||||
|
|
||||||
.monospace-identifier {
|
.monospace-identifier {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
+332
-332
@@ -1,348 +1,348 @@
|
|||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="administrator-section">
|
<section class="administrator-section">
|
||||||
{% if setup_required %}
|
{% if setup_required %}
|
||||||
<div class="content-card">
|
<div class="content-card">
|
||||||
<h2 class="section-title"><i data-lucide="settings"></i> Administrator Einrichtung</h2>
|
<h2 class="section-title"><i data-lucide="settings"></i> Einrichtung</h2>
|
||||||
<form action="{{ url_for('handle_administrator') }}" method="POST" class="input-form">
|
<form action="{{ url_for('handle_administrator') }}" method="POST" class="input-form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="password_string" class="input-label">Neues Administrator-Passwort</label>
|
<label for="password_string" class="input-label">Neues Passwort</label>
|
||||||
<input type="password" id="password_string" name="password_string" class="text-input" required>
|
<input type="password" id="password_string" name="password_string" class="text-input" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="password_string_confirmation" class="input-label">Passwort wiederholen</label>
|
<label for="password_string_confirmation" class="input-label">Passwort bestätigen</label>
|
||||||
<input type="password" id="password_string_confirmation" name="password_string_confirmation" class="text-input" required>
|
<input type="password" id="password_string_confirmation" name="password_string_confirmation" class="text-input" required>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="action-button primary-button"><i data-lucide="check"></i> Einrichtung abschließen</button>
|
<button type="submit" class="action-button primary-button"><i data-lucide="check"></i> Abschließen</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% elif login_required %}
|
{% elif login_required %}
|
||||||
<div class="content-card">
|
<div class="content-card">
|
||||||
<h2 class="section-title"><i data-lucide="shield"></i> Administrator Anmeldung</h2>
|
<h2 class="section-title"><i data-lucide="shield"></i> Admin-Login</h2>
|
||||||
<form action="{{ url_for('handle_administrator') }}" method="POST" class="input-form">
|
<form action="{{ url_for('handle_administrator') }}" method="POST" class="input-form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="password_string" class="input-label">Passwort</label>
|
<label for="password_string" class="input-label">Passwort</label>
|
||||||
<input type="password" id="password_string" name="password_string" class="text-input" required>
|
<input type="password" id="password_string" name="password_string" class="text-input" required>
|
||||||
</div>
|
</div>
|
||||||
{% if two_factor_enabled %}
|
{% if two_factor_enabled %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="two_factor_code" class="input-label">Zwei-Faktor Code</label>
|
<label for="two_factor_code" class="input-label">2FA-Code</label>
|
||||||
<input type="text" id="two_factor_code" name="two_factor_code" class="text-input" required>
|
<input type="text" id="two_factor_code" name="two_factor_code" class="text-input" required>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<button type="submit" class="action-button primary-button"><i data-lucide="log-in"></i> Anmelden</button>
|
<button type="submit" class="action-button primary-button"><i data-lucide="log-in"></i> Anmelden</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% elif dashboard_active %}
|
{% elif dashboard_active %}
|
||||||
<div class="tab-container">
|
<div class="tab-container">
|
||||||
<button class="tab-button active-tab" data-target="control-panel"><i data-lucide="sliders"></i> Steuerung</button>
|
<button class="tab-button active-tab" data-target="control-panel"><i data-lucide="sliders"></i> Steuerung</button>
|
||||||
<button class="tab-button" data-target="player-panel"><i data-lucide="users"></i> Spieler</button>
|
<button class="tab-button" data-target="player-panel"><i data-lucide="users"></i> Spieler</button>
|
||||||
<button class="tab-button" data-target="kill-panel"><i data-lucide="message-square"></i> Meldungen</button>
|
<button class="tab-button" data-target="kill-panel"><i data-lucide="message-square"></i> Meldungen</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="control-panel" class="tab-content active-content">
|
<div id="control-panel" class="tab-content active-content">
|
||||||
<div class="content-card status-card">
|
<div class="content-card status-card">
|
||||||
<h3 class="card-title" style="display: flex; align-items: center; gap: var(--spacing-small);">
|
<h3 class="card-title" style="display: flex; align-items: center; gap: var(--spacing-small);">
|
||||||
<i data-lucide="activity"></i> Spielstatus:
|
<i data-lucide="activity"></i> Spielstatus:
|
||||||
{% if game_status == 'pending' %}
|
{% if game_status == 'pending' %}
|
||||||
<span class="status-pill warning-pill"><i data-lucide="clock"></i> Ausstehend</span>
|
<span class="status-pill warning-pill"><i data-lucide="clock"></i> Ausstehend</span>
|
||||||
{% elif game_status == 'running' %}
|
{% elif game_status == 'running' %}
|
||||||
<span class="status-pill success-pill"><i data-lucide="play"></i> Läuft</span>
|
<span class="status-pill success-pill"><i data-lucide="play"></i> Läuft</span>
|
||||||
{% elif game_status == 'paused' %}
|
{% elif game_status == 'paused' %}
|
||||||
<span class="status-pill warning-pill"><i data-lucide="pause"></i> Pausiert</span>
|
<span class="status-pill warning-pill"><i data-lucide="pause"></i> Pausiert</span>
|
||||||
{% elif game_status == 'finished' %}
|
{% elif game_status == 'finished' %}
|
||||||
<span class="status-pill danger-pill"><i data-lucide="square"></i> Beendet</span>
|
<span class="status-pill danger-pill"><i data-lucide="square"></i> Beendet</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
{% if game_status == 'pending' %}
|
{% if game_status == 'pending' %}
|
||||||
{% if can_start %}
|
{% if can_start %}
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="input-form admin-asynchronous-form">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="input-form admin-asynchronous-form">
|
||||||
<input type="hidden" name="action_type" value="start_game">
|
<input type="hidden" name="action_type" value="start_game">
|
||||||
<button type="submit" class="action-button success-button large-button"><i data-lucide="play"></i> Spiel starten</button>
|
<button type="submit" class="action-button success-button large-button"><i data-lucide="play"></i> Spiel starten</button>
|
||||||
</form>
|
</form>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="content-card notice-card warning-card">
|
<div class="content-card notice-card warning-card">
|
||||||
<p class="notice-text">Es werden mindestens zwei freigegebene Spieler benötigt, um das Spiel zu starten.</p>
|
<p class="notice-text">Zu wenige Spieler zum Starten.</p>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% elif game_status == 'running' %}
|
{% elif game_status == 'running' %}
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="input-form admin-asynchronous-form" style="margin-bottom: var(--spacing-medium);">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="input-form admin-asynchronous-form" style="margin-bottom: var(--spacing-medium);">
|
||||||
<input type="hidden" name="action_type" value="pause_game">
|
<input type="hidden" name="action_type" value="pause_game">
|
||||||
<button type="submit" class="action-button warning-button large-button"><i data-lucide="pause"></i> Spiel pausieren</button>
|
<button type="submit" class="action-button warning-button large-button"><i data-lucide="pause"></i> Spiel pausieren</button>
|
||||||
</form>
|
</form>
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="input-form admin-asynchronous-form">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="input-form admin-asynchronous-form">
|
||||||
<input type="hidden" name="action_type" value="stop_game">
|
<input type="hidden" name="action_type" value="stop_game">
|
||||||
<button type="submit" class="action-button danger-button large-button"><i data-lucide="square"></i> Spiel beenden</button>
|
<button type="submit" class="action-button danger-button large-button"><i data-lucide="square"></i> Spiel beenden</button>
|
||||||
</form>
|
</form>
|
||||||
{% elif game_status == 'paused' %}
|
{% elif game_status == 'paused' %}
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="input-form admin-asynchronous-form" style="margin-bottom: var(--spacing-medium);">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="input-form admin-asynchronous-form" style="margin-bottom: var(--spacing-medium);">
|
||||||
<input type="hidden" name="action_type" value="resume_game">
|
<input type="hidden" name="action_type" value="resume_game">
|
||||||
<button type="submit" class="action-button success-button large-button"><i data-lucide="play"></i> Spiel fortsetzen</button>
|
<button type="submit" class="action-button success-button large-button"><i data-lucide="play"></i> Spiel fortsetzen</button>
|
||||||
</form>
|
</form>
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="input-form admin-asynchronous-form">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="input-form admin-asynchronous-form">
|
||||||
<input type="hidden" name="action_type" value="stop_game">
|
<input type="hidden" name="action_type" value="stop_game">
|
||||||
<button type="submit" class="action-button danger-button large-button"><i data-lucide="square"></i> Spiel beenden</button>
|
<button type="submit" class="action-button danger-button large-button"><i data-lucide="square"></i> Spiel beenden</button>
|
||||||
</form>
|
</form>
|
||||||
{% elif game_status == 'finished' %}
|
{% elif game_status == 'finished' %}
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="input-form admin-asynchronous-form">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="input-form admin-asynchronous-form">
|
||||||
<input type="hidden" name="action_type" value="reset_game">
|
<input type="hidden" name="action_type" value="reset_game">
|
||||||
<button type="submit" class="action-button danger-button large-button"><i data-lucide="rotate-ccw"></i> Spiel zurücksetzen</button>
|
<button type="submit" class="action-button danger-button large-button"><i data-lucide="rotate-ccw"></i> Spiel zurücksetzen</button>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content-card settings-card">
|
<div class="content-card settings-card">
|
||||||
<h3 class="card-title"><i data-lucide="shield-check"></i> Sicherheit</h3>
|
<h3 class="card-title"><i data-lucide="shield-check"></i> Sicherheit</h3>
|
||||||
|
|
||||||
{% if two_factor_enabled %}
|
{% if two_factor_enabled %}
|
||||||
<div class="content-card notice-card success-card">
|
<div class="content-card notice-card success-card">
|
||||||
<p class="notice-text">Zwei-Faktor-Authentifizierung ist aktiv.</p>
|
<p class="notice-text">2FA ist aktiv.</p>
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="inline-form admin-asynchronous-form">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="inline-form admin-asynchronous-form">
|
||||||
<input type="hidden" name="action_type" value="disable_two_factor">
|
<input type="hidden" name="action_type" value="disable_two_factor">
|
||||||
<button type="submit" class="action-button danger-button"><i data-lucide="shield-off"></i> Deaktivieren</button>
|
<button type="submit" class="action-button danger-button"><i data-lucide="shield-off"></i> Deaktivieren</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="content-card notice-card warning-card">
|
<div class="content-card notice-card warning-card">
|
||||||
<p class="notice-text">Zwei-Faktor-Authentifizierung ist inaktiv. Bitte scanne den QR-Code und bestätige zur Aktivierung.</p>
|
<p class="notice-text">2FA inaktiv. QR-Code scannen.</p>
|
||||||
<p>Manuelle Eingabe: <strong>{{ two_factor_secret }}</strong></p>
|
<p>Code: <strong>{{ two_factor_secret }}</strong></p>
|
||||||
<div class="qr-code-container">
|
<div class="qr-code-container">
|
||||||
{{ qr_code_svg | safe }}
|
{{ qr_code_svg | safe }}
|
||||||
</div>
|
</div>
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="input-form admin-asynchronous-form">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="input-form admin-asynchronous-form">
|
||||||
<input type="hidden" name="action_type" value="enable_two_factor">
|
<input type="hidden" name="action_type" value="enable_two_factor">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="enable_two_factor_code" class="input-label">Bestätigungscode</label>
|
<label for="enable_two_factor_code" class="input-label">Bestätigungscode</label>
|
||||||
<input type="text" id="enable_two_factor_code" name="two_factor_code" class="text-input" required>
|
<input type="text" id="enable_two_factor_code" name="two_factor_code" class="text-input" required>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="action-button success-button"><i data-lucide="shield-alert"></i> Aktivieren</button>
|
<button type="submit" class="action-button success-button"><i data-lucide="shield-alert"></i> Aktivieren</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="input-form admin-asynchronous-form" style="margin-top: var(--spacing-large);">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="input-form admin-asynchronous-form" style="margin-top: var(--spacing-large);">
|
||||||
<input type="hidden" name="action_type" value="update_credentials">
|
<input type="hidden" name="action_type" value="update_credentials">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="new_password_string" class="input-label">Passwort ändern</label>
|
<label for="new_password_string" class="input-label">Neues Passwort</label>
|
||||||
<input type="password" id="new_password_string" name="password_string" class="text-input" required>
|
<input type="password" id="new_password_string" name="password_string" class="text-input" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="update_password_confirmation" class="input-label">Passwort wiederholen</label>
|
<label for="update_password_confirmation" class="input-label">Passwort bestätigen</label>
|
||||||
<input type="password" id="update_password_confirmation" name="password_string_confirmation" class="text-input" required>
|
<input type="password" id="update_password_confirmation" name="password_string_confirmation" class="text-input" required>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="action-button primary-button"><i data-lucide="save"></i> Aktualisieren</button>
|
<button type="submit" class="action-button primary-button"><i data-lucide="save"></i> Speichern</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="player-panel" class="tab-content">
|
<div id="player-panel" class="tab-content">
|
||||||
<div class="content-card">
|
<div class="content-card">
|
||||||
<h3 class="card-title"><i data-lucide="user-plus"></i> Spieler manuell erstellen</h3>
|
<h3 class="card-title"><i data-lucide="user-plus"></i> Spieler erstellen</h3>
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="input-form admin-asynchronous-form">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="input-form admin-asynchronous-form">
|
||||||
<input type="hidden" name="action_type" value="create_player">
|
<input type="hidden" name="action_type" value="create_player">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="display_name" class="input-label">Anzeigename</label>
|
<label for="display_name" class="input-label">Name</label>
|
||||||
<input type="text" id="display_name" name="display_name" class="text-input" required>
|
<input type="text" id="display_name" name="display_name" class="text-input" required>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="action-button primary-button"><i data-lucide="plus"></i> Spieler erstellen</button>
|
<button type="submit" class="action-button primary-button"><i data-lucide="plus"></i> Erstellen</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content-card" style="overflow-x: auto;">
|
<div class="content-card" style="overflow-x: auto;">
|
||||||
<table class="data-table">
|
<table class="data-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Kennung</th>
|
<th>Kennung</th>
|
||||||
<th>Name & Änderung</th>
|
<th>Name</th>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
<th>Ziel</th>
|
<th>Ziel</th>
|
||||||
<th>Aktionen</th>
|
<th>Aktionen</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for current_player in all_players %}
|
{% for current_player in all_players %}
|
||||||
<tr class="{% if not current_player.is_approved %}unregistered-row{% endif %}">
|
<tr class="{% if not current_player.is_approved %}unregistered-row{% endif %}">
|
||||||
<td class="monospace-identifier">
|
<td class="monospace-identifier">
|
||||||
{{ current_player.player_identifier }}
|
{{ current_player.player_identifier }}
|
||||||
{% if not current_player.is_approved %}
|
{% if not current_player.is_approved %}
|
||||||
<br><span class="status-pill warning-pill" style="margin-left: 0; margin-top: var(--spacing-small);">Wartet</span>
|
<br><span class="status-pill warning-pill" style="margin-left: 0; margin-top: var(--spacing-small);">Wartet</span>
|
||||||
{% elif current_player.late_joiner %}
|
{% elif current_player.late_joiner %}
|
||||||
<br><span class="status-pill warning-pill" style="margin-left: 0; margin-top: var(--spacing-small);">Späteinstieg</span>
|
<br><span class="status-pill warning-pill" style="margin-left: 0; margin-top: var(--spacing-small);">Späteinstieg</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form">
|
||||||
<input type="hidden" name="action_type" value="edit_name">
|
<input type="hidden" name="action_type" value="edit_name">
|
||||||
<input type="hidden" name="player_identifier" value="{{ current_player.player_identifier }}">
|
<input type="hidden" name="player_identifier" value="{{ current_player.player_identifier }}">
|
||||||
<input type="text" name="new_display_name" value="{{ current_player.display_name }}" class="text-input small-input" required>
|
<input type="text" name="new_display_name" value="{{ current_player.display_name }}" class="text-input small-input" required>
|
||||||
<button type="submit" class="action-button primary-button small-button"><i data-lucide="save"></i></button>
|
<button type="submit" class="action-button primary-button small-button"><i data-lucide="save"></i></button>
|
||||||
</form>
|
</form>
|
||||||
{% if current_player.pending_display_name %}
|
{% if current_player.pending_display_name %}
|
||||||
<div class="content-card notice-card warning-card" style="margin-top: var(--spacing-small); padding: var(--spacing-small); margin-bottom: 0;">
|
<div class="content-card notice-card warning-card" style="margin-top: var(--spacing-small); padding: var(--spacing-small); margin-bottom: 0;">
|
||||||
<p style="margin: 0 0 var(--spacing-small) 0; font-size: calc(0.875rem * var(--dimension-multiplier));">
|
<p style="margin: 0 0 var(--spacing-small) 0; font-size: calc(0.875rem * var(--dimension-multiplier));">
|
||||||
Wünscht: <strong>{{ current_player.pending_display_name }}</strong>
|
-> <strong>{{ current_player.pending_display_name }}</strong>
|
||||||
</p>
|
</p>
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form" style="margin-bottom: var(--spacing-small);">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form" style="margin-bottom: var(--spacing-small);">
|
||||||
<input type="hidden" name="action_type" value="approve_name_change">
|
<input type="hidden" name="action_type" value="approve_name_change">
|
||||||
<input type="hidden" name="player_identifier" value="{{ current_player.player_identifier }}">
|
<input type="hidden" name="player_identifier" value="{{ current_player.player_identifier }}">
|
||||||
<button type="submit" class="action-button success-button small-button"><i data-lucide="check"></i></button>
|
<button type="submit" class="action-button success-button small-button"><i data-lucide="check"></i></button>
|
||||||
</form>
|
</form>
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form">
|
||||||
<input type="hidden" name="action_type" value="reject_name_change">
|
<input type="hidden" name="action_type" value="reject_name_change">
|
||||||
<input type="hidden" name="player_identifier" value="{{ current_player.player_identifier }}">
|
<input type="hidden" name="player_identifier" value="{{ current_player.player_identifier }}">
|
||||||
<button type="submit" class="action-button danger-button small-button"><i data-lucide="x"></i></button>
|
<button type="submit" class="action-button danger-button small-button"><i data-lucide="x"></i></button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if not current_player.is_approved %}
|
{% if not current_player.is_approved %}
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form">
|
||||||
<input type="hidden" name="action_type" value="approve_player">
|
<input type="hidden" name="action_type" value="approve_player">
|
||||||
<input type="hidden" name="player_identifier" value="{{ current_player.player_identifier }}">
|
<input type="hidden" name="player_identifier" value="{{ current_player.player_identifier }}">
|
||||||
<button type="submit" class="action-button success-button small-button"><i data-lucide="user-check"></i> Freigeben</button>
|
<button type="submit" class="action-button success-button small-button"><i data-lucide="user-check"></i> Freigeben</button>
|
||||||
</form>
|
</form>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if current_player.is_alive %}
|
{% if current_player.is_alive %}
|
||||||
<span class="status-pill success-pill" style="margin-left: 0;"><i data-lucide="heart"></i> Lebend</span>
|
<span class="status-pill success-pill" style="margin-left: 0;"><i data-lucide="heart"></i> Lebend</span>
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form" style="margin-top: var(--spacing-small);">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form" style="margin-top: var(--spacing-small);">
|
||||||
<input type="hidden" name="action_type" value="eliminate_player">
|
<input type="hidden" name="action_type" value="eliminate_player">
|
||||||
<input type="hidden" name="player_identifier" value="{{ current_player.player_identifier }}">
|
<input type="hidden" name="player_identifier" value="{{ current_player.player_identifier }}">
|
||||||
<button type="submit" class="action-button danger-button small-button"><i data-lucide="skull"></i> Töten</button>
|
<button type="submit" class="action-button danger-button small-button"><i data-lucide="skull"></i> Killen</button>
|
||||||
</form>
|
</form>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="status-pill danger-pill" style="margin-left: 0;"><i data-lucide="skull"></i> Tot</span>
|
<span class="status-pill danger-pill" style="margin-left: 0;"><i data-lucide="skull"></i> Gekillt</span>
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form" style="margin-top: var(--spacing-small);">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form" style="margin-top: var(--spacing-small);">
|
||||||
<input type="hidden" name="action_type" value="revive_player">
|
<input type="hidden" name="action_type" value="revive_player">
|
||||||
<input type="hidden" name="player_identifier" value="{{ current_player.player_identifier }}">
|
<input type="hidden" name="player_identifier" value="{{ current_player.player_identifier }}">
|
||||||
<button type="submit" class="action-button success-button small-button"><i data-lucide="heart-pulse"></i> Beleben</button>
|
<button type="submit" class="action-button success-button small-button"><i data-lucide="heart-pulse"></i> Beleben</button>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if current_player.target_identifier %}
|
{% if current_player.target_identifier %}
|
||||||
{{ current_player.target_identifier }}
|
{{ current_player.target_identifier }}
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form" style="margin-top: var(--spacing-small);">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form" style="margin-top: var(--spacing-small);">
|
||||||
<input type="hidden" name="action_type" value="change_target">
|
<input type="hidden" name="action_type" value="change_target">
|
||||||
<input type="hidden" name="player_identifier" value="{{ current_player.player_identifier }}">
|
<input type="hidden" name="player_identifier" value="{{ current_player.player_identifier }}">
|
||||||
<select name="new_target_identifier" class="text-input small-input">
|
<select name="new_target_identifier" class="text-input small-input">
|
||||||
{% for possible_target in all_players %}
|
{% for possible_target in all_players %}
|
||||||
{% if possible_target.is_alive and possible_target.player_identifier != current_player.player_identifier %}
|
{% if possible_target.is_alive and possible_target.player_identifier != current_player.player_identifier %}
|
||||||
<option value="{{ possible_target.player_identifier }}" {% if current_player.target_identifier == possible_target.player_identifier %}selected{% endif %}>{{ possible_target.display_name }}</option>
|
<option value="{{ possible_target.player_identifier }}" {% if current_player.target_identifier == possible_target.player_identifier %}selected{% endif %}>{{ possible_target.display_name }}</option>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<button type="submit" class="action-button secondary-button small-button"><i data-lucide="refresh-cw"></i></button>
|
<button type="submit" class="action-button secondary-button small-button"><i data-lucide="refresh-cw"></i></button>
|
||||||
</form>
|
</form>
|
||||||
{% else %}
|
{% else %}
|
||||||
-
|
-
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form">
|
||||||
<input type="hidden" name="action_type" value="generate_reset">
|
<input type="hidden" name="action_type" value="generate_reset">
|
||||||
<input type="hidden" name="player_identifier" value="{{ current_player.player_identifier }}">
|
<input type="hidden" name="player_identifier" value="{{ current_player.player_identifier }}">
|
||||||
<button type="submit" class="action-button secondary-button small-button"><i data-lucide="link"></i> Reset Link</button>
|
<button type="submit" class="action-button secondary-button small-button"><i data-lucide="link"></i> Reset Link</button>
|
||||||
</form>
|
</form>
|
||||||
{% if current_player.reset_token %}
|
{% if current_player.reset_token %}
|
||||||
<input type="text" readonly value="{{ url_for('handle_password_reset', token=current_player.reset_token, _external=True) }}" class="text-input small-input" style="margin: var(--spacing-small) 0;">
|
<input type="text" readonly value="{{ url_for('handle_password_reset', token=current_player.reset_token, _external=True) }}" class="text-input small-input" style="margin: var(--spacing-small) 0;">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form" style="margin-top: var(--spacing-small);">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form" style="margin-top: var(--spacing-small);">
|
||||||
<input type="hidden" name="action_type" value="delete_player">
|
<input type="hidden" name="action_type" value="delete_player">
|
||||||
<input type="hidden" name="player_identifier" value="{{ current_player.player_identifier }}">
|
<input type="hidden" name="player_identifier" value="{{ current_player.player_identifier }}">
|
||||||
<button type="submit" class="action-button danger-button small-button"><i data-lucide="trash-2"></i></button>
|
<button type="submit" class="action-button danger-button small-button"><i data-lucide="trash-2"></i></button>
|
||||||
</form>
|
</form>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="kill-panel" class="tab-content">
|
<div id="kill-panel" class="tab-content">
|
||||||
<div class="content-card" style="overflow-x: auto;">
|
<div class="content-card" style="overflow-x: auto;">
|
||||||
<table class="data-table">
|
<table class="data-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Zeitpunkt</th>
|
<th>Zeitpunkt</th>
|
||||||
<th>Killer</th>
|
<th>Killer</th>
|
||||||
<th>Opfer</th>
|
<th>Opfer</th>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
<th>Aktionen</th>
|
<th>Aktionen</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for report in kill_logs %}
|
{% for report in kill_logs %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ report.kill_timestamp[:16].replace('T', ' ') if report.kill_timestamp else '-' }}</td>
|
<td>{{ report.kill_timestamp[:16].replace('T', ' ') if report.kill_timestamp else '-' }}</td>
|
||||||
<td>{{ report.killer_name or 'Unbekannt' }} ({{ report.killer_identifier }})</td>
|
<td>{{ report.killer_name or 'Unbekannt' }} ({{ report.killer_identifier }})</td>
|
||||||
<td>{{ report.victim_name or 'Unbekannt' }} ({{ report.victim_identifier }})</td>
|
<td>{{ report.victim_name or 'Unbekannt' }} ({{ report.victim_identifier }})</td>
|
||||||
<td>
|
<td>
|
||||||
{% if report.report_status == 'confirmed' %}
|
{% if report.report_status == 'confirmed' %}
|
||||||
<span class="status-pill success-pill" style="margin-left: 0;">Bestätigt</span>
|
<span class="status-pill success-pill" style="margin-left: 0;">Bestätigt</span>
|
||||||
{% elif report.report_status == 'pending' %}
|
{% elif report.report_status == 'pending' %}
|
||||||
<span class="status-pill warning-pill" style="margin-left: 0;">Ausstehend</span>
|
<span class="status-pill warning-pill" style="margin-left: 0;">Ausstehend</span>
|
||||||
{% elif report.report_status == 'rejected' %}
|
{% elif report.report_status == 'rejected' %}
|
||||||
<span class="status-pill danger-pill" style="margin-left: 0;">Abgelehnt</span>
|
<span class="status-pill danger-pill" style="margin-left: 0;">Abgelehnt</span>
|
||||||
{% elif report.report_status == 'revoked' %}
|
{% elif report.report_status == 'revoked' %}
|
||||||
<span class="status-pill danger-pill" style="margin-left: 0;">Widerrufen</span>
|
<span class="status-pill danger-pill" style="margin-left: 0;">Widerrufen</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ report.report_status }}
|
{{ report.report_status }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if report.report_status == 'confirmed' %}
|
{% if report.report_status == 'confirmed' %}
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form">
|
||||||
<input type="hidden" name="action_type" value="revoke_kill">
|
<input type="hidden" name="action_type" value="revoke_kill">
|
||||||
<input type="hidden" name="kill_identifier" value="{{ report.kill_identifier }}">
|
<input type="hidden" name="kill_identifier" value="{{ report.kill_identifier }}">
|
||||||
<button type="submit" class="action-button danger-button small-button"><i data-lucide="rotate-ccw"></i> Rückgängig</button>
|
<button type="submit" class="action-button danger-button small-button"><i data-lucide="rotate-ccw"></i> Rückgängig</button>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if report.report_status == 'pending' %}
|
{% if report.report_status == 'pending' %}
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form">
|
||||||
<input type="hidden" name="action_type" value="force_confirm_report">
|
<input type="hidden" name="action_type" value="force_confirm_report">
|
||||||
<input type="hidden" name="kill_identifier" value="{{ report.kill_identifier }}">
|
<input type="hidden" name="kill_identifier" value="{{ report.kill_identifier }}">
|
||||||
<button type="submit" class="action-button success-button small-button"><i data-lucide="check"></i> Bestätigen</button>
|
<button type="submit" class="action-button success-button small-button"><i data-lucide="check"></i> Bestätigen</button>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if report.report_status in ['pending', 'rejected', 'revoked'] %}
|
{% if report.report_status in ['pending', 'rejected', 'revoked'] %}
|
||||||
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form" style="margin-top: var(--spacing-small);">
|
<form action="{{ url_for('handle_administrator_action') }}" method="POST" class="flexible_form_container admin-asynchronous-form" style="margin-top: var(--spacing-small);">
|
||||||
<input type="hidden" name="action_type" value="delete_report">
|
<input type="hidden" name="action_type" value="delete_report">
|
||||||
<input type="hidden" name="kill_identifier" value="{{ report.kill_identifier }}">
|
<input type="hidden" name="kill_identifier" value="{{ report.kill_identifier }}">
|
||||||
<button type="submit" class="action-button secondary-button small-button"><i data-lucide="trash-2"></i> Löschen</button>
|
<button type="submit" class="action-button secondary-button small-button"><i data-lucide="trash-2"></i> Löschen</button>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% else %}
|
{% else %}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="5" style="text-align: center; opacity: 0.6;">Bisher keine Meldungen im System.</td>
|
<td colspan="5" style="text-align: center; opacity: 0.6;">Keine Meldungen.</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="admin-confirmation-modal" class="modal-overlay">
|
<div id="admin-confirmation-modal" class="modal-overlay">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<h2 class="modal-title"><i data-lucide="calendar-clock"></i> Aktion planen</h2>
|
<h2 class="modal-title"><i data-lucide="calendar-clock"></i> Aktion planen</h2>
|
||||||
<p class="modal-description">Führe die Aktion sofort aus oder plane sie für die Zukunft.</p>
|
<p class="modal-description">Sofort ausführen oder planen.</p>
|
||||||
<div class="form-group" style="margin-bottom: var(--spacing-large); text-align: left;">
|
<div class="form-group" style="margin-bottom: var(--spacing-large); text-align: left;">
|
||||||
<label for="modal_execution_time" class="input-label">Zeitpunkt (Optional)</label>
|
<label for="modal_execution_time" class="input-label">Zeitpunkt (Optional)</label>
|
||||||
<input type="datetime-local" id="modal_execution_time" class="text-input time-input" style="width: 100%; box-sizing: border-box;">
|
<input type="datetime-local" id="modal_execution_time" class="text-input time-input" style="width: 100%; box-sizing: border-box;">
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-actions">
|
<div class="modal-actions">
|
||||||
<button type="button" id="confirm-admin-action" class="action-button success-button"><i data-lucide="check"></i> Bestätigen</button>
|
<button type="button" id="confirm-admin-action" class="action-button success-button"><i data-lucide="check"></i> Bestätigen</button>
|
||||||
<button type="button" id="cancel-admin-action" class="action-button secondary-button"><i data-lucide="x"></i> Abbrechen</button>
|
<button type="button" id="cancel-admin-action" class="action-button secondary-button"><i data-lucide="x"></i> Abbrechen</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</section>
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
+177
-170
@@ -1,187 +1,194 @@
|
|||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="game-section">
|
<section class="game-section">
|
||||||
<div class="content-card">
|
<div class="content-card">
|
||||||
<h2 class="section-title" style="margin: 0;"><i data-lucide="user"></i> Willkommen, {{ player.display_name }} ({{ player.player_identifier }})!</h2>
|
<h2 class="section-title" style="margin: 0;"><i data-lucide="user"></i> Hallo, {{ player.display_name }}!</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if not player.is_approved %}
|
{% if not player.is_approved %}
|
||||||
<div class="content-card notice-card warning-card">
|
<div class="content-card notice-card warning-card">
|
||||||
<h2 class="modal-title"><i data-lucide="hourglass"></i> Konto wird geprüft…</h2>
|
<h2 class="modal-title"><i data-lucide="hourglass"></i> Warten auf Freigabe</h2>
|
||||||
</div>
|
<p class="notice-text">Die Spielleitung muss deinen Account noch aktivieren.</p>
|
||||||
{% elif game_status == 'pending' %}
|
</div>
|
||||||
<div class="content-card notice-card">
|
{% elif game_status == 'pending' %}
|
||||||
<h2 class="modal-title"><i data-lucide="clock"></i> Startet bald…</h2>
|
<div class="content-card notice-card">
|
||||||
</div>
|
<h2 class="modal-title"><i data-lucide="clock"></i> Spiel startet in Kürze</h2>
|
||||||
{% elif game_status == 'finished' %}
|
</div>
|
||||||
<div class="content-card leaderboard-card">
|
{% elif game_status == 'finished' %}
|
||||||
<h2 class="section-title"><i data-lucide="trophy"></i> Spiel beendet – Rangliste</h2>
|
<div class="content-card leaderboard-card">
|
||||||
<table class="data-table">
|
<h2 class="section-title"><i data-lucide="trophy"></i> Rangliste</h2>
|
||||||
<thead>
|
<table class="data-table">
|
||||||
<tr>
|
<thead>
|
||||||
<th>Platz</th>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Platz</th>
|
||||||
<th>Punkte</th>
|
<th>Name</th>
|
||||||
</tr>
|
<th>Punkte</th>
|
||||||
</thead>
|
</tr>
|
||||||
<tbody>
|
</thead>
|
||||||
{% for current_player in all_players %}
|
<tbody>
|
||||||
<tr class="{% if current_player.player_identifier == session['player_identifier'] %}highlighted-row{% endif %}">
|
{% for current_player in all_players %}
|
||||||
<td>{{ loop.index }}</td>
|
<tr class="{% if current_player.player_identifier == session['player_identifier'] %}highlighted-row{% endif %}">
|
||||||
<td>{{ current_player.display_name }}</td>
|
<td>{{ loop.index }}</td>
|
||||||
<td>{{ current_player.score }}</td>
|
<td>{{ current_player.display_name }}</td>
|
||||||
</tr>
|
<td>{{ current_player.score }}</td>
|
||||||
{% endfor %}
|
</tr>
|
||||||
</tbody>
|
{% endfor %}
|
||||||
</table>
|
</tbody>
|
||||||
</div>
|
</table>
|
||||||
{% elif game_status in ['running', 'paused'] %}
|
</div>
|
||||||
<div class="tab-container">
|
{% elif game_status in ['running', 'paused'] %}
|
||||||
<button class="tab-button active-tab" data-target="mission-panel"><i data-lucide="crosshair"></i> Ziel</button>
|
<div class="tab-container">
|
||||||
<button class="tab-button" data-target="leaderboard-panel"><i data-lucide="bar-chart-2"></i> Punkte</button>
|
<button class="tab-button active-tab" data-target="mission-panel"><i data-lucide="crosshair"></i> Ziel</button>
|
||||||
<button class="tab-button" data-target="rules-panel"><i data-lucide="book-open"></i> Regeln</button>
|
<button class="tab-button" data-target="leaderboard-panel"><i data-lucide="bar-chart-2"></i> Punkte</button>
|
||||||
</div>
|
<button class="tab-button" data-target="rules-panel"><i data-lucide="book-open"></i> Regeln</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="mission-panel" class="tab-content active-content">
|
<div id="mission-panel" class="tab-content active-content">
|
||||||
{% if game_status == 'paused' %}
|
{% if game_status == 'paused' %}
|
||||||
<div class="content-card notice-card warning-card">
|
<div class="content-card notice-card warning-card">
|
||||||
<h2 class="modal-title"><i data-lucide="pause-circle"></i> Spiel pausiert</h2>
|
<h2 class="modal-title"><i data-lucide="pause-circle"></i> Pausiert</h2>
|
||||||
<p class="notice-text">Das Spiel wurde pausiert. Eliminierungen sind aktuell nicht möglich.</p>
|
<p class="notice-text">Kills sind vorübergehend deaktiviert.</p>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if player.is_alive %}
|
{% if player.is_alive %}
|
||||||
<div class="content-card target-card" style="text-align: center;">
|
<div class="content-card target-card" style="text-align: center;">
|
||||||
<h3 class="card-subtitle"><i data-lucide="target"></i> Dein Ziel</h3>
|
<h3 class="card-subtitle"><i data-lucide="target"></i> Ziel</h3>
|
||||||
<h2 class="target-name">{{ target_player.display_name if target_player else 'Niemand' }}</h2>
|
<h2 class="target-name">{{ target_player.display_name if target_player else 'Niemand' }}</h2>
|
||||||
{% if game_status == 'running' %}
|
|
||||||
<button type="button" id="trigger-elimination" class="action-button danger-button large-button"><i data-lucide="crosshair"></i> Kill melden</button>
|
|
||||||
{% else %}
|
|
||||||
<button type="button" class="action-button secondary-button large-button" disabled><i data-lucide="lock"></i> Pausiert</button>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% elif player.late_joiner %}
|
|
||||||
<div class="content-card notice-card warning-card">
|
|
||||||
<h2 class="modal-title"><i data-lucide="users"></i> Warte auf Einstieg…</h2>
|
|
||||||
<p class="notice-text">Du bist dem Spiel während einer laufenden Runde beigetreten. Du wirst aktiv, sobald du eingewechselt wirst.</p>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="content-card notice-card danger-card">
|
|
||||||
<h2 class="modal-title"><i data-lucide="skull"></i> Eliminiert</h2>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="content-card statistics-card">
|
{% if game_status == 'running' %}
|
||||||
<h3 class="section-title"><i data-lucide="pie-chart"></i> Spielübersicht</h3>
|
{% if has_pending_kill_report %}
|
||||||
<p>Überlebende: <strong>{{ alive_count }}</strong></p>
|
<div class="notice-card warning-card" style="padding: var(--spacing-medium); border-radius: var(--border-radius-small); margin-top: var(--spacing-large);">
|
||||||
<p>Deine Kills: <strong>{{ player.score }}</strong></p>
|
<h3 style="margin-top: 0; display: flex; align-items: center; justify-content: center; gap: var(--spacing-small);"><i data-lucide="loader"></i> Kill gemeldet</h3>
|
||||||
<p>Gesamte Kills: <strong>{{ total_eliminations }}</strong></p>
|
<p>Warte auf Bestätigung…</p>
|
||||||
<p>Laufzeit: <strong id="game-stopwatch" data-start-time="{{ start_time }}">Wird berechnet...</strong></p>
|
<button type="button" id="trigger-revoke-elimination" class="action-button secondary-button"><i data-lucide="rotate-ccw"></i> Zurückziehen</button>
|
||||||
</div>
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<button type="button" id="trigger-elimination" class="action-button danger-button large-button"><i data-lucide="crosshair"></i> Kill melden</button>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<button type="button" class="action-button secondary-button large-button" disabled><i data-lucide="lock"></i> Gesperrt</button>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% elif player.late_joiner %}
|
||||||
|
<div class="content-card notice-card warning-card">
|
||||||
|
<h2 class="modal-title"><i data-lucide="users"></i> Warten auf Einwechslung</h2>
|
||||||
|
<p class="notice-text">Die Spielleitung wechselt dich bald ein.</p>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="content-card notice-card danger-card">
|
||||||
|
<h2 class="modal-title"><i data-lucide="skull"></i> Gekillt</h2>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div class="content-card settings-card">
|
<div class="content-card statistics-card">
|
||||||
<h3 class="section-title"><i data-lucide="user-cog"></i> Profil</h3>
|
<h3 class="section-title"><i data-lucide="pie-chart"></i> Übersicht</h3>
|
||||||
{% if player.pending_display_name %}
|
<p>Lebend: <strong>{{ alive_count }}</strong></p>
|
||||||
<div class="notice-card warning-card" style="padding: var(--spacing-medium);">
|
<p>Kills: <strong>{{ player.score }}</strong></p>
|
||||||
<p class="notice-text" style="margin: 0;">Der Name <strong>{{ player.pending_display_name }}</strong> wird geprüft…</p>
|
<p>Gesamt: <strong>{{ total_eliminations }}</strong></p>
|
||||||
</div>
|
<p>Zeit: <strong id="game-stopwatch" data-start-time="{{ start_time }}">Lade...</strong></p>
|
||||||
{% else %}
|
</div>
|
||||||
<button type="button" id="trigger-name-change-modal" class="action-button secondary-button"><i data-lucide="edit-3"></i> Name ändern</button>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="leaderboard-panel" class="tab-content">
|
<div class="content-card settings-card">
|
||||||
<div class="content-card">
|
<h3 class="section-title"><i data-lucide="user-cog"></i> Profil</h3>
|
||||||
<table class="data-table">
|
{% if player.pending_display_name %}
|
||||||
<thead>
|
<div class="notice-card warning-card" style="padding: var(--spacing-medium); border-radius: var(--border-radius-small);">
|
||||||
<tr>
|
<p class="notice-text" style="margin-top: 0;"><strong>{{ player.pending_display_name }}</strong> wird geprüft…</p>
|
||||||
<th>Platz</th>
|
<form action="{{ url_for('handle_player_action') }}" method="POST" class="asynchronous-form">
|
||||||
<th>Name</th>
|
<input type="hidden" name="action_type" value="cancel_name_change">
|
||||||
<th>Punkte</th>
|
<button type="submit" class="action-button secondary-button small-button"><i data-lucide="x"></i> Abbrechen</button>
|
||||||
</tr>
|
</form>
|
||||||
</thead>
|
</div>
|
||||||
<tbody>
|
{% else %}
|
||||||
{% for current_player in all_players %}
|
<button type="button" id="trigger-name-change-modal" class="action-button secondary-button"><i data-lucide="edit-3"></i> Name ändern</button>
|
||||||
<tr class="{% if current_player.player_identifier == session['player_identifier'] %}highlighted-row{% endif %}">
|
{% endif %}
|
||||||
<td>{{ loop.index }}</td>
|
</div>
|
||||||
<td>{{ current_player.display_name }}</td>
|
</div>
|
||||||
<td>{{ current_player.score }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="rules-panel" class="tab-content">
|
<div id="leaderboard-panel" class="tab-content">
|
||||||
<div class="content-card markdown-container">
|
<div class="content-card">
|
||||||
{{ rules_markup | safe }}
|
<table class="data-table">
|
||||||
</div>
|
<thead>
|
||||||
</div>
|
<tr>
|
||||||
|
<th>Platz</th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Punkte</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for current_player in all_players %}
|
||||||
|
<tr class="{% if current_player.player_identifier == session['player_identifier'] %}highlighted-row{% endif %}">
|
||||||
|
<td>{{ loop.index }}</td>
|
||||||
|
<td>{{ current_player.display_name }}</td>
|
||||||
|
<td>{{ current_player.score }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="elimination-confirm-modal" class="modal-overlay">
|
<div id="rules-panel" class="tab-content">
|
||||||
<div class="modal-dialog">
|
<div class="content-card markdown-container">
|
||||||
<h2 class="modal-title"><i data-lucide="alert-triangle"></i> Kill</h2>
|
{{ rules_markup | safe }}
|
||||||
<p class="modal-description">Möchtest du melden, dass du dein Ziel gekillt hast?</p>
|
</div>
|
||||||
<div class="modal-actions">
|
</div>
|
||||||
<button type="button" id="proceed-elimination" class="action-button danger-button"><i data-lucide="crosshair"></i> Ja, melden</button>
|
|
||||||
<button type="button" id="cancel-elimination" class="action-button secondary-button"><i data-lucide="x"></i> Abbrechen</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="waiting-modal" class="modal-overlay">
|
<div id="elimination-confirm-modal" class="modal-overlay">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<h2 class="modal-title"><i data-lucide="loader"></i> Bitte warten</h2>
|
<h2 class="modal-title"><i data-lucide="alert-triangle"></i> Kill melden</h2>
|
||||||
<p class="modal-description">Warte auf Bestätigung vom Ziel...</p>
|
<p class="modal-description">Sicher, dass du den Kill melden möchtest?</p>
|
||||||
</div>
|
<div class="modal-actions">
|
||||||
</div>
|
<button type="button" id="proceed-elimination" class="action-button danger-button"><i data-lucide="crosshair"></i> Melden</button>
|
||||||
|
<button type="button" id="cancel-elimination" class="action-button secondary-button"><i data-lucide="x"></i> Abbrechen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="rejection-modal" class="modal-overlay">
|
<div id="rejection-modal" class="modal-overlay">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<h2 class="modal-title"><i data-lucide="x-octagon"></i> Abgelehnt</h2>
|
<h2 class="modal-title"><i data-lucide="x-octagon"></i> Abgelehnt</h2>
|
||||||
<p class="modal-description">Das Ziel hat deinen Kill abgelehnt.</p>
|
<p class="modal-description">Das Ziel hat die Meldung abgelehnt. Bitte klären.</p>
|
||||||
<div class="modal-actions">
|
<div class="modal-actions">
|
||||||
<button type="button" id="acknowledge-rejection" class="action-button primary-button"><i data-lucide="check"></i> Verstanden</button>
|
<button type="button" id="acknowledge-rejection" class="action-button primary-button"><i data-lucide="check"></i> Okay</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="verification-modal" class="modal-overlay {% if player.pending_attacker_identifier %}active-modal{% endif %}">
|
<div id="verification-modal" class="modal-overlay {% if player.pending_attacker_identifier %}active-modal{% endif %}">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<h2 class="modal-title"><i data-lucide="shield-alert"></i> Wurdest du eliminiert?</h2>
|
<h2 class="modal-title"><i data-lucide="shield-alert"></i> Gekillt?</h2>
|
||||||
<p class="modal-description">
|
<p class="modal-description">
|
||||||
{% if pending_attacker_name %}
|
{% if pending_attacker_name %}
|
||||||
<strong>{{ pending_attacker_name }}</strong> hat gemeldet, dass er/sie dich gekillt hat.
|
Wurdest du von <strong>{{ pending_attacker_name }}</strong> gekillt?
|
||||||
{% else %}
|
{% else %}
|
||||||
Dein Angreifer hat gemeldet, dass er dich gekillt hat.
|
Wurdest du gekillt?
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
<div class="modal-actions">
|
<div class="modal-actions">
|
||||||
<button type="button" id="confirm-elimination" class="action-button danger-button"><i data-lucide="check"></i> Bestätigen</button>
|
<button type="button" id="confirm-elimination" class="action-button danger-button"><i data-lucide="check"></i> Ja</button>
|
||||||
<button type="button" id="reject-elimination" class="action-button secondary-button"><i data-lucide="x"></i> Ablehnen</button>
|
<button type="button" id="reject-elimination" class="action-button secondary-button"><i data-lucide="x"></i> Nein</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="name-change-modal" class="modal-overlay">
|
<div id="name-change-modal" class="modal-overlay">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<h2 class="modal-title"><i data-lucide="edit-3"></i> Name ändern</h2>
|
<h2 class="modal-title"><i data-lucide="edit-3"></i> Name ändern</h2>
|
||||||
<p class="modal-description">Dein neuer Name muss erst geprüft werden.</p>
|
<p class="modal-description">Der Name wird vorher von der Spielleitung geprüft.</p>
|
||||||
<form action="{{ url_for('handle_player_action') }}" method="POST" class="input-form asynchronous-form">
|
<form action="{{ url_for('handle_player_action') }}" method="POST" class="input-form asynchronous-form">
|
||||||
<input type="hidden" name="action_type" value="request_name_change">
|
<input type="hidden" name="action_type" value="request_name_change">
|
||||||
<div class="form-group" style="text-align: left;">
|
<div class="form-group" style="text-align: left;">
|
||||||
<label for="pending_display_name" class="input-label">Neuer Name</label>
|
<label for="pending_display_name" class="input-label">Neuer Name</label>
|
||||||
<input type="text" id="pending_display_name" name="pending_display_name" class="text-input" required>
|
<input type="text" id="pending_display_name" name="pending_display_name" class="text-input" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-actions">
|
<div class="modal-actions">
|
||||||
<button type="submit" class="action-button primary-button"><i data-lucide="send"></i> Abschicken</button>
|
<button type="submit" class="action-button primary-button"><i data-lucide="send"></i> Speichern</button>
|
||||||
<button type="button" id="cancel-name-change" class="action-button secondary-button"><i data-lucide="x"></i> Abbrechen</button>
|
<button type="button" id="cancel-name-change-modal" class="action-button secondary-button"><i data-lucide="x"></i> Abbrechen</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</section>
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
+28
-26
@@ -1,33 +1,35 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
<title>SCOicide Squad</title>
|
<title>SCOicide Squad</title>
|
||||||
<link rel="shortcut icon" href="{{ url_for('static', filename='logo.svg') }}" type="image/svg">
|
<link rel="shortcut icon" href="{{ url_for('static', filename='logo.svg') }}" type="image/svg">
|
||||||
<link rel="manifest" href="{{ url_for('static', filename='manifest.json') }}">
|
<link rel="manifest" href="{{ url_for('static', filename='manifest.json') }}">
|
||||||
<meta name="theme-color" content="#1a1e29">
|
<meta name="theme-color" content="#1a1e29">
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='stylesheet.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='stylesheet.css') }}">
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.2/socket.io.js" defer></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.2/socket.io.js" defer></script>
|
||||||
<script src="https://unpkg.com/lucide@latest" defer></script>
|
<script src="https://unpkg.com/lucide@latest" defer></script>
|
||||||
<script src="{{ url_for('static', filename='behavior.js') }}" defer></script>
|
<script src="{{ url_for('static', filename='behavior.js') }}" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="application-header">
|
<header class="application-header">
|
||||||
<div class="brand-container">
|
<a href="/" class="brand-link">
|
||||||
<img src="{{ url_for('static', filename='logo.svg') }}" alt="Logo" class="brand-logo">
|
<div class="brand-container">
|
||||||
<h1 class="application-title"><i>SCO</i>icide Squad</h1>
|
<img src="{{ url_for('static', filename='logo.svg') }}" alt="Logo" class="brand-logo">
|
||||||
</div>
|
<h1 class="application-title"><i>SCO</i>icide Squad</h1>
|
||||||
<nav class="application-navigation">
|
</div>
|
||||||
{% if session.get('player_identifier') or session.get('administrator_authenticated') %}
|
</a>
|
||||||
<a href="{{ url_for('handle_logout') }}" class="action-button secondary-button icon-button">
|
<nav class="application-navigation">
|
||||||
<i data-lucide="log-out"></i>
|
{% if session.get('player_identifier') or session.get('administrator_authenticated') %}
|
||||||
</a>
|
<a href="{{ url_for('handle_logout') }}" class="action-button secondary-button icon-button">
|
||||||
{% endif %}
|
<i data-lucide="log-out"></i>
|
||||||
</nav>
|
</a>
|
||||||
</header>
|
{% endif %}
|
||||||
<main class="application-content">
|
</nav>
|
||||||
{% block content %}{% endblock %}
|
</header>
|
||||||
</main>
|
<main class="application-content">
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
+80
-67
@@ -1,72 +1,85 @@
|
|||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="authentication-section">
|
<section class="authentication-section">
|
||||||
{% if reset_mode %}
|
{% if reset_mode %}
|
||||||
<div class="content-card">
|
<div class="content-card">
|
||||||
<h2 class="section-title"><i data-lucide="key-round"></i> Passwort zurücksetzen</h2>
|
<h2 class="section-title"><i data-lucide="key-round"></i> Passwort zurücksetzen</h2>
|
||||||
<form action="{{ url_for('handle_password_reset') }}" method="POST" class="input-form">
|
<form action="{{ url_for('handle_password_reset') }}" method="POST" class="input-form">
|
||||||
<input type="hidden" name="token" value="{{ token }}">
|
<input type="hidden" name="token" value="{{ token }}">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="password_string" class="input-label">Neues Passwort</label>
|
<label for="password_string" class="input-label">Neues Passwort</label>
|
||||||
<input type="password" id="password_string" name="password_string" class="text-input" required>
|
<input type="password" id="password_string" name="password_string" class="text-input" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="password_string_confirmation" class="input-label">Passwort wiederholen</label>
|
<label for="password_string_confirmation" class="input-label">Passwort bestätigen</label>
|
||||||
<input type="password" id="password_string_confirmation" name="password_string_confirmation" class="text-input" required>
|
<input type="password" id="password_string_confirmation" name="password_string_confirmation" class="text-input" required>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="action-button primary-button"><i data-lucide="save"></i> Passwort speichern</button>
|
<button type="submit" class="action-button primary-button"><i data-lucide="save"></i> Speichern</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{% elif register_mode %}
|
{% elif register_mode %}
|
||||||
<div class="content-card">
|
<div class="content-card">
|
||||||
<h2 class="section-title"><i data-lucide="user-plus"></i> Neues Konto erstellen</h2>
|
<h2 class="section-title"><i data-lucide="user-plus"></i> Registrieren</h2>
|
||||||
<form action="{{ url_for('handle_registration') }}" method="POST" class="input-form">
|
<form action="{{ url_for('handle_registration') }}" method="POST" class="input-form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="display_name" class="input-label">Dein Name</label>
|
<label for="display_name" class="input-label">Name</label>
|
||||||
<input type="text" id="display_name" name="display_name" class="text-input" required>
|
<input type="text" id="display_name" name="display_name" class="text-input" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="password_string" class="input-label">Passwort</label>
|
<label for="password_string" class="input-label">Passwort</label>
|
||||||
<input type="password" id="password_string" name="password_string" class="text-input" required>
|
<input type="password" id="password_string" name="password_string" class="text-input" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="password_string_confirmation" class="input-label">Passwort wiederholen</label>
|
<label for="password_string_confirmation" class="input-label">Passwort bestätigen</label>
|
||||||
<input type="password" id="password_string_confirmation" name="password_string_confirmation" class="text-input" required>
|
<input type="password" id="password_string_confirmation" name="password_string_confirmation" class="text-input" required>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="action-button primary-button"><i data-lucide="check-circle"></i> Registrieren</button>
|
<button type="submit" class="action-button primary-button"><i data-lucide="check-circle"></i> Konto erstellen</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-card" style="text-align: center;">
|
<div class="content-card" style="text-align: center;">
|
||||||
<a href="{{ url_for('handle_login') }}" class="action-button secondary-button"><i data-lucide="arrow-left"></i> Zurück zur Anmeldung</a>
|
<a href="{{ url_for('handle_login') }}" class="action-button secondary-button"><i data-lucide="arrow-left"></i> Zur Anmeldung</a>
|
||||||
</div>
|
</div>
|
||||||
{% elif registration_success %}
|
{% elif registration_success %}
|
||||||
<div class="content-card notice-card success-card" style="text-align: center;">
|
<div class="content-card notice-card success-card" style="text-align: center;">
|
||||||
<h2 class="section-title" style="justify-content: center;"><i data-lucide="party-popper"></i> Registrierung erfolgreich</h2>
|
<h2 class="section-title" style="justify-content: center;"><i data-lucide="party-popper"></i> Erfolgreich</h2>
|
||||||
<p>Deine ID für die Anmeldung lautet:</p>
|
<h2 style="color: var(--fallback-danger); color: oklch(var(--lightness-danger) var(--chroma-danger) var(--hue-danger));">Wichtig!</h2>
|
||||||
<h1 class="monospace-identifier" style="font-size: calc(3rem * var(--dimension-multiplier)); letter-spacing: calc(0.25rem * var(--dimension-multiplier)); color: oklch(var(--lightness-primary) var(--chroma-primary) var(--hue-primary)); margin: var(--spacing-medium) 0;">{{ new_identifier }}</h1>
|
<p>Deine ID:</p>
|
||||||
<p>Bitte speichere oder merke sie dir!</p>
|
<h1 class="monospace-identifier" style="font-size: calc(3rem * var(--dimension-multiplier)); letter-spacing: calc(0.25rem * var(--dimension-multiplier)); color: var(--fallback-primary); color: oklch(var(--lightness-primary) var(--chroma-primary) var(--hue-primary)); margin: var(--spacing-medium) 0;">{{ new_identifier }}</h1>
|
||||||
<div style="margin-top: var(--spacing-large);">
|
<button type="button" id="copy-identifier-button" class="action-button secondary-button" data-identifier="{{ new_identifier }}"><i data-lucide="copy"></i> ID kopieren</button>
|
||||||
<a href="{{ url_for('handle_login') }}" class="action-button primary-button"><i data-lucide="log-in"></i> Jetzt anmelden</a>
|
<p style="margin-top: var(--spacing-medium);">Gut aufbewahren!</p>
|
||||||
</div>
|
<div style="margin-top: var(--spacing-large);">
|
||||||
</div>
|
<a href="{{ url_for('handle_login', prefill_identifier=new_identifier) }}" class="action-button primary-button"><i data-lucide="log-in"></i> Anmelden</a>
|
||||||
{% else %}
|
</div>
|
||||||
<div class="content-card">
|
</div>
|
||||||
<h2 class="section-title"><i data-lucide="lock"></i> Anmeldung</h2>
|
{% else %}
|
||||||
<form action="{{ url_for('handle_login') }}" method="POST" class="input-form">
|
<div class="content-card">
|
||||||
<div class="form-group">
|
<h2 class="section-title"><i data-lucide="lock"></i> Login</h2>
|
||||||
<label for="player_identifier" class="input-label">ID</label>
|
<form action="{{ url_for('handle_login') }}" method="POST" class="input-form">
|
||||||
<input type="text" id="player_identifier" name="player_identifier" class="text-input" style="text-transform: uppercase;" maxlength="{{ identifier_length }}" oninput="this.value = this.value.replace(/[^a-zA-Z]/g, '').toUpperCase().slice(0, {{ identifier_length }});" pattern="[A-Za-z]{{ '{' }}{{ identifier_length }}{{ '}' }}" required>
|
<div class="form-group">
|
||||||
</div>
|
<label for="player_identifier" class="input-label">ID</label>
|
||||||
<div class="form-group">
|
<input type="text" id="player_identifier" name="player_identifier" class="text-input" value="{{ request.args.get('prefill_identifier', '') }}" style="text-transform: uppercase;" maxlength="{{ identifier_length }}" oninput="this.value = this.value.replace(/[^a-zA-Z]/g, '').toUpperCase().slice(0, {{ identifier_length }});" pattern="[A-Za-z]{{ '{' }}{{ identifier_length }}{{ '}' }}" required>
|
||||||
<label for="password_string" class="input-label">Passwort</label>
|
</div>
|
||||||
<input type="password" id="password_string" name="password_string" class="text-input" required>
|
<div class="form-group">
|
||||||
</div>
|
<label for="password_string" class="input-label">Passwort</label>
|
||||||
<button type="submit" class="action-button primary-button"><i data-lucide="log-in"></i> Anmelden</button>
|
<input type="password" id="password_string" name="password_string" class="text-input" required>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
<button type="submit" class="action-button primary-button"><i data-lucide="log-in"></i> Anmelden</button>
|
||||||
<div class="content-card" style="text-align: center;">
|
</form>
|
||||||
<a href="{{ url_for('handle_registration') }}" class="action-button secondary-button"><i data-lucide="user-plus"></i> Noch kein Konto? Hier registrieren</a>
|
</div>
|
||||||
</div>
|
<div class="content-card" style="text-align: center; display: flex; flex-direction: column; gap: var(--spacing-medium);">
|
||||||
{% endif %}
|
<a href="{{ url_for('handle_registration') }}" class="action-button secondary-button"><i data-lucide="user-plus"></i> Registrieren</a>
|
||||||
|
<button type="button" id="trigger-help-modal" class="action-button secondary-button"><i data-lucide="life-buoy"></i> Probleme beim Login?</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="help-modal" class="modal-overlay">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<h2 class="modal-title"><i data-lucide="help-circle"></i> Hilfe</h2>
|
||||||
|
<p class="modal-description">Wende dich an die Spielleitung, falls du deine ID oder dein Passwort vergessen hast.</p>
|
||||||
|
<div class="modal-actions">
|
||||||
|
<button type="button" id="close-help-modal" class="action-button primary-button"><i data-lucide="check"></i> Okay</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</section>
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
Reference in new issue
Block a user