Autosave: 20260306-000945

This commit is contained in:
Flatlogic Bot 2026-03-06 00:09:45 +00:00
parent cc3322de66
commit 386719b74f
11 changed files with 220 additions and 539 deletions

608
admin.php
View File

@ -408,7 +408,8 @@ if (isset($_GET['delete_resource'])) {
exit;
}
// Handle Lootbox CRUD
// Handle Grade CRUD
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'upsert_grade') {
$id = (int)$_POST['id'];
$name = trim($_POST['name']);
@ -422,6 +423,22 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
exit;
}
$image_url = null;
if ($id > 0) {
$stmt_img = $db->prepare("SELECT image_url FROM grades WHERE id = ?");
$stmt_img->execute([$id]);
$image_url = $stmt_img->fetchColumn();
}
if (isset($_FILES["image"]) && $_FILES["image"]["error"] === UPLOAD_ERR_OK) {
$ext = pathinfo($_FILES["image"]["name"], PATHINFO_EXTENSION);
$filename = "grade_" . time() . "." . $ext;
if (!is_dir("assets/images/grades")) mkdir("assets/images/grades", 0775, true);
$target = "assets/images/grades/" . $filename;
if (move_uploaded_file($_FILES["image"]["tmp_name"], $target)) {
$image_url = $target;
}
}
// Check for overlap if user_type is 'utilisateur'
if ($user_type === 'utilisateur') {
$check = db()->prepare("SELECT id FROM grades WHERE user_type = 'utilisateur' AND id != ? AND NOT (max_level < ? OR min_level > ?)");
@ -434,11 +451,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
try {
if ($id > 0) {
$stmt = db()->prepare("UPDATE grades SET name = ?, slug = ?, user_type = ?, min_level = ?, max_level = ? WHERE id = ?");
$stmt->execute([$name, $slug, $user_type, $min_level, $max_level, $id]);
$stmt = db()->prepare("UPDATE grades SET name = ?, slug = ?, user_type = ?, min_level = ?, max_level = ?, image_url = ? WHERE id = ?");
$stmt->execute([$name, $slug, $user_type, $min_level, $max_level, $image_url, $id]);
} else {
$stmt = db()->prepare("INSERT INTO grades (name, slug, user_type, min_level, max_level) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$name, $slug, $user_type, $min_level, $max_level]);
$stmt = db()->prepare("INSERT INTO grades (name, slug, user_type, min_level, max_level, image_url) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$name, $slug, $user_type, $min_level, $max_level, $image_url]);
}
header('Location: ?tab=ranks&success=1');
exit;
@ -447,7 +464,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
exit;
}
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'upsert_lootbox') {
if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["action"]) && $_POST["action"] === "upsert_lootbox") {
$id = (int)$_POST['id'];
$name = $_POST['name'];
$slug = $_POST['slug'];
@ -695,7 +712,7 @@ if ($tab === 'users') {
</nav>
</div>
<div>
<span style="font-weight: bold; color: #88c0d0;"><?php echo htmlspecialchars($_SESSION['username']); ?></span>
<span style="font-weight: bold; color: #88c0d0;"><?php echo htmlspecialchars($_SESSION["display_name"] ?? $_SESSION["username"]); ?></span>
</div>
</header>
@ -842,7 +859,7 @@ if ($tab === 'users') {
<div class="form-card">
<h4>Ajouter / Modifier un Grade</h4>
<form method="POST" id="rankForm">
<form method="POST" id="rankForm" enctype="multipart/form-data">
<input type="hidden" name="action" value="upsert_grade">
<input type="hidden" name="id" id="rank_id" value="0">
<div style="display: flex; gap: 20px; margin-bottom: 15px;">
@ -862,6 +879,10 @@ if ($tab === 'users') {
<option value="admin">Admin</option>
</select>
</div>
<div class="form-group" style="flex: 1;">
<label>Image (PNG)</label>
<input type="file" name="image" accept="image/png">
</div>
</div>
<div style="display: flex; gap: 20px;">
<div class="form-group" style="flex: 1;">
@ -883,6 +904,7 @@ if ($tab === 'users') {
<table>
<thead>
<tr>
<th>Image</th>
<th>Nom</th>
<th>Slug</th>
<th>Type</th>
@ -892,14 +914,21 @@ if ($tab === 'users') {
</thead>
<tbody>
<?php if (empty($ranks_list)): ?>
<tr><td colspan="5" style="text-align: center;">Aucun grade configuré.</td></tr>
<tr><td colspan="6" style="text-align: center;">Aucun grade configuré.</td></tr>
<?php else: ?>
<?php foreach ($ranks_list as $r): ?>
<tr>
<td>
<?php if (!empty($r["image_url"])): ?>
<img src="<?php echo htmlspecialchars($r["image_url"]); ?>?v=<?php echo time(); ?>" style="max-width: 40px; max-height: 40px;">
<?php else: ?>
-
<?php endif; ?>
</td>
<td><strong><?php echo htmlspecialchars($r['name']); ?></strong></td>
<td><code><?php echo htmlspecialchars($r['slug']); ?></code></td>
<td>
<span class="badge <?php echo $r['user_type'] === 'admin' ? 'tag-malus' : ($r['user_type'] === 'GM' ? 'tag-bonus' : ''); ?>"
<span class="badge"
style="background: <?php echo $r['user_type'] === 'admin' ? '#bf616a' : ($r['user_type'] === 'GM' ? '#ebcb8b' : '#88c0d0'); ?>; color: #2e3440; padding: 2px 8px; border-radius: 10px; font-size: 10px; text-transform: uppercase; font-weight: bold;">
<?php echo htmlspecialchars($r['user_type']); ?>
</span>
@ -1650,475 +1679,12 @@ if ($tab === 'users') {
document.getElementById('log_id').value = 0;
document.getElementById('logForm').reset();
}
function toggleMS(id) {
const d = document.getElementById(id);
d.style.display = d.style.display === 'block' ? 'none' : 'block';
}
function updateMSLabel(containerId) {
const container = document.getElementById(containerId);
const checkboxes = container.querySelectorAll('input[type="checkbox"]:checked');
const display = container.querySelector('.ms-display');
if (checkboxes.length === 0) {
display.innerText = "Toutes / Peu importe";
} else {
const labels = Array.from(checkboxes).map(cb => cb.parentElement.innerText.trim());
display.innerText = labels.join(', ');
}
}
document.addEventListener('click', function(e) {
if (!e.target.closest('.ms-container')) {
document.querySelectorAll('.ms-dropdown').forEach(d => d.style.display = 'none');
}
});
function editLevel(data) {
document.getElementById("level_id").value = data.id;
document.getElementById("level_name").value = data.name;
document.getElementById("level_slug").value = data.slug;
document.getElementById("level_resource_id").value = data.resource_id;
document.getElementById("level_required_quantity").value = data.required_quantity;
window.scrollTo(0,0);
}
function resetLevelForm() {
document.getElementById("levelForm").reset();
document.getElementById("level_id").value = 0;
const select = document.getElementById("level_resource_id");
for (let i = 0; i < select.options.length; i++) {
if (select.options[i].text.toLowerCase().includes("expérience") || select.options[i].text.toLowerCase().includes("experience")) {
select.selectedIndex = i;
break;
}
}
}
function updateRankFields() {
const type = document.getElementById('rank_type').value;
const min = document.getElementById('rank_min');
const max = document.getElementById('rank_max');
if (type === 'utilisateur') {
min.disabled = false;
max.disabled = false;
min.required = true;
max.required = true;
min.style.opacity = '1';
max.style.opacity = '1';
} else {
min.disabled = true;
max.disabled = true;
min.required = false;
max.required = false;
min.style.opacity = '0.5';
max.style.opacity = '0.5';
min.value = '';
max.value = '';
}
}
function editRank(rank) {
document.getElementById('rank_id').value = rank.id;
document.getElementById('rank_name').value = rank.name;
document.getElementById('rank_slug').value = rank.slug;
document.getElementById('rank_type').value = rank.user_type;
document.getElementById('rank_min').value = rank.min_level || '';
document.getElementById('rank_max').value = rank.max_level || '';
updateRankFields();
window.scrollTo({ top: 0, behavior: 'smooth' });
}
function resetRankForm() {
document.getElementById('rankForm').reset();
document.getElementById('rank_id').value = '0';
updateRankFields();
}
// Initial call to set fields on load
document.addEventListener('DOMContentLoaded', function() {
if (document.getElementById('rank_type')) {
updateRankFields();
}
});
</script>
<?php elseif ($tab === 'lootboxes'): ?>
<h3 style="color: #88c0d0;">Système de Lootboxes</h3>
<div class="form-card">
<h4>Créer / Modifier une Lootbox</h4>
<form method="POST" id="lootboxForm">
<input type="hidden" name="action" value="upsert_lootbox">
<input type="hidden" name="id" id="lb_id" value="0">
<div style="display: flex; gap: 20px;">
<div class="form-group" style="flex: 2;">
<label>Nom de la Lootbox</label>
<input type="text" name="name" id="lb_name" required placeholder="Ex: Coffre Mystère">
</div>
<div class="form-group" style="flex: 1;">
<label>Slug</label>
<input type="text" name="slug" id="lb_slug" required placeholder="Ex: coffre_mystere">
</div>
</div>
<div class="form-group">
<label>Description</label>
<textarea name="description" id="lb_desc" rows="2"></textarea>
</div>
<div style="display: flex; gap: 20px; margin-top: 20px;">
<!-- ROLLS SECTION -->
<div style="flex: 0 0 350px; border: 1px solid #2d3545; padding: 15px; border-radius: 8px; background: #0f172a;">
<label style="color: #88c0d0; font-weight: bold; font-size: 14px; margin-bottom: 15px; display: block; border-bottom: 1px solid #2d3545; padding-bottom: 5px;">
Probabilité du nombre total d'objets
</label>
<div class="sub-form-header">
<div style="flex: 1;">Nb Total</div>
<div style="flex: 1;">Chance (%)</div>
<div class="btn-del-row-placeholder"></div>
</div>
<div id="rolls_container">
<!-- Dynamic rows for rolls -->
</div>
<button type="button" class="btn btn-ok" style="margin-top: 10px; width: 100%;" onclick="addRollRow()">+ Ajouter un palier</button>
</div>
<!-- ITEMS SECTION -->
<div style="flex: 1; border: 1px solid #2d3545; padding: 15px; border-radius: 8px; background: #0f172a;">
<label style="color: #88c0d0; font-weight: bold; font-size: 14px; margin-bottom: 15px; display: block; border-bottom: 1px solid #2d3545; padding-bottom: 5px;">
Objets en poule (Attribution direct ou pool aléatoire)
</label>
<div class="sub-form-header">
<div style="flex: 0 0 100px;">Type</div>
<div style="flex: 2;">Ressource / Objet</div>
<div style="flex: 1;" class="chance-header">Chance (%)</div>
<div style="flex: 0 0 70px;">Qté Min</div>
<div style="flex: 0 0 70px;">Qté Max</div>
<div class="btn-del-row-placeholder"></div>
</div>
<div id="items_container">
<!-- Dynamic rows for items -->
</div>
<button type="button" class="btn btn-ok" style="margin-top: 10px; width: 100%;" onclick="addItemRow()">+ Ajouter un objet</button>
</div>
</div>
<div style="margin-top: 30px; text-align: right;">
<button type="button" class="btn" style="background: #4c566a; color: #fff; margin-right: 10px;" onclick="resetLootboxForm()">ANNULER TOUT</button>
<button type="submit" class="btn btn-add" style="padding: 12px 30px;">ENREGISTRER LA CONFIGURATION</button>
</div>
</form>
</div>
<table>
<thead><tr><th>Nom</th><th>Slug</th><th>Objets Directs (100%)</th><th>Nb Total (%)</th><th>Pool Aléatoire (%)</th><th>Actions</th></tr></thead>
<tbody>
<?php foreach ($lootboxes_list as $lb): ?>
<tr>
<td><strong><?php echo htmlspecialchars($lb['name']); ?></strong></td>
<td><code><?php echo htmlspecialchars($lb['slug']); ?></code></td>
<td>
<small>
<?php
$directs = array_filter($lb['items'], fn($i) => $i['is_guaranteed']);
foreach ($directs as $d): ?>
<div style="margin-bottom: 2px; color: #a3be8c;">
<i class="fa-solid fa-bolt"></i> <?php echo htmlspecialchars($d['resource_slug']); ?>
<span style="color: #8c92a3;">(Qté: <?php echo $d['quantity_min']; ?>-<?php echo $d['quantity_max']; ?>)</span>
</div>
<?php endforeach; if(empty($directs)) echo "<em>Aucun</em>"; ?>
</small>
</td>
<td>
<small>
<?php foreach ($lb['rolls'] as $r): ?>
<div style="margin-bottom: 2px;">
<span style="color: #88c0d0;"><?php echo $r['roll_count']; ?> objet(s)</span>:
<strong><?php echo $r['probability']; ?>%</strong>
</div>
<?php endforeach; ?>
</small>
</td>
<td>
<small>
<?php
$pool = array_filter($lb['items'], fn($i) => !$i['is_guaranteed']);
foreach ($pool as $i): ?>
<div style="margin-bottom: 2px;">
<?php echo $i['resource_slug'] ?: '<em style="color:#bf616a">(Rien)</em>'; ?>:
<strong><?php echo $i['probability']; ?>%</strong>
<span style="color: #8c92a3;">(Qté: <?php echo $i['quantity_min']; ?>-<?php echo $i['quantity_max']; ?>)</span>
</div>
<?php endforeach; ?>
</small>
</td>
<td>
<button class="btn btn-edit" onclick='editLootbox(<?php echo json_encode($lb, JSON_HEX_APOS); ?>)'>Editer</button>
<a href="?tab=lootboxes&delete_lootbox=<?php echo $lb['id']; ?>" class="btn btn-del" onclick="return confirm('Supprimer cette lootbox ?')">Suppr</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</script>
<?php endif; ?>
</div>
<script>
// --- SHARED RESOURCES ---
const resourcesList = <?php echo json_encode($resources_list); ?>;
function editObject(data) {
document.getElementById('obj_id').value = data.id;
document.getElementById('obj_name').value = data.name;
document.getElementById('obj_slug').value = data.slug;
document.getElementById('obj_icon').value = data.icon;
document.getElementById('obj_desc').value = data.description;
document.getElementById('obj_orbital_enabled').checked = data.orbital_control_enabled == 1;
document.getElementById('obj_terrestrial_enabled').checked = data.terrestrial_control_enabled == 1;
document.getElementById('obj_profile_id').value = data.status_profile_id || "";
document.querySelectorAll('.modifier-checkbox').forEach(cb => cb.checked = false);
if (data.modifier_ids) {
data.modifier_ids.forEach(mid => {
const cb = document.querySelector(`.modifier-checkbox[value="${mid}"]`);
if (cb) cb.checked = true;
});
}
window.scrollTo(0,0);
}
function resetObjectForm() { document.getElementById('objectForm').reset(); document.getElementById('obj_id').value = 0; document.querySelectorAll('.modifier-checkbox').forEach(cb => cb.checked = false); }
function editProfile(data) {
document.getElementById("prof_id").value = data.id;
document.getElementById("prof_name").value = data.name;
document.getElementById("prof_slug").value = data.slug;
}
function resetProfileForm() {
document.getElementById("prof_id").value = 0;
document.getElementById("prof_name").value = "";
document.getElementById("prof_slug").value = "";
}
function editStatus(data) {
document.getElementById("st_id").value = data.id;
document.getElementById("st_name").value = data.name;
document.getElementById("st_slug").value = data.slug;
let color = data.color || "#000000";
let isBlinking = color.includes(";blink");
let pureColor = color.replace(";blink", "");
document.getElementById("st_color").value = pureColor;
document.getElementById("st_is_blinking").checked = isBlinking;
if (document.getElementById("st_color_picker")) {
document.getElementById("st_color_picker").value = pureColor.startsWith("#") ? pureColor : "#000000";
}
document.getElementById("st_desc").value = data.description;
window.scrollTo(0,0);
}
function resetStatusForm() {
document.getElementById("statusForm").reset();
document.getElementById("st_id").value = 0;
document.getElementById("st_color_picker").value = "#000000";
document.getElementById("st_is_blinking").checked = false;
}
function editRule(data) {
document.getElementById('rule_id').value = data.id;
document.getElementById('rule_name').value = data.name;
document.getElementById('rule_profile_id').value = data.profile_id || "";
document.getElementById('rule_status_id').value = data.status_id;
document.getElementById('rule_priority').value = data.priority || 0;
document.getElementById('rule_orb_op').value = data.orbital_count_op || "";
document.getElementById('rule_orb_val').value = data.orbital_count_val !== null ? data.orbital_count_val : "";
document.getElementById('rule_terr_op').value = data.terrestrial_count_op || "";
document.getElementById('rule_terr_val').value = data.terrestrial_count_val !== null ? data.terrestrial_count_val : "";
// Set multi-selects
const orbVals = (data.orbital_dominance || "").split(",");
document.querySelectorAll('#ms_orb_list input').forEach(cb => {
cb.checked = orbVals.includes(cb.value);
});
updateMSLabel('ms_orb');
const terrVals = (data.terrestrial_dominance || "").split(",");
document.querySelectorAll('#ms_terr_list input').forEach(cb => {
cb.checked = terrVals.includes(cb.value);
});
updateMSLabel('ms_terr');
document.getElementById('rule_empty').checked = data.is_empty_case == 1;
document.getElementById('rule_combine').value = data.combine_mode || "OR";
window.scrollTo(0,0);
}
function resetRuleForm() {
document.getElementById('ruleForm').reset();
document.getElementById('rule_id').value = 0; document.getElementById('rule_combine').value = 'OR';
updateMSLabel('ms_orb');
updateMSLabel('ms_terr');
}
function editSettlementType(data) {
document.getElementById('set_t_id').value = data.id;
document.getElementById('set_t_name').value = data.name;
document.getElementById('set_t_slug').value = data.slug;
document.getElementById('set_t_desc').value = data.description;
window.scrollTo(0,0);
}
function resetSettlementTypeForm() { document.getElementById('settlementTypeForm').reset(); document.getElementById('set_t_id').value = 0; }
function editModifier(data) {
document.getElementById('mod_id').value = data.id;
document.getElementById('mod_name').value = data.name;
document.getElementById('mod_slug').value = data.slug || '';
document.getElementById('mod_type').value = data.type;
document.getElementById('mod_desc').value = data.description;
window.scrollTo(0,0);
}
function resetModifierForm() { document.getElementById('modifierForm').reset(); document.getElementById('mod_id').value = 0; }
function editFaction(data) {
document.getElementById('fac_id').value = data.id;
document.getElementById('fac_name').value = data.name;
document.getElementById('fac_slug').value = data.slug || '';
const color = data.color || '#808080';
document.getElementById('fac_color').value = color;
if (document.getElementById('fac_color_picker')) {
document.getElementById('fac_color_picker').value = color.startsWith('#') ? color : '#808080';
}
document.getElementById('fac_fa_icon').value = data.fa_icon || '';
// Handle Alliances
document.querySelectorAll('.alliance-checkbox').forEach(cb => cb.checked = false);
document.querySelectorAll('.alliance-label').forEach(lbl => lbl.style.display = 'flex');
// Hide current faction from alliance list
const currentLabel = document.querySelector(`.alliance-label[data-id="${data.id}"]`);
if (currentLabel) currentLabel.style.display = 'none';
if (data.alliance_ids) {
data.alliance_ids.forEach(aid => {
const cb = document.querySelector(`.alliance-checkbox[value="${aid}"]`);
if (cb) cb.checked = true;
});
}
window.scrollTo(0,0);
}
function resetFactionForm() {
document.getElementById('factionForm').reset();
document.getElementById('fac_id').value = 0;
document.getElementById('fac_color').value = '#808080';
document.getElementById('fac_color_picker').value = '#808080';
document.querySelectorAll('.alliance-checkbox').forEach(cb => cb.checked = false);
document.querySelectorAll('.alliance-label').forEach(lbl => lbl.style.display = 'flex');
}
function editResource(data) {
document.getElementById('res_id').value = data.id;
document.getElementById('res_name').value = data.name;
document.getElementById('res_slug').value = data.slug;
document.getElementById('res_icon').value = data.icon || '';
document.getElementById('res_desc').value = data.description || '';
document.getElementById('res_show').checked = (data.show_in_header == 1);
window.scrollTo(0,0);
}
function resetResourceForm() { document.getElementById('resourceForm').reset(); document.getElementById('res_id').value = 0; }
// --- LOOTBOX SYSTEM ---
function addRollRow(count = 1, prob = 100) {
const container = document.getElementById('rolls_container');
const row = document.createElement('div');
row.className = 'sub-form-row';
row.innerHTML = `
<input type="number" name="rolls_count[]" value="${count}" placeholder="Nb total" min="0">
<input type="number" name="rolls_prob[]" value="${prob}" placeholder="Chance %" step="0.01">
<div class="btn-del-row" onclick="this.parentElement.remove()">×</div>
`;
container.appendChild(row);
}
function toggleItemType(cb) {
const row = cb.closest('.sub-form-row');
const probInput = row.querySelector('.item-prob-input');
const typeLabel = row.querySelector('.type-label');
if (cb.checked) {
probInput.style.visibility = 'hidden';
probInput.value = 100;
typeLabel.textContent = 'DIRECT';
typeLabel.style.color = '#a3be8c';
} else {
probInput.style.visibility = 'visible';
typeLabel.textContent = 'POULE';
typeLabel.style.color = '#8c92a3';
}
}
function addItemRow(slug = '', prob = 0, qmin = 1, qmax = 1, isGuaranteed = 0) {
const container = document.getElementById('items_container');
const rowIdx = container.children.length;
const row = document.createElement('div');
row.className = 'sub-form-row';
let options = '<option value="">(Rien)</option>';
resourcesList.forEach(r => {
options += `<option value="${r.slug}" ${slug === r.slug ? 'selected' : ''}>${r.name}</option>`;
});
row.innerHTML = `
<div class="toggle-container">
<label class="switch">
<input type="hidden" name="item_is_guaranteed[${rowIdx}]" value="0">
<input type="checkbox" name="item_is_guaranteed[${rowIdx}]" value="1" ${isGuaranteed ? 'checked' : ''} onchange="toggleItemType(this)">
<span class="slider"></span>
</label>
<span class="type-label" style="color: ${isGuaranteed ? '#a3be8c' : '#8c92a3'}">${isGuaranteed ? 'DIRECT' : 'POULE'}</span>
</div>
<select name="item_slug[${rowIdx}]" style="flex: 2;">${options}</select>
<input type="number" name="item_prob[${rowIdx}]" value="${prob}" class="item-prob-input" style="flex: 1; ${isGuaranteed ? 'visibility:hidden' : ''}" placeholder="Prob %" step="0.01">
<input type="number" name="item_qmin[${rowIdx}]" value="${qmin}" style="flex: 0 0 70px;" placeholder="Min">
<input type="number" name="item_qmax[${rowIdx}]" value="${qmax}" style="flex: 0 0 70px;" placeholder="Max">
<div class="btn-del-row" onclick="this.parentElement.remove()">×</div>
`;
container.appendChild(row);
}
function editLootbox(data) {
document.getElementById('lb_id').value = data.id;
document.getElementById('lb_name').value = data.name;
document.getElementById('lb_slug').value = data.slug;
document.getElementById('lb_desc').value = data.description || '';
document.getElementById('rolls_container').innerHTML = '';
if (data.rolls && data.rolls.length > 0) {
data.rolls.forEach(r => addRollRow(r.roll_count, r.probability));
} else {
addRollRow();
}
document.getElementById('items_container').innerHTML = '';
if (data.items && data.items.length > 0) {
data.items.forEach((i, idx) => addItemRow(i.resource_slug, i.probability, i.quantity_min, i.quantity_max, i.is_guaranteed));
} else {
addItemRow();
}
window.scrollTo(0,0);
}
function resetLootboxForm() {
document.getElementById('lootboxForm').reset();
document.getElementById('lb_id').value = 0;
document.getElementById('rolls_container').innerHTML = '';
document.getElementById('items_container').innerHTML = '';
addRollRow();
addItemRow();
}
<?php if ($tab === 'lootboxes'): ?>
window.onload = function() {
if (document.getElementById('rolls_container').children.length === 0) addRollRow();
if (document.getElementById('items_container').children.length === 0) addItemRow();
};
<?php endif; ?>
function toggleMS(id) {
const d = document.getElementById(id);
d.style.display = d.style.display === 'block' ? 'none' : 'block';
@ -2207,6 +1773,96 @@ document.addEventListener('DOMContentLoaded', function() {
}
});
// --- LOOTBOX SYSTEM ---
const resourcesList = <?php echo json_encode($resources_list); ?>;
function addRollRow(count = 1, prob = 100) {
const container = document.getElementById('rolls_container');
const row = document.createElement('div');
row.className = 'sub-form-row';
row.innerHTML = `
<input type="number" name="rolls_count[]" value="${count}" placeholder="Nb total" min="0">
<input type="number" name="rolls_prob[]" value="${prob}" placeholder="Chance %" step="0.01">
<div class="btn-del-row" onclick="this.parentElement.remove()">×</div>
`;
container.appendChild(row);
}
function toggleItemType(cb) {
const row = cb.closest('.sub-form-row');
const probInput = row.querySelector('.item-prob-input');
const typeLabel = row.querySelector('.type-label');
if (cb.checked) {
probInput.style.visibility = 'hidden';
probInput.value = 100;
typeLabel.textContent = 'DIRECT';
typeLabel.style.color = '#a3be8c';
} else {
probInput.style.visibility = 'visible';
typeLabel.textContent = 'POULE';
typeLabel.style.color = '#8c92a3';
}
}
function addItemRow(slug = '', prob = 0, qmin = 1, qmax = 1, isGuaranteed = 0) {
const container = document.getElementById('items_container');
const rowIdx = container.children.length;
const row = document.createElement('div');
row.className = 'sub-form-row';
let options = '<option value="">(Rien)</option>';
resourcesList.forEach(r => {
options += `<option value="${r.slug}" ${slug === r.slug ? 'selected' : ''}>${r.name}</option>`;
});
row.innerHTML = `
<div class="toggle-container">
<label class="switch">
<input type="hidden" name="item_is_guaranteed[${rowIdx}]" value="0">
<input type="checkbox" name="item_is_guaranteed[${rowIdx}]" value="1" ${isGuaranteed ? 'checked' : ''} onchange="toggleItemType(this)">
<span class="slider"></span>
</label>
<span class="type-label" style="color: ${isGuaranteed ? '#a3be8c' : '#8c92a3'}">${isGuaranteed ? 'DIRECT' : 'POULE'}</span>
</div>
<select name="item_slug[${rowIdx}]" style="flex: 2;">${options}</select>
<input type="number" name="item_prob[${rowIdx}]" value="${prob}" class="item-prob-input" style="flex: 1; ${isGuaranteed ? 'visibility:hidden' : ''}" placeholder="Prob %" step="0.01">
<input type="number" name="item_qmin[${rowIdx}]" value="${qmin}" style="flex: 0 0 70px;" placeholder="Min">
<input type="number" name="item_qmax[${rowIdx}]" value="${qmax}" style="flex: 0 0 70px;" placeholder="Max">
<div class="btn-del-row" onclick="this.parentElement.remove()">×</div>
`;
container.appendChild(row);
}
function editLootbox(data) {
document.getElementById('lb_id').value = data.id;
document.getElementById('lb_name').value = data.name;
document.getElementById('lb_slug').value = data.slug;
document.getElementById('lb_desc').value = data.description || '';
document.getElementById('rolls_container').innerHTML = '';
if (data.rolls && data.rolls.length > 0) {
data.rolls.forEach(r => addRollRow(r.roll_count, r.probability));
} else {
addRollRow();
}
document.getElementById('items_container').innerHTML = '';
if (data.items && data.items.length > 0) {
data.items.forEach((i, idx) => addItemRow(i.resource_slug, i.probability, i.quantity_min, i.quantity_max, i.is_guaranteed));
} else {
addItemRow();
}
window.scrollTo(0,0);
}
function resetLootboxForm() {
document.getElementById('lootboxForm').reset();
document.getElementById('lb_id').value = 0;
document.getElementById('rolls_container').innerHTML = '';
document.getElementById('items_container').innerHTML = '';
addRollRow();
addItemRow();
}
<?php if ($tab === 'lootboxes'): ?>
window.onload = function() {
if (document.getElementById('rolls_container').children.length === 0) addRollRow();
if (document.getElementById('items_container').children.length === 0) addItemRow();
};
<?php endif; ?>
</script>
</body>
</html>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@ -52,7 +52,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($user && password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION["username"] = $user["username"]; $_SESSION["display_name"] = $user["display_name"];
$_SESSION['role'] = $user['role'];
$db->prepare("UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = ?")->execute([$user['id']]);

View File

@ -557,7 +557,7 @@ function getStatusColor($status, $statuses_map) {
<header id="top-bar">
<div class="user-auth-bar">
<?php if (isset($_SESSION['user_id'])): ?>
<span>Bienvenue, <span class="username">@<?php echo htmlspecialchars($_SESSION['username']); ?></span></span>
<span>Bienvenue, <span class="username">@<?php echo htmlspecialchars($_SESSION["display_name"] ?? $_SESSION["username"]); ?></span></span>
<a href="project_log.php"><i class="fa-solid fa-clipboard-list"></i> Journal</a> <a href="profile.php"><i class="fa-solid fa-user-gear"></i> Profil</a>
<a href="auth.php?logout=1" style="color: #bf616a;"><i class="fa-solid fa-right-from-bracket"></i> Déconnexion</a>
<?php else: ?>

View File

@ -12,8 +12,13 @@ $user_id = $_SESSION['user_id'];
$error = '';
$success = '';
// Fetch current user data
$stmt = $db->prepare("SELECT * FROM users WHERE id = ?");
// Fetch current user data + join for stats
$stmt = $db->prepare("
SELECT u.*, l.level_name, g.grade_name
FROM users u
LEFT JOIN levels l ON u.level_id = l.id
LEFT JOIN grades g ON u.level_id = g.level_id
WHERE u.id = ?");
$stmt->execute([$user_id]);
$user = $stmt->fetch();
@ -33,27 +38,41 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} elseif (!password_verify($current_password, $user['password'])) {
$error = 'Mot de passe actuel incorrect.';
} else {
// Update password if provided
$sql = "UPDATE users SET email = ?";
$params = [$email];
if (!empty($new_password)) {
if ($new_password !== $confirm_password) {
$error = 'Les nouveaux mots de passe ne correspondent pas.';
} else {
$hashed_password = password_hash($new_password, PASSWORD_DEFAULT);
$stmt = $db->prepare("UPDATE users SET email = ?, password = ? WHERE id = ?");
$stmt->execute([$email, $hashed_password, $user_id]);
$success = 'Profil et mot de passe mis à jour avec succès.';
$sql .= ", password = ?";
$params[] = password_hash($new_password, PASSWORD_DEFAULT);
}
} else {
$stmt = $db->prepare("UPDATE users SET email = ? WHERE id = ?");
$stmt->execute([$email, $user_id]);
$success = 'Profil mis à jour avec succès.';
}
$sql .= " WHERE id = ?";
$params[] = $user_id;
$stmt = $db->prepare($sql);
$stmt->execute($params);
$success = 'Profil mis à jour avec succès.';
// Refresh user data
$stmt = $db->prepare("SELECT * FROM users WHERE id = ?");
$stmt = $db->prepare("SELECT u.*, l.level_name, g.grade_name FROM users u LEFT JOIN levels l ON u.level_id = l.id LEFT JOIN grades g ON u.level_id = g.level_id WHERE u.id = ?");
$stmt->execute([$user_id]);
$user = $stmt->fetch();
}
} elseif ($action === 'update_display_name') {
$display_name = trim($_POST['display_name'] ?? '');
if (!empty($display_name)) {
$stmt = $db->prepare("UPDATE users SET display_name = ? WHERE id = ?");
$stmt->execute([$display_name, $user_id]);
$_SESSION["display_name"] = $display_name; $success = "Nom affiché mis à jour avec succès.";;
$stmt = $db->prepare("SELECT u.*, l.level_name, g.grade_name FROM users u LEFT JOIN levels l ON u.level_id = l.id LEFT JOIN grades g ON u.level_id = g.level_id WHERE u.id = ?");
$stmt->execute([$user_id]);
$user = $stmt->fetch();
} else {
$error = 'Le nom affiché ne peut pas être vide.';
}
}
}
?>
@ -65,65 +84,65 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<title>Mon Profil - Nexus</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
<style>
body {
background: #000;
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
color: #fff;
background-image: radial-gradient(circle at 50% 50%, #1a2a4a 0%, #000 70%);
background-attachment: fixed;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.profile-container {
background: rgba(10, 15, 30, 0.95);
border: 1px solid #4c566a;
padding: 30px;
width: 100%;
max-width: 500px;
box-shadow: 0 0 20px rgba(0,0,0,0.8);
}
h2 { text-transform: uppercase; color: #88c0d0; border-bottom: 1px solid #4c566a; padding-bottom: 10px; margin-top: 0; display: flex; align-items: center; gap: 10px; }
body { background: #000; margin: 0; padding: 20px; font-family: Arial, sans-serif; color: #fff; background-image: radial-gradient(circle at 50% 50%, #1a2a4a 0%, #000 70%); min-height: 100vh; }
.profile-container { background: rgba(10, 15, 30, 0.95); border: 1px solid #4c566a; padding: 30px; width: 100%; max-width: 600px; margin: 0 auto; box-shadow: 0 0 20px rgba(0,0,0,0.8); }
h2 { text-transform: uppercase; color: #88c0d0; border-bottom: 1px solid #4c566a; padding-bottom: 10px; display: flex; align-items: center; gap: 10px; }
.tabs { display: flex; gap: 10px; margin-bottom: 20px; border-bottom: 1px solid #4c566a; padding-bottom: 10px; }
.tab-btn { background: none; border: none; color: #8c92a3; cursor: pointer; padding: 10px 15px; text-transform: uppercase; font-weight: bold; }
.tab-btn.active { color: #88c0d0; border-bottom: 2px solid #88c0d0; }
.tab-content { display: none; }
.tab-content.active { display: block; }
.form-group { margin-bottom: 20px; }
label { display: block; margin-bottom: 5px; color: #8c92a3; font-size: 14px; }
input { width: 100%; padding: 10px; background: #000; border: 1px solid #4c566a; color: #fff; box-sizing: border-box; }
.password-section { margin-top: 25px; border-top: 1px solid #2d3545; padding-top: 15px; }
.password-section h3 { font-size: 14px; text-transform: uppercase; color: #ebcb8b; margin-top: 0; }
button { width: 100%; padding: 12px; background: #5e81ac; border: none; color: #fff; font-weight: bold; cursor: pointer; text-transform: uppercase; margin-top: 10px; transition: background 0.2s; }
.inline-form { display: flex; gap: 10px; align-items: end; }
.inline-form input { flex-grow: 1; }
.inline-form button { width: auto; padding: 10px 20px; }
button { width: 100%; padding: 12px; background: #5e81ac; border: none; color: #fff; font-weight: bold; cursor: pointer; text-transform: uppercase; transition: background 0.2s; }
button:hover { background: #81a1c1; }
.stat-card { padding: 15px; background: rgba(0,0,0,0.3); border: 1px solid #2d3545; margin-bottom: 10px; }
.alert { padding: 10px; margin-bottom: 20px; font-size: 14px; }
.alert-error { background: rgba(191, 97, 106, 0.2); border: 1px solid #bf616a; color: #bf616a; }
.alert-success { background: rgba(163, 190, 140, 0.2); border: 1px solid #a3be8c; color: #a3be8c; }
.nav-links { display: flex; justify-content: space-between; margin-top: 25px; border-top: 1px solid #2d3545; padding-top: 15px; font-size: 13px; }
.nav-links a { color: #88c0d0; text-decoration: none; }
.nav-links a:hover { text-decoration: underline; }
.username-display { font-size: 18px; color: #81a1c1; margin-bottom: 20px; padding: 5px 10px; background: rgba(0,0,0,0.3); border-radius: 4px; display: inline-block; }
</style>
</head>
<body>
<div class="profile-container">
<h2><i class="fa-solid fa-user-gear"></i> Gestion du Compte</h2>
<div class="username-display">
<i class="fa-solid fa-id-card"></i> @<?php echo htmlspecialchars($user['username']); ?>
<div class="tabs">
<button class="tab-btn active" onclick="openTab('overview')">Vue d\'ensemble</button>
<button class="tab-btn" onclick="openTab('account')">Gestion du compte</button>
</div>
<?php if ($error): ?><div class="alert alert-error"><?php echo $error; ?></div><?php endif; ?>
<?php if ($success): ?><div class="alert alert-success"><?php echo $success; ?></div><?php endif; ?>
<form method="POST">
<input type="hidden" name="action" value="update_profile">
<div id="overview" class="tab-content active">
<h2><i class="fa-solid fa-chart-line"></i> Vue d\'ensemble</h2>
<div class="stat-card"><strong>Pseudo de compte:</strong> @<?php echo htmlspecialchars($user['username']); ?></div>
<div class="form-group">
<label>Adresse Email</label>
<input type="email" name="email" required value="<?php echo htmlspecialchars($user['email']); ?>">
</div>
<form method="POST" class="form-group">
<input type="hidden" name="action" value="update_display_name">
<label>Nom affiché (Jeu)</label>
<div class="inline-form">
<input type="text" name="display_name" value="<?php echo htmlspecialchars($user['display_name']); ?>">
<button type="submit">Sauvegarder</button>
</div>
</form>
<div class="password-section">
<h3>Changer le mot de passe (Optionnel)</h3>
<div class="stat-card"><strong>Niveau:</strong> <?php echo htmlspecialchars($user['level_name'] ?? 'N/A'); ?></div>
<div class="stat-card"><strong>Grade:</strong> <?php echo htmlspecialchars($user['grade_name'] ?? 'N/A'); ?></div>
</div>
<div id="account" class="tab-content">
<h2><i class="fa-solid fa-user-gear"></i> Gestion du Compte</h2>
<form method="POST">
<input type="hidden" name="action" value="update_profile">
<div class="form-group">
<label>Adresse Email</label>
<input type="email" name="email" required value="<?php echo htmlspecialchars($user['email']); ?>">
</div>
<div class="form-group">
<label>Nouveau mot de passe</label>
<input type="password" name="new_password" placeholder="Laisser vide pour ne pas changer">
@ -132,20 +151,26 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<label>Confirmer le nouveau mot de passe</label>
<input type="password" name="confirm_password">
</div>
</div>
<div class="form-group" style="margin-top: 20px;">
<label>Mot de passe actuel (requis pour valider)</label>
<input type="password" name="current_password" required>
</div>
<button type="submit">Enregistrer les modifications</button>
</form>
<div class="form-group">
<label>Mot de passe actuel (requis pour valider)</label>
<input type="password" name="current_password" required>
</div>
<button type="submit">Enregistrer les modifications</button>
</form>
</div>
<div class="nav-links">
<a href="index.php"><i class="fa-solid fa-arrow-left"></i> Retour au Nexus</a>
<a href="auth.php?logout=1" style="color: #bf616a;"><i class="fa-solid fa-right-from-bracket"></i> Déconnexion</a>
</div>
</div>
<script>
function openTab(id) {
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
document.getElementById(id).classList.add('active');
event.currentTarget.classList.add('active');
}
</script>
</body>
</html>