Files
2026-05-10 18:07:51 +02:00

432 lines
31 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{define "title"}}{{if .IsNew}}Nouveau panel{{else}}Éditer — {{.Panel.Name}}{{end}}{{end}}
{{define "header"}}{{if .IsNew}}Créer un panel{{else}}Éditer {{.Panel.Name}}{{end}}{{end}}
{{define "content"}}
<form id="panelForm" method="POST" action="{{if .IsNew}}/panels/new{{else}}/panels/{{.Panel.ID}}/edit{{end}}">
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
{{if and .IsNew .Guilds}}
<div class="bg-discord-card rounded-lg border border-discord-border p-4 mb-4 flex items-center gap-4">
<label class="text-sm text-gray-300 flex-shrink-0">Serveur Discord :</label>
<select id="guildSelector" class="px-3 py-2 bg-gray-800 border border-gray-600 rounded-lg text-white text-sm focus:outline-none focus:border-indigo-500"
onchange="location.href='/panels/new?guild_id='+this.value">
{{range .Guilds}}
<option value="{{.ID}}" {{if eq .ID $.Panel.GuildID}}selected{{end}}>{{.Name}}</option>
{{end}}
</select>
<p class="text-xs text-gray-500">Les rôles et channels chargés correspondent au serveur sélectionné.</p>
</div>
{{end}}
<input type="hidden" name="guild_id" value="{{.Panel.GuildID}}">
<div class="grid grid-cols-1 xl:grid-cols-3 gap-6">
<!-- Left: form -->
<div class="xl:col-span-2 space-y-6">
<!-- Section 1: Infos panel -->
<div class="bg-discord-card rounded-lg border border-discord-border p-6">
<h2 class="font-semibold text-white mb-4">Informations du panel</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div class="sm:col-span-2">
<label class="block text-sm text-gray-300 mb-1">Nom interne <span class="text-red-400">*</span></label>
<input type="text" name="name" value="{{.Panel.Name}}" required {{if not .IsNew}}readonly{{end}}
class="w-full px-3 py-2 bg-gray-800 border {{if index .Errors "name"}}border-red-600{{else}}border-gray-600{{end}} rounded-lg text-white focus:outline-none focus:border-indigo-500 {{if not .IsNew}}opacity-60 cursor-not-allowed{{end}}">
{{if index .Errors "name"}}<p class="text-red-400 text-xs mt-1">{{index .Errors "name"}}</p>{{end}}
</div>
<div class="sm:col-span-2">
<label class="block text-sm text-gray-300 mb-1">Titre embed <span class="text-red-400">*</span></label>
<input type="text" name="embed_title" value="{{.Panel.EmbedTitle}}" required
hx-post="/panels/preview" hx-trigger="input changed delay:300ms" hx-include="#panelForm" hx-target="#preview"
class="w-full px-3 py-2 bg-gray-800 border {{if index .Errors "embed_title"}}border-red-600{{else}}border-gray-600{{end}} rounded-lg text-white focus:outline-none focus:border-indigo-500">
{{if index .Errors "embed_title"}}<p class="text-red-400 text-xs mt-1">{{index .Errors "embed_title"}}</p>{{end}}
</div>
<div class="sm:col-span-2">
<label class="block text-sm text-gray-300 mb-1">Description embed</label>
<textarea name="embed_description" rows="3"
hx-post="/panels/preview" hx-trigger="input changed delay:300ms" hx-include="#panelForm" hx-target="#preview"
class="w-full px-3 py-2 bg-gray-800 border border-gray-600 rounded-lg text-white focus:outline-none focus:border-indigo-500 resize-none">{{.Panel.EmbedDescription.String}}</textarea>
</div>
<div>
<label class="block text-sm text-gray-300 mb-1">Couleur embed</label>
<div class="flex items-center gap-3">
<input type="color" name="embed_color" value="{{.Panel.EmbedColor}}" id="embedColor"
hx-post="/panels/preview" hx-trigger="input changed delay:200ms" hx-include="#panelForm" hx-target="#preview"
class="h-9 w-16 rounded cursor-pointer bg-gray-800 border border-gray-600">
<span id="embedColorText" class="text-sm text-gray-400">{{.Panel.EmbedColor}}</span>
</div>
</div>
<div class="sm:col-span-2">
<label class="block text-sm text-gray-300 mb-1">Image URL (grande image en bas de l'embed)</label>
<input type="url" name="embed_image" value="{{.Panel.EmbedImage}}"
class="w-full px-3 py-2 bg-gray-800 border border-gray-600 rounded-lg text-white focus:outline-none focus:border-indigo-500"
placeholder="https://...">
</div>
<div>
<label class="block text-sm text-gray-300 mb-1">Channel Discord</label>
{{if .TextChans}}
<select name="channel_id" class="w-full px-3 py-2 bg-gray-800 border border-gray-600 rounded-lg text-white focus:outline-none focus:border-indigo-500">
<option value="">— Sélectionner —</option>
{{range .TextChans}}
<option value="{{.ID}}" {{if eq .ID $.Panel.ChannelID.String}}selected{{end}}>#{{.Name}}</option>
{{end}}
</select>
{{else}}
<input type="text" name="channel_id" value="{{.Panel.ChannelID.String}}"
class="w-full px-3 py-2 bg-gray-800 border border-gray-600 rounded-lg text-white focus:outline-none focus:border-indigo-500"
placeholder="ID du channel cible">
{{end}}
</div>
</div>
</div>
<!-- Section 2: Types de tickets -->
<div class="bg-discord-card rounded-lg border border-discord-border p-6">
<div class="flex items-center justify-between mb-4">
<h2 class="font-semibold text-white">Types de tickets</h2>
<button type="button" onclick="addType()"
class="px-3 py-1.5 text-sm bg-indigo-600 hover:bg-indigo-500 text-white rounded-lg transition-colors">
+ Ajouter un type
</button>
</div>
<div id="typesList" class="space-y-4">
{{range $i, $t := .Panel.Types}}
{{template "type_block" dict "Type" $t "Index" $i "CSRFToken" $.CSRFToken "Roles" $.Roles "TextChans" $.TextChans "Categories" $.Categories}}
{{end}}
</div>
{{if eq (len .Panel.Types) 0}}
<p id="noTypesMsg" class="text-gray-500 text-sm text-center py-4">Aucun type. Cliquez sur "+ Ajouter un type".</p>
{{end}}
</div>
</div>
<!-- Right: preview + actions -->
<div class="space-y-6">
<div class="bg-discord-card rounded-lg border border-discord-border p-5">
<h3 class="font-semibold text-white mb-3">Aperçu</h3>
<div id="preview">
<div class="rounded-lg overflow-hidden border-l-4 bg-[#2b2d31] p-4" style="border-left-color:{{.Panel.EmbedColor}}">
<p class="font-semibold text-white mb-1">{{if .Panel.EmbedTitle}}{{.Panel.EmbedTitle}}{{else}}Titre du panel{{end}}</p>
{{if .Panel.EmbedDescription.Valid}}
<p class="text-gray-300 text-sm">{{.Panel.EmbedDescription.String}}</p>
{{end}}
{{if .Panel.Types}}
<div class="flex flex-wrap gap-2 mt-3">
{{range .Panel.Types}}
<span class="{{buttonClass .ButtonColor}} text-white text-sm px-3 py-1.5 rounded">{{.ButtonEmoji}} {{.ButtonLabel}}</span>
{{end}}
</div>
{{end}}
</div>
</div>
</div>
<div class="bg-discord-card rounded-lg border border-discord-border p-5 space-y-3">
<button type="submit" name="action" value="save"
class="w-full px-4 py-2.5 bg-indigo-600 hover:bg-indigo-500 text-white font-medium rounded-lg transition-colors">
Sauvegarder
</button>
<button type="submit" name="action" value="save_send"
class="w-full px-4 py-2.5 bg-green-700 hover:bg-green-600 text-white font-medium rounded-lg transition-colors">
Sauvegarder et envoyer sur Discord
</button>
<a href="/panels" class="block text-center px-4 py-2.5 text-gray-400 hover:text-white border border-gray-700 hover:border-gray-500 rounded-lg transition-colors text-sm">
Annuler
</a>
</div>
</div>
</div>
</form>
<!-- Emoji picker -->
<div id="emojiPicker" class="hidden fixed z-50 bg-gray-900 border border-gray-700 rounded-xl shadow-2xl w-72">
<div class="flex gap-1 p-2 border-b border-gray-700 overflow-x-auto">
<button type="button" onclick="showEmojiCat('smileys')" class="emoji-cat-btn px-2 py-1 text-sm rounded hover:bg-gray-700" data-cat="smileys">😀</button>
<button type="button" onclick="showEmojiCat('gestures')" class="emoji-cat-btn px-2 py-1 text-sm rounded hover:bg-gray-700" data-cat="gestures">👍</button>
<button type="button" onclick="showEmojiCat('activities')" class="emoji-cat-btn px-2 py-1 text-sm rounded hover:bg-gray-700" data-cat="activities">🎮</button>
<button type="button" onclick="showEmojiCat('objects')" class="emoji-cat-btn px-2 py-1 text-sm rounded hover:bg-gray-700" data-cat="objects">📝</button>
<button type="button" onclick="showEmojiCat('symbols')" class="emoji-cat-btn px-2 py-1 text-sm rounded hover:bg-gray-700" data-cat="symbols"></button>
</div>
<div id="emojiGrid" class="p-2 grid grid-cols-8 gap-0.5 max-h-48 overflow-y-auto"></div>
</div>
<!-- Type block template for JS -->
<template id="typeTpl">
<div class="type-block border border-gray-700 rounded-lg p-4 space-y-3 cursor-move" data-index="__IDX__">
<div class="flex items-center justify-between mb-1">
<span class="text-sm font-medium text-gray-300 flex items-center gap-2">
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8h16M4 16h16"/></svg>
Type <span class="type-num"></span>
</span>
<button type="button" onclick="removeType(this)" class="text-red-400 hover:text-red-300 text-sm">Supprimer</button>
</div>
<div class="grid grid-cols-2 gap-3">
<div><label class="block text-xs text-gray-400 mb-1">Nom interne</label>
<input type="text" name="types[__IDX__][name]" required class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500" placeholder="ex: support"></div>
<div><label class="block text-xs text-gray-400 mb-1">Label bouton</label>
<input type="text" name="types[__IDX__][button_label]"
hx-post="/panels/preview" hx-trigger="input changed delay:300ms" hx-include="#panelForm" hx-target="#preview"
class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500"></div>
<div><label class="block text-xs text-gray-400 mb-1">Couleur bouton</label>
<select name="types[__IDX__][button_color]"
hx-post="/panels/preview" hx-trigger="change" hx-include="#panelForm" hx-target="#preview"
class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500">
<option value="primary">🔵 Bleu (primary)</option>
<option value="secondary">⚪ Gris (secondary)</option>
<option value="success">🟢 Vert (success)</option>
<option value="danger">🔴 Rouge (danger)</option>
</select></div>
<div><label class="block text-xs text-gray-400 mb-1">Emoji bouton</label>
<div class="flex gap-1">
<input type="text" name="types[__IDX__][button_emoji]" readonly
hx-post="/panels/preview" hx-trigger="input changed delay:200ms" hx-include="#panelForm" hx-target="#preview"
class="flex-1 px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none cursor-pointer" placeholder="—" onclick="openEmojiPicker(this)">
<button type="button" onclick="openEmojiPicker(this.previousElementSibling)" class="px-2 py-1.5 bg-gray-700 hover:bg-gray-600 border border-gray-600 rounded text-sm">😀</button>
</div></div>
<div><label class="block text-xs text-gray-400 mb-1">Titre embed ticket</label>
<input type="text" name="types[__IDX__][embed_title]" class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500"></div>
<div><label class="block text-xs text-gray-400 mb-1">Couleur embed</label>
<input type="color" name="types[__IDX__][embed_color]" value="#5865f2" class="h-9 w-full rounded cursor-pointer bg-gray-800 border border-gray-600"></div>
<div class="col-span-2"><label class="block text-xs text-gray-400 mb-1">Texte embed bienvenue</label>
<textarea name="types[__IDX__][embed_text]" rows="2" class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500 resize-none"></textarea></div>
<div class="col-span-2"><label class="block text-xs text-gray-400 mb-1">Image URL (optionnel)</label>
<input type="url" name="types[__IDX__][image_url]" class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500" placeholder="https://..."></div>
__ROLES_HTML__
__CATEGORIES_HTML__
__TEXTCHANS_HTML__
<div><label class="block text-xs text-gray-400 mb-1">Règle de fermeture</label>
<select name="types[__IDX__][close_rule]" class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500">
<option value="staff_only">Staff uniquement</option>
<option value="author_if_unclaimed">Auteur si non claimé</option>
</select></div>
<div class="col-span-2"><label class="block text-xs text-gray-400 mb-1">Reup claim (minutes, 0 = global)</label>
<input type="number" name="types[__IDX__][claim_reup_minutes]" value="0" min="0" max="1440"
class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500"></div>
<div class="flex items-center gap-2">
<input type="hidden" name="types[__IDX__][claim_mode]" value="0">
<input type="checkbox" name="types[__IDX__][claim_mode]" value="1" class="rounded" id="cm__IDX__">
<label for="cm__IDX__" class="text-xs text-gray-400">Système de claim activé</label></div>
<div class="flex items-center gap-2">
<input type="hidden" name="types[__IDX__][modal_enabled]" value="1">
<input type="checkbox" name="types[__IDX__][modal_enabled]" value="1" checked class="rounded" id="me__IDX__">
<label for="me__IDX__" class="text-xs text-gray-400">Modal activé</label></div>
</div>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
<script>
let typeCount = {{len .Panel.Types}};
// Emoji data
const emojiData = {
smileys: ['😀','😃','😄','😁','😆','😅','😂','🤣','😊','😇','🙂','😉','😌','😍','🥰','😘','😗','😙','😚','😋','😛','😜','🤪','😝','🤑','🤗','🤭','🤫','🤔','🤐','🤨','😐','😑','😶','😏','😒','🙄','😬','🤥','😌','😔','😪','🤤','😴','😷','🤒','🤕','🤢','🤧','🥵','🥶','🥴','😵','🤯','🤠','🥳','😎','🤓','🧐','😕','😟','🙁','☹️','😮','😯','😲','😳','🥺','😦','😧','😨','😰','😥','😢','😭','😱','😖','😣','😞','😓','😩','😫','🥱','😤','😡','😠','🤬','😈','👿','💀','☠️','💩','🤡','👹','👺','👻','👽','👾','🤖'],
gestures: ['👋','🤚','🖐','✋','🖖','👌','🤌','🤏','✌️','🤞','🤟','🤘','🤙','👈','👉','👆','🖕','👇','☝️','👍','👎','✊','👊','🤛','🤜','👏','🙌','👐','🤲','🤝','🙏','✍️','💅','🤳','💪','🦾','🦿','🦵','🦶','👂','🦻','👃','🫀','🫁','🧠','🦷','🦴','👀','👁','👅','👄'],
activities: ['⚽','🏀','🏈','⚾','🥎','🎾','🏐','🏉','🥏','🎱','🪀','🏓','🏸','🏒','🥍','🏏','🪃','🥅','⛳','🪁','🏹','🎣','🤿','🥊','🥋','🎽','🛹','🛷','⛸','🥌','🎿','⛷','🏂','🏋','🤼','🤸','⛹','🤺','🤾','🏌','🏇','🧘','🏄','🏊','🤽','🚣','🧗','🚵','🚴','🎖','🏆','🥇','🥈','🥉','🏅','🎪','🤹','🎭','🎨','🎬','🎤','🎧','🎼','🎵','🎶','🎷','🪗','🎸','🎹','🎺','🎻','🥁','🪘','🎙','🎚','🎛','📻','🎮','🕹','🎲','🎯','🎳','🎰','🎴','🀄','🎭','🎠','🎡','🎢','🎪'],
objects: ['📱','💻','🖥','🖨','⌨️','🖱','🖲','💽','💾','💿','📀','📷','📸','📹','🎥','📽','🎞','📞','☎️','📟','📠','📺','📻','🧭','⏱','⏲','⏰','🕰','⌚','⏳','📡','🔋','🔌','💡','🔦','🕯','🗑','🛢','💰','💴','💵','💶','💷','💸','💳','🪙','💹','📈','📉','📊','📋','📌','📍','✂️','🗃','🗂','🗄','🗑','🔧','🔨','⚒','🛠','⛏','🔩','🪛','🔑','🗝','🔐','🔏','🔒','🔓','🚪','🪑','🛋','🚽','🪠','🚿','🛁','🛒','🚬','⚰️','🪦','⚱️','🧿','💎','🔮','🧸','🪆','🖼','🪅','🎊','🎉','🎁','🎀','🎗','🎟','🎫','🎖','📝','📓','📔','📒','📕','📗','📘','📙','📚','📖','🔖','🏷','💉','🩸','💊','🩹','🩺','🔬','🔭','🩻','🧬','🦠','🧫','🧪','🌡','🧹','🧺','🧻','🪣','🧼','🫧','🪥','🧽','🧯','🛒'],
symbols: ['✅','❌','❎','🔰','⛔','🚫','📵','🔕','🚷','🚯','🚳','🚱','🔞','📵','🔇','🔈','🔉','🔊','📢','📣','🔔','🔕','🔋','🪫','🔌','💡','🔦','🕯','🪔','🧯','💰','💱','💲','♾','🔁','🔂','▶️','⏩','⏭','⏯','◀️','⏪','⏮','🔀','🔃','🎵','🎶','','','✖️','➗','🟰','♾','💲','💱','🏧','🔱','⚜️','🔰','♻️','✅','🆗','✔️','❎','🆘','❓','❔','❕','❗','⁉️','‼️','🔅','🔆','🔱','⚜️','💠','🌀','⭕','✖️','❌','✖','➰','➿','〽️','✳️','✴️','❇️','💯','☑️','🔘','🔲','🔳','▪️','▫️','◾','◽','◼️','◻️','🟥','🟧','🟨','🟩','🟦','🟪','⬛','⬜','🟫','🔶','🔷','🔸','🔹','🔺','🔻','💠','🔘','🔳','🔲']
};
let currentEmojiInput = null;
let currentCat = 'smileys';
function openEmojiPicker(input) {
currentEmojiInput = input;
const picker = document.getElementById('emojiPicker');
const rect = input.getBoundingClientRect();
picker.style.top = (window.scrollY + rect.bottom + 4) + 'px';
picker.style.left = rect.left + 'px';
picker.classList.remove('hidden');
showEmojiCat(currentCat);
}
function showEmojiCat(cat) {
currentCat = cat;
const grid = document.getElementById('emojiGrid');
grid.innerHTML = emojiData[cat].map(e =>
`<button type="button" onclick="pickEmoji('${e}')" class="text-xl p-1 hover:bg-gray-700 rounded transition-colors" title="${e}">${e}</button>`
).join('');
}
function pickEmoji(e) {
if (currentEmojiInput) {
currentEmojiInput.value = e;
currentEmojiInput.dispatchEvent(new Event('input', {bubbles: true}));
}
document.getElementById('emojiPicker').classList.add('hidden');
}
document.addEventListener('click', function(ev) {
const picker = document.getElementById('emojiPicker');
if (!picker.classList.contains('hidden') && !picker.contains(ev.target) &&
!ev.target.closest('[onclick*="openEmojiPicker"]') && !ev.target.closest('[onclick*="EmojiPicker"]')) {
picker.classList.add('hidden');
}
});
// Sync color picker
document.getElementById('embedColor').addEventListener('input', function() {
document.getElementById('embedColorText').textContent = this.value;
});
// Roles HTML for JS-added type blocks
const rolesHtml = `{{range .Roles}}<option value="{{.ID}}">{{.Name}}</option>{{end}}`;
const categoriesHtml = `{{range .Categories}}<option value="{{.ID}}">📁 {{.Name}}</option>{{end}}`;
const textChansHtml = `{{range .TextChans}}<option value="{{.ID}}"># {{.Name}}</option>{{end}}`;
function makeSelect(name, label, options, value = '') {
if (!options) return `<div><label class="block text-xs text-gray-400 mb-1">${label}</label><input type="text" name="${name}" value="${value}" class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500"></div>`;
return `<div><label class="block text-xs text-gray-400 mb-1">${label}</label><select name="${name}" class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500"><option value="">— Sélectionner —</option>${options}</select></div>`;
}
// SortableJS
new Sortable(document.getElementById('typesList'), {
animation: 150, handle: '.type-block',
onEnd: function() {
renumberTypes();
{{if not .IsNew}}
const ids = [...document.querySelectorAll('.type-block')].map(el => el.dataset.typeId).filter(Boolean);
if (ids.length) {
fetch('/panels/{{.Panel.ID}}/types/reorder', {
method: 'POST',
headers: {'X-CSRF-Token': '{{.CSRFToken}}', 'Content-Type': 'application/x-www-form-urlencoded'},
body: ids.map(id => 'ids[]=' + id).join('&')
});
}
{{end}}
}
});
function addType() {
document.getElementById('noTypesMsg')?.remove();
let html = document.getElementById('typeTpl').innerHTML.replaceAll('__IDX__', typeCount);
html = html.replace('__ROLES_HTML__', makeSelect(`types[${typeCount}][staff_role_id]`, 'Staff Role', rolesHtml));
html = html.replace('__CATEGORIES_HTML__', makeSelect(`types[${typeCount}][category_id]`, 'Catégorie', categoriesHtml));
html = html.replace('__TEXTCHANS_HTML__',
makeSelect(`types[${typeCount}][claim_channel_id]`, 'Claim Channel', textChansHtml) +
makeSelect(`types[${typeCount}][log_channel_id]`, 'Log Channel', textChansHtml));
const div = document.createElement('div');
div.innerHTML = html;
document.getElementById('typesList').appendChild(div.firstElementChild);
typeCount++;
renumberTypes();
htmx.process(document.getElementById('typesList'));
}
function removeType(btn) {
btn.closest('.type-block').remove();
renumberTypes();
}
function renumberTypes() {
document.querySelectorAll('.type-num').forEach((el, i) => el.textContent = i + 1);
}
</script>
{{end}}
{{define "type_block"}}
<div class="type-block border border-gray-700 rounded-lg p-4 space-y-3 cursor-move" data-index="{{.Index}}" data-type-id="{{.Type.ID}}">
<div class="flex items-center justify-between mb-1">
<span class="text-sm font-medium text-gray-300 flex items-center gap-2">
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8h16M4 16h16"/></svg>
Type <span class="type-num">{{add .Index 1}}</span>
</span>
<button type="button" onclick="removeType(this)" class="text-red-400 hover:text-red-300 text-sm">Supprimer</button>
</div>
<div class="grid grid-cols-2 gap-3">
<div><label class="block text-xs text-gray-400 mb-1">Nom interne</label>
<input type="text" name="types[{{.Index}}][name]" value="{{.Type.Name}}" required class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500"></div>
<div><label class="block text-xs text-gray-400 mb-1">Label bouton</label>
<input type="text" name="types[{{.Index}}][button_label]" value="{{.Type.ButtonLabel}}"
hx-post="/panels/preview" hx-trigger="input changed delay:300ms" hx-include="#panelForm" hx-target="#preview"
class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500"></div>
<div><label class="block text-xs text-gray-400 mb-1">Couleur bouton</label>
<select name="types[{{.Index}}][button_color]"
hx-post="/panels/preview" hx-trigger="change" hx-include="#panelForm" hx-target="#preview"
class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500">
<option value="primary" {{if eq .Type.ButtonColor "primary"}}selected{{end}}>🔵 Bleu (primary)</option>
<option value="secondary" {{if eq .Type.ButtonColor "secondary"}}selected{{end}}>⚪ Gris (secondary)</option>
<option value="success" {{if eq .Type.ButtonColor "success"}}selected{{end}}>🟢 Vert (success)</option>
<option value="danger" {{if eq .Type.ButtonColor "danger"}}selected{{end}}>🔴 Rouge (danger)</option>
</select></div>
<div><label class="block text-xs text-gray-400 mb-1">Emoji bouton</label>
<div class="flex gap-1">
<input type="text" name="types[{{.Index}}][button_emoji]" value="{{.Type.ButtonEmoji}}" readonly
hx-post="/panels/preview" hx-trigger="input changed delay:200ms" hx-include="#panelForm" hx-target="#preview"
class="flex-1 px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm cursor-pointer" placeholder="—" onclick="openEmojiPicker(this)">
<button type="button" onclick="openEmojiPicker(this.previousElementSibling)" class="px-2 py-1.5 bg-gray-700 hover:bg-gray-600 border border-gray-600 rounded text-sm">😀</button>
</div></div>
<div><label class="block text-xs text-gray-400 mb-1">Titre embed ticket</label>
<input type="text" name="types[{{.Index}}][embed_title]" value="{{.Type.EmbedTitle}}" class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500"></div>
<div><label class="block text-xs text-gray-400 mb-1">Couleur embed</label>
<input type="color" name="types[{{.Index}}][embed_color]" value="{{.Type.EmbedColor}}" class="h-9 w-full rounded cursor-pointer bg-gray-800 border border-gray-600"></div>
<div class="col-span-2"><label class="block text-xs text-gray-400 mb-1">Texte embed bienvenue</label>
<textarea name="types[{{.Index}}][embed_text]" rows="2" class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500 resize-none">{{.Type.EmbedText}}</textarea></div>
<div class="col-span-2"><label class="block text-xs text-gray-400 mb-1">Image URL (optionnel)</label>
<input type="url" name="types[{{.Index}}][image_url]" value="{{.Type.ImageURL}}" class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500" placeholder="https://..."></div>
<div>
<label class="block text-xs text-gray-400 mb-1">Staff Role</label>
{{if .Roles}}
<select name="types[{{.Index}}][staff_role_id]" class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500">
<option value="">— Sélectionner —</option>
{{range .Roles}}<option value="{{.ID}}" {{if eq .ID $.Type.StaffRoleID}}selected{{end}}>{{.Name}}</option>{{end}}
</select>
{{else}}
<input type="text" name="types[{{.Index}}][staff_role_id]" value="{{.Type.StaffRoleID}}" class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500">
{{end}}
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Catégorie</label>
{{if .Categories}}
<select name="types[{{.Index}}][category_id]" class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500">
<option value="">— Sélectionner —</option>
{{range .Categories}}<option value="{{.ID}}" {{if eq .ID $.Type.CategoryID}}selected{{end}}>📁 {{.Name}}</option>{{end}}
</select>
{{else}}
<input type="text" name="types[{{.Index}}][category_id]" value="{{.Type.CategoryID}}" class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500">
{{end}}
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Claim Channel</label>
{{if .TextChans}}
<select name="types[{{.Index}}][claim_channel_id]" class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500">
<option value="">— Sélectionner —</option>
{{range .TextChans}}<option value="{{.ID}}" {{if eq .ID $.Type.ClaimChannelID}}selected{{end}}># {{.Name}}</option>{{end}}
</select>
{{else}}
<input type="text" name="types[{{.Index}}][claim_channel_id]" value="{{.Type.ClaimChannelID}}" class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500">
{{end}}
</div>
<div>
<label class="block text-xs text-gray-400 mb-1">Log Channel</label>
{{if .TextChans}}
<select name="types[{{.Index}}][log_channel_id]" class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500">
<option value="">— Sélectionner —</option>
{{range .TextChans}}<option value="{{.ID}}" {{if eq .ID $.Type.LogChannelID}}selected{{end}}># {{.Name}}</option>{{end}}
</select>
{{else}}
<input type="text" name="types[{{.Index}}][log_channel_id]" value="{{.Type.LogChannelID}}" class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500">
{{end}}
</div>
<div><label class="block text-xs text-gray-400 mb-1">Max tickets / user</label>
<input type="number" name="types[{{.Index}}][max_per_user]" value="{{.Type.MaxPerUser}}" min="1" class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500"></div>
<div><label class="block text-xs text-gray-400 mb-1">Règle de close</label>
<select name="types[{{.Index}}][close_rule]" class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500">
<option value="staff_only" {{if eq .Type.CloseRule "staff_only"}}selected{{end}}>Staff uniquement</option>
<option value="author_if_unclaimed" {{if eq .Type.CloseRule "author_if_unclaimed"}}selected{{end}}>Auteur si non claimé</option>
</select></div>
<div class="flex items-center gap-2 pt-1">
<input type="hidden" name="types[{{.Index}}][claim_mode]" value="0">
<input type="checkbox" name="types[{{.Index}}][claim_mode]" value="1" {{if .Type.ClaimMode}}checked{{end}} class="rounded" id="cm{{.Index}}">
<label for="cm{{.Index}}" class="text-xs text-gray-400">Système de claim activé</label></div>
<div class="col-span-2"><label class="block text-xs text-gray-400 mb-1">Reup claim (minutes, 0 = global)</label>
<input type="number" name="types[{{.Index}}][claim_reup_minutes]" value="{{.Type.ClaimReupMinutes}}" min="0" max="1440"
class="w-full px-2 py-1.5 bg-gray-800 border border-gray-600 rounded text-white text-sm focus:outline-none focus:border-indigo-500"></div>
<div class="flex items-center gap-2 pt-1">
<input type="hidden" name="types[{{.Index}}][modal_enabled]" value="0">
<input type="checkbox" name="types[{{.Index}}][modal_enabled]" value="1" {{if .Type.ModalEnabled}}checked{{end}} class="rounded" id="me{{.Index}}">
<label for="me{{.Index}}" class="text-xs text-gray-400">Modal activé</label></div>
</div>
</div>
{{end}}