Autosave: 20260221-120345
This commit is contained in:
parent
6badd0c4e6
commit
0e0596eb17
@ -55,7 +55,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$name = $_POST['name'] ?? '';
|
||||
$type = $_POST['type'] ?? 'chat';
|
||||
$status = $_POST['status'] ?? null;
|
||||
$allow_file_sharing = isset($_POST['allow_file_sharing']) ? 1 : 0;
|
||||
if ($type === 'poll') $allow_file_sharing = 0;
|
||||
else $allow_file_sharing = isset($_POST['allow_file_sharing']) ? 1 : 0;
|
||||
$message_limit = !empty($_POST['message_limit']) ? (int)$_POST['message_limit'] : null;
|
||||
$icon = $_POST['icon'] ?? null;
|
||||
if ($icon === '') $icon = null;
|
||||
@ -100,7 +101,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
|
||||
$name = $_POST['name'] ?? '';
|
||||
$type = $_POST['type'] ?? 'text';
|
||||
$user_id = $_SESSION['user_id'];
|
||||
|
||||
// Check if user has permission to manage channels
|
||||
if (Permissions::hasPermission($user_id, $server_id, Permissions::MANAGE_CHANNELS) && ($name || $type === 'separator')) {
|
||||
@ -108,7 +108,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if ($type === 'separator' && !$name) $name = 'separator';
|
||||
// Allow spaces, accents and mixed case
|
||||
$name = trim($name);
|
||||
$allow_file_sharing = isset($_POST['allow_file_sharing']) ? 1 : 0;
|
||||
if ($type === 'poll') $allow_file_sharing = 0;
|
||||
else $allow_file_sharing = isset($_POST['allow_file_sharing']) ? 1 : 0;
|
||||
$message_limit = !empty($_POST['message_limit']) ? (int)$_POST['message_limit'] : null;
|
||||
$icon = $_POST['icon'] ?? null;
|
||||
if ($icon === '') $icon = null;
|
||||
|
||||
@ -188,6 +188,38 @@ if ($_SERVER['REQUEST_METHOD'] === 'PUT') {
|
||||
'description' => $content,
|
||||
'url' => $data['ann_link'] ?? ''
|
||||
]);
|
||||
} elseif (isset($data['is_poll']) && $data['is_poll'] == '1') {
|
||||
// Check permissions
|
||||
if ($msg_data['user_id'] != $user_id && !Permissions::canDoInChannel($user_id, $msg_data['channel_id'], Permissions::MANAGE_MESSAGES)) {
|
||||
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$options = $data['poll_options'] ?? [];
|
||||
$emotes = $data['poll_emotes'] ?? [];
|
||||
|
||||
$stmt_old = db()->prepare("SELECT metadata, created_at FROM messages WHERE id = ?");
|
||||
$stmt_old->execute([$message_id]);
|
||||
$msg_info = $stmt_old->fetch();
|
||||
$old_meta = json_decode($msg_info['metadata'] ?? '{}', true);
|
||||
|
||||
// Recalculate end date if duration provided, otherwise keep old
|
||||
$end_date = $old_meta['poll_end_date'] ?? date('Y-m-d H:i:s', time() + 86400);
|
||||
if (isset($data['poll_duration'])) {
|
||||
$duration = (int)$data['poll_duration'];
|
||||
$end_date = date('Y-m-d H:i:s', strtotime($msg_info['created_at']) + $duration);
|
||||
}
|
||||
|
||||
$metadata = json_encode([
|
||||
'is_poll' => true,
|
||||
'poll_title' => $data['poll_title'] ?? 'Sondage',
|
||||
'poll_options' => $options,
|
||||
'poll_emotes' => $emotes,
|
||||
'poll_choice_type' => ($data['allow_multiple'] ?? '0') == '1' ? 'multiple' : 'single',
|
||||
'is_anonymous' => ($data['is_anonymous'] ?? '0') == '1',
|
||||
'poll_end_date' => $end_date,
|
||||
'poll_color' => $data['poll_color'] ?? '#5865f2'
|
||||
]);
|
||||
}
|
||||
|
||||
if ($metadata) {
|
||||
@ -324,6 +356,27 @@ if (isset($_POST['is_announcement']) && $_POST['is_announcement'] == '1') {
|
||||
]);
|
||||
// Clear content for the message text itself if we want it only in the embed
|
||||
// But keeping it in content might be good for search/fallback
|
||||
} elseif (isset($_POST['is_poll']) && $_POST['is_poll'] == '1') {
|
||||
if (!Permissions::canSendInChannel($user_id, $channel_id)) {
|
||||
echo json_encode(['success' => false, 'error' => 'You do not have permission to create polls in this channel.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$options = json_decode($_POST['poll_options'] ?? '[]', true);
|
||||
$emotes = json_decode($_POST['poll_emotes'] ?? '[]', true);
|
||||
$duration = (int)($_POST['poll_duration'] ?? 86400);
|
||||
$end_date = date('Y-m-d H:i:s', time() + $duration);
|
||||
|
||||
$metadata = json_encode([
|
||||
'is_poll' => true,
|
||||
'poll_title' => $_POST['poll_title'] ?? 'Sondage',
|
||||
'poll_options' => $options,
|
||||
'poll_emotes' => $emotes,
|
||||
'poll_choice_type' => ($_POST['allow_multiple'] ?? '0') == '1' ? 'multiple' : 'single',
|
||||
'is_anonymous' => ($_POST['is_anonymous'] ?? '0') == '1',
|
||||
'poll_end_date' => $end_date,
|
||||
'poll_color' => $_POST['poll_color'] ?? '#5865f2'
|
||||
]);
|
||||
} elseif (!empty($content)) {
|
||||
$urls = extractUrls($content);
|
||||
if (!empty($urls)) {
|
||||
|
||||
81
api_v1_poll_vote.php
Normal file
81
api_v1_poll_vote.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once 'auth/session.php';
|
||||
require_once 'includes/permissions.php';
|
||||
requireLogin();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['success' => false, 'error' => 'Method not allowed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
if (!$data) {
|
||||
$data = $_POST;
|
||||
}
|
||||
|
||||
$message_id = $data['message_id'] ?? 0;
|
||||
$option_index = $data['option_index'] ?? 0;
|
||||
$user_id = $_SESSION['user_id'];
|
||||
|
||||
try {
|
||||
$stmt = db()->prepare("SELECT metadata, channel_id FROM messages WHERE id = ?");
|
||||
$stmt->execute([$message_id]);
|
||||
$msg = $stmt->fetch();
|
||||
|
||||
if (!$msg) {
|
||||
echo json_encode(['success' => false, 'error' => 'Sondage non trouvé']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$meta = json_decode($msg['metadata'], true);
|
||||
if (!$meta || empty($meta['is_poll'])) {
|
||||
echo json_encode(['success' => false, 'error' => 'Ce message n\'est pas un sondage']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Check expiration
|
||||
if (!empty($meta['poll_end_date']) && strtotime($meta['poll_end_date']) < time()) {
|
||||
echo json_encode(['success' => false, 'error' => 'Ce sondage est terminé']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Check permissions
|
||||
if (!Permissions::canViewChannel($user_id, $msg['channel_id'])) {
|
||||
echo json_encode(['success' => false, 'error' => 'Vous n\'avez pas la permission de voter']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$choice_type = $meta['poll_choice_type'] ?? 'single';
|
||||
|
||||
if ($choice_type === 'single') {
|
||||
// Remove previous votes from this user for this poll
|
||||
$stmt = db()->prepare("DELETE FROM poll_votes WHERE message_id = ? AND user_id = ?");
|
||||
$stmt->execute([$message_id, $user_id]);
|
||||
|
||||
// Add new vote
|
||||
$stmt = db()->prepare("INSERT INTO poll_votes (message_id, user_id, option_index) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$message_id, $user_id, $option_index]);
|
||||
} else {
|
||||
// Multiple choice: toggle vote
|
||||
$stmt = db()->prepare("SELECT id FROM poll_votes WHERE message_id = ? AND user_id = ? AND option_index = ?");
|
||||
$stmt->execute([$message_id, $user_id, $option_index]);
|
||||
if ($stmt->fetch()) {
|
||||
$stmt = db()->prepare("DELETE FROM poll_votes WHERE message_id = ? AND user_id = ? AND option_index = ?");
|
||||
$stmt->execute([$message_id, $user_id, $option_index]);
|
||||
} else {
|
||||
$stmt = db()->prepare("INSERT INTO poll_votes (message_id, user_id, option_index) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$message_id, $user_id, $option_index]);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch updated votes
|
||||
$stmt = db()->prepare("SELECT option_index, COUNT(*) as vote_count, GROUP_CONCAT(user_id) as user_ids FROM poll_votes WHERE message_id = ? GROUP BY option_index");
|
||||
$stmt->execute([$message_id]);
|
||||
$votes = $stmt->fetchAll();
|
||||
|
||||
echo json_encode(['success' => true, 'votes' => $votes]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
@ -1571,15 +1571,24 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
rulesRoleContainer.style.display = (channelType === 'rules') ? 'block' : 'none';
|
||||
}
|
||||
|
||||
// Hide limit, files and clear chat for rules, autorole, and role channels
|
||||
const editLimitContainer = document.getElementById('edit-channel-limit-container');
|
||||
const editFilesContainer = document.getElementById('edit-channel-files-container');
|
||||
const clearChatBtn = document.getElementById('clear-channel-history-btn');
|
||||
|
||||
const hideExtra = (channelType === 'rules' || channelType === 'autorole' || isRoleChannel);
|
||||
if (editLimitContainer) editLimitContainer.style.display = hideExtra ? 'none' : 'block';
|
||||
if (editFilesContainer) editFilesContainer.style.display = hideExtra ? 'none' : 'block';
|
||||
if (clearChatBtn) clearChatBtn.style.display = (channelType === 'rules') ? 'none' : 'inline-block';
|
||||
if (editLimitContainer) {
|
||||
editLimitContainer.style.display = (channelType === 'rules' || channelType === 'autorole' || isRoleChannel) ? 'none' : 'block';
|
||||
if (channelType === 'poll') {
|
||||
editLimitContainer.querySelector('.form-label').textContent = 'Limite de sondages';
|
||||
editLimitContainer.querySelector('input').placeholder = 'ex: 10 (Sondages conservés)';
|
||||
} else {
|
||||
editLimitContainer.querySelector('.form-label').textContent = 'Limite de messages';
|
||||
editLimitContainer.querySelector('input').placeholder = 'Conserver tous les messages';
|
||||
}
|
||||
}
|
||||
if (editFilesContainer) {
|
||||
editFilesContainer.style.display = (channelType === 'rules' || channelType === 'autorole' || channelType === 'poll' || isRoleChannel) ? 'none' : 'block';
|
||||
}
|
||||
if (clearChatBtn) clearChatBtn.style.display = (channelType === 'rules' || channelType === 'poll') ? 'none' : 'inline-block';
|
||||
|
||||
// Reset delete zone
|
||||
document.getElementById('delete-confirm-zone').style.display = 'none';
|
||||
@ -1658,9 +1667,18 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const clearChatBtn = document.getElementById('clear-channel-history-btn');
|
||||
|
||||
const hideExtra = (type === 'rules' || type === 'autorole' || isRoleChannel);
|
||||
if (editLimitContainer) editLimitContainer.style.display = hideExtra ? 'none' : 'block';
|
||||
if (editFilesContainer) editFilesContainer.style.display = hideExtra ? 'none' : 'block';
|
||||
if (clearChatBtn) clearChatBtn.style.display = (type === 'rules') ? 'none' : 'inline-block';
|
||||
if (editLimitContainer) {
|
||||
editLimitContainer.style.display = (hideExtra && type !== 'poll') ? 'none' : 'block';
|
||||
if (type === 'poll') {
|
||||
editLimitContainer.querySelector('.form-label').textContent = 'Limite de sondages';
|
||||
editLimitContainer.querySelector('input').placeholder = 'ex: 10 (Sondages conservés)';
|
||||
} else {
|
||||
editLimitContainer.querySelector('.form-label').textContent = 'Rétention des messages';
|
||||
editLimitContainer.querySelector('input').placeholder = 'Conserver tous les messages';
|
||||
}
|
||||
}
|
||||
if (editFilesContainer) editFilesContainer.style.display = (hideExtra || type === 'poll') ? 'none' : 'block';
|
||||
if (clearChatBtn) clearChatBtn.style.display = (type === 'rules' || type === 'poll') ? 'none' : 'inline-block';
|
||||
});
|
||||
|
||||
// RSS Management
|
||||
@ -2955,8 +2973,17 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
const limitContainer = document.getElementById('add-channel-limit-container');
|
||||
const filesContainer = document.getElementById('add-channel-files-container');
|
||||
if (limitContainer) limitContainer.style.display = (type === 'rules' || type === 'autorole') ? 'none' : 'block';
|
||||
if (filesContainer) filesContainer.style.display = (type === 'rules' || type === 'autorole') ? 'none' : 'block';
|
||||
if (limitContainer) {
|
||||
limitContainer.style.display = (type === 'rules' || type === 'autorole') ? 'none' : 'block';
|
||||
if (type === 'poll') {
|
||||
limitContainer.querySelector('.form-label').textContent = 'Limite de sondages';
|
||||
limitContainer.querySelector('.form-text').textContent = 'Conserve automatiquement seulement les X derniers sondages de ce salon.';
|
||||
} else {
|
||||
limitContainer.querySelector('.form-label').textContent = 'Limite de messages';
|
||||
limitContainer.querySelector('.form-text').textContent = 'Conserve automatiquement seulement les X derniers messages de ce salon.';
|
||||
}
|
||||
}
|
||||
if (filesContainer) filesContainer.style.display = (type === 'rules' || type === 'autorole' || type === 'poll') ? 'none' : 'block';
|
||||
});
|
||||
|
||||
// User Settings - Avatar Search
|
||||
@ -3505,6 +3532,220 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
updateInviteTimer();
|
||||
}
|
||||
|
||||
// Poll Logic
|
||||
const addPollForm = document.getElementById('add-poll-form');
|
||||
const addOptionBtn = document.getElementById('add-option-btn');
|
||||
const optionsContainer = document.getElementById('poll-options-container');
|
||||
|
||||
addOptionBtn?.addEventListener('click', () => {
|
||||
const count = optionsContainer.querySelectorAll('.poll-option-input-group').length;
|
||||
if (count >= 10) return;
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.className = 'poll-option-input-group d-flex gap-2 mb-2';
|
||||
div.innerHTML = `
|
||||
<input type="text" class="form-control bg-dark text-white border-0 poll-option-emote" placeholder="📊" style="max-width: 50px;" maxlength="20">
|
||||
<input type="text" class="form-control bg-dark text-white border-0 poll-option-text" placeholder="Réponse ${count + 1}" required>
|
||||
<button type="button" class="btn btn-outline-danger btn-sm remove-option-btn"><i class="fa-solid fa-times"></i></button>
|
||||
`;
|
||||
optionsContainer.appendChild(div);
|
||||
|
||||
if (count + 1 >= 10) addOptionBtn.style.display = 'none';
|
||||
|
||||
div.querySelector('.remove-option-btn').addEventListener('click', () => {
|
||||
div.remove();
|
||||
addOptionBtn.style.display = 'block';
|
||||
});
|
||||
});
|
||||
|
||||
addPollForm?.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const btn = addPollForm.querySelector('button[type="submit"]');
|
||||
const originalText = btn.innerHTML;
|
||||
const messageId = document.getElementById('poll_message_id').value;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span> ' + (messageId ? 'Mise à jour...' : 'Création...');
|
||||
|
||||
const formData = new FormData(addPollForm);
|
||||
formData.append('is_poll', '1');
|
||||
|
||||
const options = [];
|
||||
const emotes = [];
|
||||
optionsContainer.querySelectorAll('.poll-option-input-group').forEach(group => {
|
||||
const text = group.querySelector('.poll-option-text').value.trim();
|
||||
const emote = group.querySelector('.poll-option-emote').value.trim();
|
||||
if (text) {
|
||||
options.push(text);
|
||||
emotes.push(emote || '📊');
|
||||
}
|
||||
});
|
||||
|
||||
if (messageId) {
|
||||
// Edit mode
|
||||
const data = {
|
||||
id: messageId,
|
||||
content: formData.get('content'),
|
||||
poll_title: formData.get('poll_title'),
|
||||
poll_options: options,
|
||||
poll_emotes: emotes,
|
||||
poll_duration: formData.get('poll_duration'),
|
||||
poll_color: formData.get('poll_color'),
|
||||
allow_multiple: formData.get('allow_multiple') || '0',
|
||||
is_anonymous: formData.get('is_anonymous') || '0',
|
||||
is_poll: '1',
|
||||
action: 'edit'
|
||||
};
|
||||
|
||||
try {
|
||||
const resp = await fetch('api_v1_messages.php', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
const result = await resp.json();
|
||||
if (result.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert(result.error || 'Erreur lors de la modification du sondage');
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalText;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalText;
|
||||
}
|
||||
} else {
|
||||
// Create mode
|
||||
formData.append('poll_options', JSON.stringify(options));
|
||||
formData.append('poll_emotes', JSON.stringify(emotes));
|
||||
|
||||
try {
|
||||
const resp = await fetch('api_v1_messages.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
const result = await resp.json();
|
||||
if (result.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert(result.error || 'Erreur lors de la création du sondage');
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalText;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalText;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Handle Poll Edit Button
|
||||
document.addEventListener('click', (e) => {
|
||||
const editPollBtn = e.target.closest('.edit-poll-btn');
|
||||
if (editPollBtn) {
|
||||
const data = editPollBtn.dataset;
|
||||
const modalEl = document.getElementById('addPollModal');
|
||||
const form = document.getElementById('add-poll-form');
|
||||
|
||||
modalEl.querySelector('.modal-title').textContent = 'Modifier le sondage';
|
||||
form.querySelector('button[type="submit"]').textContent = 'Enregistrer les modifications';
|
||||
|
||||
form.poll_message_id.value = data.id;
|
||||
form.poll_title.value = data.title;
|
||||
form.content.value = data.question;
|
||||
form.poll_color.value = data.color;
|
||||
form.poll_duration.value = data.duration;
|
||||
form.allow_multiple.checked = data.multiple === '1';
|
||||
form.is_anonymous.checked = data.anonymous === '1';
|
||||
|
||||
const options = JSON.parse(data.options);
|
||||
const emotes = JSON.parse(data.emotes);
|
||||
|
||||
optionsContainer.innerHTML = '';
|
||||
options.forEach((opt, i) => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'poll-option-input-group d-flex gap-2 mb-2';
|
||||
div.innerHTML = `
|
||||
<input type="text" class="form-control bg-dark text-white border-0 poll-option-emote" placeholder="📊" style="max-width: 50px;" maxlength="20" value="${emotes[i] || ''}">
|
||||
<input type="text" class="form-control bg-dark text-white border-0 poll-option-text" placeholder="Réponse ${i + 1}" required value="${opt}">
|
||||
<button type="button" class="btn btn-outline-danger btn-sm remove-option-btn"><i class="fa-solid fa-times"></i></button>
|
||||
`;
|
||||
optionsContainer.appendChild(div);
|
||||
|
||||
div.querySelector('.remove-option-btn').addEventListener('click', () => {
|
||||
div.remove();
|
||||
addOptionBtn.style.display = 'block';
|
||||
});
|
||||
});
|
||||
|
||||
if (options.length >= 10) addOptionBtn.style.display = 'none';
|
||||
else addOptionBtn.style.display = 'block';
|
||||
|
||||
const bsModal = new bootstrap.Modal(modalEl);
|
||||
bsModal.show();
|
||||
}
|
||||
});
|
||||
|
||||
// Reset poll modal for new poll
|
||||
document.querySelector('[data-bs-target="#addPollModal"]')?.addEventListener('click', () => {
|
||||
const modalEl = document.getElementById('addPollModal');
|
||||
const form = document.getElementById('add-poll-form');
|
||||
modalEl.querySelector('.modal-title').textContent = 'Créer un sondage';
|
||||
form.querySelector('button[type="submit"]').textContent = 'Créer le sondage';
|
||||
form.reset();
|
||||
form.poll_message_id.value = '';
|
||||
|
||||
optionsContainer.innerHTML = `
|
||||
<div class="poll-option-input-group d-flex gap-2 mb-2">
|
||||
<input type="text" class="form-control bg-dark text-white border-0 poll-option-emote" placeholder="📊" style="max-width: 50px;" maxlength="20">
|
||||
<input type="text" class="form-control bg-dark text-white border-0 poll-option-text" placeholder="Réponse 1" required>
|
||||
</div>
|
||||
<div class="poll-option-input-group d-flex gap-2 mb-2">
|
||||
<input type="text" class="form-control bg-dark text-white border-0 poll-option-emote" placeholder="📊" style="max-width: 50px;" maxlength="20">
|
||||
<input type="text" class="form-control bg-dark text-white border-0 poll-option-text" placeholder="Réponse 2" required>
|
||||
</div>
|
||||
`;
|
||||
addOptionBtn.style.display = 'block';
|
||||
});
|
||||
|
||||
window.votePoll = async (messageId, optionIndex) => {
|
||||
try {
|
||||
const resp = await fetch('api_v1_poll_vote.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ message_id: messageId, option_index: optionIndex })
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (data.success) {
|
||||
// We could update the UI dynamically, but for simplicity let's reload or just fetch the message again
|
||||
// For a poll channel, reloading is fine as there aren't many updates
|
||||
location.reload();
|
||||
} else {
|
||||
alert(data.error || 'Erreur lors du vote');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('click', async (e) => {
|
||||
const deletePollBtn = e.target.closest('.delete-poll-btn');
|
||||
if (deletePollBtn) {
|
||||
if (!confirm('Supprimer ce sondage ?')) return;
|
||||
const id = deletePollBtn.dataset.id;
|
||||
try {
|
||||
const resp = await fetch('api_v1_messages.php', {
|
||||
method: 'DELETE',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ id: id })
|
||||
});
|
||||
if ((await resp.json()).success) location.reload();
|
||||
} catch (err) { console.error(err); }
|
||||
}
|
||||
});
|
||||
|
||||
// Event Channel Management
|
||||
const addEventBtn = document.getElementById('addEventBtn');
|
||||
const addEventModal = document.getElementById('addEventModal');
|
||||
@ -3745,5 +3986,36 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
} catch (err) { console.error(err); }
|
||||
}
|
||||
|
||||
// View Poll Voters
|
||||
const viewVotersBtn = e.target.closest('.view-voters-btn');
|
||||
if (viewVotersBtn) {
|
||||
const votesData = JSON.parse(viewVotersBtn.dataset.votes);
|
||||
const options = JSON.parse(viewVotersBtn.dataset.options);
|
||||
const contentEl = document.getElementById('poll-voters-content');
|
||||
|
||||
contentEl.innerHTML = '';
|
||||
|
||||
options.forEach((opt, idx) => {
|
||||
const vote = votesData.find(v => v.option_index == idx);
|
||||
const voters = vote ? vote.user_names.split(', ') : [];
|
||||
|
||||
const section = document.createElement('div');
|
||||
section.className = 'mb-4';
|
||||
section.innerHTML = `
|
||||
<h6 class="fw-bold border-bottom border-secondary pb-2 mb-2 d-flex justify-content-between">
|
||||
<span>${opt}</span>
|
||||
<span class="badge bg-secondary">${voters.length}</span>
|
||||
</h6>
|
||||
<div class="d-flex flex-wrap gap-2 mt-2">
|
||||
${voters.length > 0 ? voters.map(v => `<span class="badge bg-dark border border-secondary fw-normal px-2 py-1">${v}</span>`).join('') : '<span class="text-muted small fst-italic">Aucun vote pour cette option</span>'}
|
||||
</div>
|
||||
`;
|
||||
contentEl.appendChild(section);
|
||||
});
|
||||
|
||||
const modal = new bootstrap.Modal(document.getElementById('pollVotersModal'));
|
||||
modal.show();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
315
index.php
315
index.php
@ -337,6 +337,28 @@ if ($is_dm_view) {
|
||||
} catch (Exception $e) {
|
||||
$events = [];
|
||||
}
|
||||
} elseif ($channel_type === 'poll') {
|
||||
$display_limit = !empty($active_channel['message_limit']) ? (int)$active_channel['message_limit'] : 50;
|
||||
$stmt = db()->prepare("
|
||||
SELECT m.*, u.display_name as username, u.avatar_url,
|
||||
(SELECT r.color FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_color,
|
||||
(SELECT r.icon_url FROM roles r JOIN user_roles ur ON r.id = ur.role_id WHERE ur.user_id = u.id AND r.server_id = ? ORDER BY r.position DESC LIMIT 1) as role_icon
|
||||
FROM messages m
|
||||
JOIN users u ON m.user_id = u.id
|
||||
WHERE m.channel_id = ? AND m.thread_id IS NULL
|
||||
ORDER BY m.created_at DESC
|
||||
LIMIT " . $display_limit . "
|
||||
");
|
||||
$stmt->execute([$active_server_id, $active_server_id, $active_channel_id]);
|
||||
$messages = $stmt->fetchAll();
|
||||
|
||||
// Fetch votes for each poll message
|
||||
foreach ($messages as &$m) {
|
||||
$stmt_v = db()->prepare("SELECT pv.option_index, COUNT(*) as vote_count, GROUP_CONCAT(u.display_name SEPARATOR ', ') as user_names, GROUP_CONCAT(pv.user_id) as user_ids FROM poll_votes pv JOIN users u ON pv.user_id = u.id WHERE pv.message_id = ? GROUP BY pv.option_index");
|
||||
$stmt_v->execute([$m['id']]);
|
||||
$m['votes_data'] = $stmt_v->fetchAll();
|
||||
}
|
||||
unset($m);
|
||||
} else {
|
||||
// Fetch messages for normal chat channels
|
||||
$display_limit = !empty($active_channel['message_limit']) ? (int)$active_channel['message_limit'] : 50;
|
||||
@ -511,6 +533,41 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
.read-more-btn:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.participate-btn:hover {
|
||||
transform: scale(1.02);
|
||||
transition: transform 0.1s;
|
||||
}
|
||||
.poll-option-row:hover:not(.expired) {
|
||||
background: rgba(255,255,255,0.08) !important;
|
||||
border-color: var(--blurple) !important;
|
||||
}
|
||||
.poll-option-row.voted {
|
||||
border-color: var(--blurple) !important;
|
||||
background: rgba(88, 101, 242, 0.1) !important;
|
||||
}
|
||||
.poll-option-row.expired {
|
||||
opacity: 0.8;
|
||||
cursor: default;
|
||||
}
|
||||
.poll-option-progress {
|
||||
transition: width 0.5s ease-out;
|
||||
}
|
||||
.polls-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));
|
||||
gap: 20px;
|
||||
align-items: start;
|
||||
}
|
||||
.poll-item {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.poll-footer {
|
||||
margin-top: auto !important;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid rgba(255,255,255,0.05);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body data-theme="<?php echo htmlspecialchars($user['theme'] ?: 'dark'); ?>">
|
||||
@ -626,6 +683,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
elseif ($c['type'] === 'forum') echo '<i class="fa-solid fa-comments"></i>';
|
||||
elseif ($c['type'] === 'voice') echo '<i class="fa-solid fa-volume-up"></i>';
|
||||
elseif ($c['type'] === 'event') echo '<i class="fa-solid fa-calendar-days"></i>';
|
||||
elseif ($c['type'] === 'poll') echo '<i class="fa-solid fa-square-poll-vertical"></i>';
|
||||
else echo '<i class="fa-solid fa-hashtag"></i>';
|
||||
?>
|
||||
</span>
|
||||
@ -780,6 +838,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
elseif ($active_channel['type'] === 'autorole') echo '<i class="fa-solid fa-shield-halved"></i>';
|
||||
elseif ($active_channel['type'] === 'forum') echo '<i class="fa-solid fa-comments"></i>';
|
||||
elseif ($active_channel['type'] === 'voice') echo '<i class="fa-solid fa-volume-up"></i>';
|
||||
elseif ($active_channel['type'] === 'poll') echo '<i class="fa-solid fa-square-poll-vertical"></i>';
|
||||
else echo '<i class="fa-solid fa-hashtag"></i>';
|
||||
|
||||
if (!empty($active_channel['icon'])) {
|
||||
@ -903,6 +962,154 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php elseif($channel_type === 'poll'): ?>
|
||||
<div class="polls-container p-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h2 class="mb-0"><i class="fa-solid fa-square-poll-vertical me-2"></i>Sondages</h2>
|
||||
<p class="text-muted small mb-0">Participez aux sondages de la communauté.</p>
|
||||
</div>
|
||||
<?php if (Permissions::canSendInChannel($current_user_id, $active_channel_id)): ?>
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addPollModal">
|
||||
<i class="fa-solid fa-plus me-1"></i> Ajouter un sondage
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="polls-list">
|
||||
<?php if (empty($messages)): ?>
|
||||
<div class="text-center py-5 border rounded bg-dark bg-opacity-25 border-secondary border-dashed">
|
||||
<div class="opacity-25 mb-3">
|
||||
<i class="fa-solid fa-square-poll-vertical" style="font-size: 4rem;"></i>
|
||||
</div>
|
||||
<h4 class="text-muted">Aucun sondage pour le moment.</h4>
|
||||
<?php if (Permissions::canSendInChannel($current_user_id, $active_channel_id)): ?>
|
||||
<p class="text-muted">Soyez le premier à poser une question !</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php foreach($messages as $m):
|
||||
$meta = json_decode($m['metadata'] ?? '{}', true);
|
||||
if (!$meta || empty($meta['is_poll'])) continue;
|
||||
$is_expired = !empty($meta['poll_end_date']) && strtotime($meta['poll_end_date']) < time();
|
||||
$total_votes = 0;
|
||||
if (isset($m['votes_data'])) {
|
||||
foreach($m['votes_data'] as $v) $total_votes += (int)$v['vote_count'];
|
||||
}
|
||||
$poll_color = $meta['poll_color'] ?? 'var(--blurple)';
|
||||
?>
|
||||
<div class="poll-item mb-4 bg-dark p-4 rounded border-start border-4 shadow-sm" style="border-color: <?php echo htmlspecialchars($poll_color); ?>; background-color: #2b2d31 !important;">
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="message-avatar me-2" style="width: 32px; height: 32px; <?php echo $m['avatar_url'] ? "background-image: url('{$m['avatar_url']}');" : ""; ?>"></div>
|
||||
<div>
|
||||
<h5 class="mb-0 fw-bold text-white"><?php echo htmlspecialchars($meta['poll_title'] ?? 'Sondage'); ?></h5>
|
||||
<div class="small text-muted">Par <?php echo htmlspecialchars($m['username']); ?> • <?php echo date('d/m/Y H:i', strtotime($m['created_at'])); ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<?php if ($can_manage_server || $m['user_id'] == $current_user_id): ?>
|
||||
<button class="btn btn-sm btn-outline-light border-0 edit-poll-btn"
|
||||
style="padding: 2px 5px; opacity: 0.7;"
|
||||
data-id="<?php echo $m['id']; ?>"
|
||||
data-title="<?php echo htmlspecialchars($meta['poll_title'] ?? ''); ?>"
|
||||
data-question="<?php echo htmlspecialchars($m['content']); ?>"
|
||||
data-options='<?php echo htmlspecialchars(json_encode($meta['poll_options'] ?? [])); ?>'
|
||||
data-emotes='<?php echo htmlspecialchars(json_encode($meta['poll_emotes'] ?? [])); ?>'
|
||||
data-color="<?php echo htmlspecialchars($meta['poll_color'] ?? '#5865f2'); ?>"
|
||||
data-duration="<?php echo !empty($meta['poll_end_date']) ? (strtotime($meta['poll_end_date']) - strtotime($m['created_at'])) : 86400; ?>"
|
||||
data-multiple="<?php echo ($meta['poll_choice_type'] ?? 'single') === 'multiple' ? '1' : '0'; ?>"
|
||||
data-anonymous="<?php echo ($meta['is_anonymous'] ?? false) ? '1' : '0'; ?>"
|
||||
title="Modifier le sondage">
|
||||
<i class="fa-solid fa-pen-to-square"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-link text-danger p-0 delete-poll-btn" data-id="<?php echo $m['id']; ?>" title="Supprimer le sondage">
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="poll-question mb-4 text-white" style="font-size: 1.1em;">
|
||||
<?php echo parse_markdown($m['content']); ?>
|
||||
</div>
|
||||
<div class="poll-options-list">
|
||||
<?php
|
||||
$options = $meta['poll_options'] ?? [];
|
||||
$emotes = $meta['poll_emotes'] ?? [];
|
||||
foreach($options as $idx => $opt):
|
||||
$count = 0;
|
||||
$user_voted = false;
|
||||
$voters_list = '';
|
||||
if (isset($m['votes_data'])) {
|
||||
foreach($m['votes_data'] as $v) {
|
||||
if ($v['option_index'] == $idx) {
|
||||
$count = (int)$v['vote_count'];
|
||||
$voters_list = $v['user_names'] ?? '';
|
||||
$user_ids = explode(',', $v['user_ids'] ?? '');
|
||||
if (in_array($current_user_id, $user_ids)) $user_voted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$percent = $total_votes > 0 ? round(($count / $total_votes) * 100) : 0;
|
||||
$emote = $emotes[$idx] ?? '';
|
||||
?>
|
||||
<div class="poll-option-row mb-3 <?php echo $user_voted ? 'voted' : ''; ?> <?php echo $is_expired ? 'expired' : ''; ?>"
|
||||
onclick="<?php echo $is_expired ? '' : "votePoll({$m['id']}, {$idx})"; ?>"
|
||||
style="cursor: <?php echo $is_expired ? 'default' : 'pointer'; ?>; position: relative; overflow: hidden; border-radius: 8px; background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); transition: all 0.2s;">
|
||||
|
||||
<div class="poll-option-progress" style="position: absolute; top: 0; left: 0; bottom: 0; width: <?php echo $percent; ?>%; background-color: <?php echo $poll_color; ?>; opacity: 0.15; transition: width 0.3s;"></div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center p-3" style="position: relative; z-index: 1;">
|
||||
<div class="d-flex align-items-center">
|
||||
<?php if ($emote): ?>
|
||||
<span class="me-2"><?php echo parse_emotes($emote); ?></span>
|
||||
<?php endif; ?>
|
||||
<span class="text-white fw-medium"><?php echo htmlspecialchars($opt); ?></span>
|
||||
<?php if ($user_voted): ?>
|
||||
<i class="fa-solid fa-circle-check ms-2" style="color: <?php echo $poll_color; ?>;"></i>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span class="small fw-bold <?php echo $user_voted ? 'text-white' : 'text-muted'; ?>"><?php echo $percent; ?>%</span>
|
||||
<span class="small text-muted">
|
||||
(<?php echo $count; ?>)
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div class="poll-footer mt-4 d-flex justify-content-between align-items-center small text-muted">
|
||||
<div class="d-flex flex-column gap-1">
|
||||
<div class="d-flex gap-3">
|
||||
<span><i class="fa-solid fa-users me-1"></i><?php echo $total_votes; ?> vote<?php echo $total_votes > 1 ? 's' : ''; ?></span>
|
||||
<span><i class="fa-solid fa-circle-info me-1"></i><?php echo ($meta['poll_choice_type'] ?? 'single') === 'multiple' ? 'Plusieurs choix' : 'Choix unique'; ?></span>
|
||||
</div>
|
||||
<?php if (!($meta['is_anonymous'] ?? false)): ?>
|
||||
<button class="btn btn-link btn-sm p-0 text-muted text-start view-voters-btn"
|
||||
data-id="<?php echo $m['id']; ?>"
|
||||
data-votes='<?php echo htmlspecialchars(json_encode($m['votes_data'] ?? [])); ?>'
|
||||
data-options='<?php echo htmlspecialchars(json_encode($meta['poll_options'] ?? [])); ?>'
|
||||
style="text-decoration: none; font-size: 0.9em;">
|
||||
<i class="fa-solid fa-eye me-1"></i> Voir les votants
|
||||
</button>
|
||||
<?php else: ?>
|
||||
<span title="Les noms des votants sont cachés pour ce sondage"><i class="fa-solid fa-user-secret me-1"></i> Vote anonyme</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php if (!empty($meta['poll_end_date'])): ?>
|
||||
<div class="<?php echo $is_expired ? 'text-danger fw-bold' : ''; ?>">
|
||||
<i class="fa-solid fa-clock me-1"></i>
|
||||
<?php echo $is_expired ? 'Terminé' : 'Expire le ' . date('d/m/Y H:i', strtotime($meta['poll_end_date'])); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php elseif($channel_type === 'event'): ?>
|
||||
<div class="events-container p-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
@ -2500,6 +2707,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
<select name="type" class="form-select bg-dark text-white border-secondary mb-3" id="add-channel-type">
|
||||
<option value="chat">Salon Textuel</option>
|
||||
<option value="voice">Salon Vocal</option>
|
||||
<option value="poll">Salon de Sondage</option>
|
||||
<option value="announcement">Salon d'Annonces</option>
|
||||
<option value="rules">Salon de Règlement</option>
|
||||
<option value="autorole">Salon d'Auto-rôles</option>
|
||||
@ -2536,6 +2744,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
<option value="fa-question-circle">❓ Aide</option>
|
||||
<option value="fa-book">📖 Bibliothèque</option>
|
||||
<option value="fa-gift">🎁 Giveaways</option>
|
||||
<option value="fa-square-poll-vertical">📊 Sondage</option>
|
||||
<option value="fa-code">💻 Programmation</option>
|
||||
<option value="fa-terminal">⌨️ Bot</option>
|
||||
</select>
|
||||
@ -2572,6 +2781,111 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Poll Modal -->
|
||||
<div class="modal fade" id="addPollModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content" style="background-color: #313338; color: white;">
|
||||
<div class="modal-header border-0">
|
||||
<h5 class="modal-title fw-bold">Créer un sondage</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<form id="add-poll-form">
|
||||
<input type="hidden" name="channel_id" value="<?php echo $active_channel_id; ?>">
|
||||
<input type="hidden" name="poll_message_id" id="poll_message_id" value="">
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-uppercase fw-bold small text-muted">Sujet du sondage</label>
|
||||
<input type="text" name="poll_title" class="form-control bg-dark text-white border-0" placeholder="Ex: Quel est votre jeu préféré ?" required maxlength="100">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-uppercase fw-bold small text-muted">Question détaillée</label>
|
||||
<textarea name="content" class="form-control bg-dark text-white border-0" rows="3" placeholder="Décrivez votre question ici..." required maxlength="300"></textarea>
|
||||
<div class="form-text text-muted small">Limite de 300 caractères.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-uppercase fw-bold small text-muted">Réponses</label>
|
||||
<div id="poll-options-container">
|
||||
<div class="poll-option-input-group d-flex gap-2 mb-2">
|
||||
<input type="text" class="form-control bg-dark text-white border-0 poll-option-emote" placeholder="📊" style="max-width: 50px;" maxlength="20">
|
||||
<input type="text" class="form-control bg-dark text-white border-0 poll-option-text" placeholder="Réponse 1" required>
|
||||
<button type="button" class="btn btn-outline-danger btn-sm remove-option-btn" style="display: none;"><i class="fa-solid fa-times"></i></button>
|
||||
</div>
|
||||
<div class="poll-option-input-group d-flex gap-2 mb-2">
|
||||
<input type="text" class="form-control bg-dark text-white border-0 poll-option-emote" placeholder="📊" style="max-width: 50px;" maxlength="20">
|
||||
<input type="text" class="form-control bg-dark text-white border-0 poll-option-text" placeholder="Réponse 2" required>
|
||||
<button type="button" class="btn btn-outline-danger btn-sm remove-option-btn" style="display: none;"><i class="fa-solid fa-times"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary mt-2" id="add-option-btn">
|
||||
<i class="fa-solid fa-plus me-1"></i> Ajouter une réponse
|
||||
</button>
|
||||
<div class="form-text text-muted small mt-1">Maximum 10 réponses.</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-uppercase fw-bold small text-muted">Durée</label>
|
||||
<select name="poll_duration" class="form-select bg-dark text-white border-0">
|
||||
<option value="3600">1 heure</option>
|
||||
<option value="14400">4 heures</option>
|
||||
<option value="28800">8 heures</option>
|
||||
<option value="86400" selected>24 heures</option>
|
||||
<option value="259200">3 jours</option>
|
||||
<option value="604800">1 semaine</option>
|
||||
<option value="1209600">2 semaines</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label class="form-label text-uppercase fw-bold small text-muted">Couleur d'accentuation</label>
|
||||
<input type="color" name="poll_color" class="form-control form-control-color bg-dark border-0 w-100" value="#5865f2">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-3 mb-3">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" name="allow_multiple" id="poll-multiple-choice" value="1">
|
||||
<label class="form-check-label text-white" for="poll-multiple-choice">Plusieurs réponses</label>
|
||||
</div>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" name="is_anonymous" id="poll-anonymous" value="1">
|
||||
<label class="form-check-label text-white" for="poll-anonymous">Vote anonyme</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer border-0">
|
||||
<button type="button" class="btn btn-link text-white text-decoration-none" data-bs-dismiss="modal">Annuler</button>
|
||||
<button type="submit" class="btn btn-primary px-4">Créer le sondage</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Poll Voters Modal -->
|
||||
<div class="modal fade" id="pollVotersModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content" style="background-color: #313338; color: white;">
|
||||
<div class="modal-header border-0 pb-0">
|
||||
<h5 class="modal-title fw-bold">Détails des votes</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="poll-voters-content">
|
||||
<!-- Loaded dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer border-0">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Autorole Modal -->
|
||||
<div class="modal fade" id="addAutoroleModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
@ -2701,6 +3015,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Type de salon</label>
|
||||
<select name="type" id="edit-channel-type" class="form-select bg-dark text-white border-secondary">
|
||||
<option value="chat">Salon textuel</option>
|
||||
<option value="poll">Salon de Sondage</option>
|
||||
<option value="announcement">Annonces</option>
|
||||
<option value="rules">Règles</option>
|
||||
<option value="forum">Forum</option>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user