Trello Clone - Tableau Kanban

🏷️ Extraits de code HTML 📅 10/04/2026 11:00:00 👤 Mezgani said
Bootstrap Bootstrap5 Trello Kanban Dashboard Html

Template Bootstrap 5 de tableau Kanban type Trello avec colonnes, cartes de taches et organisation visuelle.

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8" />
  <meta name="copyright" content="MEZGANI Said" />
  <meta name="author" content="AngularForAll" />
  <meta name="robots" content="noindex, nofollow" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Template Trello Bootstrap 5 01 | AngularForAll</title>
<!-- Bootstrap 5 CSS -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
  <!-- Bootstrap Icons -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
  
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
      min-height: 100vh;
      padding: 20px;
      color: #172b4d;
    }

    /* Header style Trello */
    .trello-header {
      background: rgba(26, 26, 46, 0.8);
      backdrop-filter: blur(10px);
      padding: 12px 20px;
      border-radius: 16px;
      margin-bottom: 24px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      flex-wrap: wrap;
      gap: 15px;
      border: 1px solid rgba(255, 255, 255, 0.1);
    }

    .board-title {
      display: flex;
      align-items: center;
      gap: 12px;
    }

    .board-title i {
      color: #00b4d8;
      font-size: 1.8rem;
    }

    .board-title h1 {
      color: white;
      font-size: 1.5rem;
      font-weight: 600;
      margin: 0;
    }

    .board-actions {
      display: flex;
      gap: 10px;
    }

    .btn-trello {
      background: rgba(255, 255, 255, 0.1);
      color: white;
      border: none;
      padding: 8px 16px;
      border-radius: 8px;
      font-weight: 500;
      transition: all 0.2s;
    }

    .btn-trello:hover {
      background: rgba(255, 255, 255, 0.2);
      color: white;
    }

    .btn-trello-primary {
      background: #00b4d8;
      color: white;
      border: none;
      padding: 8px 16px;
      border-radius: 8px;
      font-weight: 500;
      transition: all 0.2s;
    }

    .btn-trello-primary:hover {
      background: #0096c7;
    }

    /* Container des listes */
    .lists-container {
      display: flex;
      gap: 20px;
      overflow-x: auto;
      padding-bottom: 20px;
      min-height: calc(100vh - 180px);
    }

    /* Style d'une liste */
    .list {
      background: #ebecf0;
      border-radius: 16px;
      width: 300px;
      min-width: 300px;
      max-height: calc(100vh - 160px);
      display: flex;
      flex-direction: column;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    }

    .list-header {
      padding: 16px 12px 8px;
      display: flex;
      align-items: center;
      justify-content: space-between;
    }

    .list-header h3 {
      font-size: 1rem;
      font-weight: 600;
      margin: 0;
      color: #172b4d;
    }

    .list-actions {
      display: flex;
      gap: 5px;
    }

    .list-actions button {
      background: none;
      border: none;
      color: #6b778c;
      cursor: pointer;
      padding: 4px 6px;
      border-radius: 4px;
      font-size: 1rem;
    }

    .list-actions button:hover {
      background: rgba(0, 0, 0, 0.1);
      color: #172b4d;
    }

    /* Container des cartes */
    .cards-container {
      padding: 8px 8px 4px;
      flex: 1;
      overflow-y: auto;
      min-height: 50px;
    }

    /* Style d'une carte */
    .card-item {
      background: white;
      border-radius: 10px;
      padding: 12px 12px 8px;
      margin-bottom: 8px;
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
      cursor: grab;
      transition: all 0.2s;
      border: 1px solid rgba(0, 0, 0, 0.05);
      position: relative;
    }

    .card-item:hover {
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
      transform: translateY(-2px);
    }

    .card-item.dragging {
      opacity: 0.5;
      cursor: grabbing;
    }

    .card-title {
      font-weight: 500;
      margin-bottom: 6px;
      word-break: break-word;
    }

    .card-badges {
      display: flex;
      gap: 8px;
      margin-bottom: 8px;
      flex-wrap: wrap;
    }

    .badge-label {
      font-size: 0.7rem;
      padding: 2px 8px;
      border-radius: 4px;
      font-weight: 600;
      color: white;
    }

    .badge-priority {
      background: #eb5a46;
    }

    .badge-feature {
      background: #00b4d8;
    }

    .badge-bug {
      background: #f39c12;
    }

    .card-footer {
      display: flex;
      align-items: center;
      justify-content: space-between;
      margin-top: 8px;
    }

    .card-meta {
      display: flex;
      gap: 12px;
      color: #6b778c;
      font-size: 0.8rem;
    }

    .card-meta span {
      display: flex;
      align-items: center;
      gap: 3px;
    }

    .card-avatar {
      width: 24px;
      height: 24px;
      border-radius: 50%;
      background: #00b4d8;
      display: flex;
      align-items: center;
      justify-content: center;
      color: white;
      font-size: 0.7rem;
      font-weight: 600;
    }

    /* Footer de liste (ajout de carte) */
    .list-footer {
      padding: 8px 12px 12px;
    }

    .add-card-btn {
      width: 100%;
      background: none;
      border: none;
      color: #6b778c;
      padding: 8px 12px;
      border-radius: 8px;
      text-align: left;
      font-size: 0.9rem;
      transition: all 0.2s;
      display: flex;
      align-items: center;
      gap: 8px;
    }

    .add-card-btn:hover {
      background: rgba(0, 0, 0, 0.05);
      color: #172b4d;
    }

    .add-card-form {
      display: none;
    }

    .add-card-form.active {
      display: block;
    }

    .add-card-form textarea {
      width: 100%;
      border: none;
      border-radius: 8px;
      padding: 8px 12px;
      resize: none;
      font-size: 0.9rem;
      margin-bottom: 8px;
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    }

    .add-card-form textarea:focus {
      outline: 2px solid #00b4d8;
    }

    .form-actions {
      display: flex;
      align-items: center;
      gap: 8px;
    }

    .form-actions button {
      padding: 6px 12px;
    }

    /* Ajout de liste */
    .add-list {
      background: rgba(255, 255, 255, 0.1);
      border-radius: 16px;
      width: 300px;
      min-width: 300px;
      padding: 12px;
      backdrop-filter: blur(10px);
      border: 1px solid rgba(255, 255, 255, 0.1);
      height: fit-content;
    }

    .add-list-btn {
      width: 100%;
      background: none;
      border: none;
      color: white;
      padding: 12px;
      border-radius: 8px;
      text-align: left;
      font-size: 1rem;
      transition: all 0.2s;
      display: flex;
      align-items: center;
      gap: 8px;
    }

    .add-list-btn:hover {
      background: rgba(255, 255, 255, 0.1);
    }

    .add-list-form {
      display: none;
    }

    .add-list-form.active {
      display: block;
    }

    .add-list-form input {
      width: 100%;
      border: none;
      border-radius: 8px;
      padding: 10px 12px;
      font-size: 0.95rem;
      margin-bottom: 8px;
    }

    /* Modal */
    .modal-content {
      border-radius: 16px;
      border: none;
    }

    .modal-header {
      border-bottom: 1px solid #e9ecef;
      padding: 20px 24px;
    }

    .modal-body {
      padding: 24px;
    }

    .modal-footer {
      border-top: 1px solid #e9ecef;
      padding: 16px 24px;
    }

    .priority-selector {
      display: flex;
      gap: 10px;
      margin-top: 8px;
    }

    .priority-option {
      flex: 1;
      padding: 8px;
      border-radius: 8px;
      text-align: center;
      cursor: pointer;
      border: 2px solid transparent;
      transition: all 0.2s;
    }

    .priority-option.selected {
      border-color: #00b4d8;
    }

    .priority-high { background: #eb5a46; color: white; }
    .priority-medium { background: #f39c12; color: white; }
    .priority-low { background: #2ecc71; color: white; }

    /* Scrollbar personnalisée */
    .lists-container::-webkit-scrollbar {
      height: 8px;
    }

    .lists-container::-webkit-scrollbar-track {
      background: rgba(255, 255, 255, 0.05);
      border-radius: 10px;
    }

    .lists-container::-webkit-scrollbar-thumb {
      background: rgba(255, 255, 255, 0.2);
      border-radius: 10px;
    }

    .cards-container::-webkit-scrollbar {
      width: 5px;
    }

    .cards-container::-webkit-scrollbar-thumb {
      background: #c1c7d0;
      border-radius: 10px;
    }

    /* Responsive */
    @media (max-width: 768px) {
      body { padding: 12px; }
      
      .trello-header {
        padding: 10px 15px;
      }
      
      .board-title h1 {
        font-size: 1.2rem;
      }
      
      .list {
        width: 280px;
        min-width: 280px;
      }
      
      .add-list {
        width: 280px;
        min-width: 280px;
      }
    }

    /* Drag over effect */
    .cards-container.drag-over {
      background: rgba(0, 180, 216, 0.1);
      border-radius: 8px;
    }
  </style>
</head>
<body>

<!-- Header -->
<div class="trello-header">
  <div class="board-title">
    <i class="bi bi-trello"></i>
    <h1>Tableau Principal</h1>
    <span class="badge bg-secondary" style="margin-left: 10px;">Kanban</span>
  </div>
  <div class="board-actions">
    <button class="btn-trello" onclick="showBoardMenu()">
      <i class="bi bi-three-dots"></i>
    </button>
    <button class="btn-trello-primary" onclick="exportBoard()">
      <i class="bi bi-download"></i> Exporter
    </button>
  </div>
</div>

<!-- Container des listes -->
<div class="lists-container" id="listsContainer">
  <!-- Les listes seront injectées ici par JavaScript -->
</div>

<!-- Bouton d'ajout de liste (fixe) -->
<div class="add-list" id="addListSection">
  <button class="add-list-btn" id="showAddListBtn">
    <i class="bi bi-plus-lg"></i> Ajouter une liste
  </button>
  <div class="add-list-form" id="addListForm">
    <input type="text" id="newListTitle" placeholder="Titre de la liste...">
    <div class="form-actions">
      <button class="btn btn-primary btn-sm" onclick="addNewList()">Ajouter</button>
      <button class="btn btn-link btn-sm text-white" onclick="hideAddListForm()">
        <i class="bi bi-x-lg"></i>
      </button>
    </div>
  </div>
</div>

<!-- Modal pour ajouter/modifier une carte -->
<div class="modal fade" id="cardModal" tabindex="-1">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="modalTitle">Ajouter une carte</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
      </div>
      <div class="modal-body">
        <form id="cardForm">
          <input type="hidden" id="cardId">
          <input type="hidden" id="listId">
          
          <div class="mb-3">
            <label class="form-label fw-bold">Titre</label>
            <input type="text" class="form-control" id="cardTitle" required placeholder="Titre de la carte">
          </div>
          
          <div class="mb-3">
            <label class="form-label fw-bold">Description</label>
            <textarea class="form-control" id="cardDescription" rows="3" placeholder="Description (optionnel)"></textarea>
          </div>
          
          <div class="mb-3">
            <label class="form-label fw-bold">Type</label>
            <select class="form-select" id="cardType">
              <option value="feature">✨ Fonctionnalité</option>
              <option value="bug">🐛 Bug</option>
              <option value="task">📋 Tâche</option>
            </select>
          </div>
          
          <div class="mb-3">
            <label class="form-label fw-bold">Priorité</label>
            <div class="priority-selector">
              <div class="priority-option priority-high" data-priority="high" onclick="selectPriority('high')">Haute</div>
              <div class="priority-option priority-medium selected" data-priority="medium" onclick="selectPriority('medium')">Moyenne</div>
              <div class="priority-option priority-low" data-priority="low" onclick="selectPriority('low')">Basse</div>
            </div>
            <input type="hidden" id="cardPriority" value="medium">
          </div>
          
          <div class="mb-3">
            <label class="form-label fw-bold">Assigné à</label>
            <select class="form-select" id="cardAssignee">
              <option value="Moi">👤 Moi</option>
              <option value="Marie">👩 Marie</option>
              <option value="Thomas">👨 Thomas</option>
              <option value="Sophie">👩 Sophie</option>
            </select>
          </div>
        </form>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
        <button type="button" class="btn btn-primary" onclick="saveCard()">Enregistrer</button>
      </div>
    </div>
  </div>
</div>

<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>

<script>
  // ===== DONNÉES =====
  let boardData = {
    lists: [
      {
        id: 'list-1',
        title: 'À faire',
        cards: [
          { id: 'card-1', title: 'Design de la page d\'accueil', description: 'Créer la maquette Figma', type: 'feature', priority: 'high', assignee: 'Marie', comments: 2 },
          { id: 'card-2', title: 'Correction bug login', description: 'Erreur 500 sur l\'authentification', type: 'bug', priority: 'high', assignee: 'Thomas', comments: 5 }
        ]
      },
      {
        id: 'list-2',
        title: 'En cours',
        cards: [
          { id: 'card-3', title: 'Développement API', description: 'Créer les endpoints REST', type: 'feature', priority: 'medium', assignee: 'Moi', comments: 3 },
          { id: 'card-4', title: 'Tests unitaires', description: 'Couvrir le code à 80%', type: 'task', priority: 'medium', assignee: 'Sophie', comments: 1 }
        ]
      },
      {
        id: 'list-3',
        title: 'Terminé',
        cards: [
          { id: 'card-5', title: 'Mise en place CI/CD', description: 'GitHub Actions configuré', type: 'task', priority: 'low', assignee: 'Moi', comments: 0 },
          { id: 'card-6', title: 'Documentation', description: 'Guide d\'installation', type: 'task', priority: 'low', assignee: 'Marie', comments: 2 }
        ]
      }
    ]
  };

  let draggedCard = null;
  let cardModal;
  let currentListId = null;
  let editingCardId = null;

  // ===== INITIALISATION =====
  document.addEventListener('DOMContentLoaded', function() {
    cardModal = new bootstrap.Modal(document.getElementById('cardModal'));
    renderBoard();
    setupEventListeners();
  });

  function setupEventListeners() {
    document.getElementById('showAddListBtn').addEventListener('click', showAddListForm);
  }

  // ===== RENDU DU TABLEAU =====
  function renderBoard() {
    const container = document.getElementById('listsContainer');
    
    let html = '';
    boardData.lists.forEach(list => {
      html += renderList(list);
    });
    
    container.innerHTML = html;
    
    // Attacher les événements de drag & drop
    attachDragDropEvents();
  }

  function renderList(list) {
    const cardsHtml = list.cards.map(card => renderCard(card)).join('');
    
    return `
      <div class="list" data-list-id="${list.id}">
        <div class="list-header">
          <h3>${list.title} <span class="badge bg-secondary ms-2">${list.cards.length}</span></h3>
          <div class="list-actions">
            <button onclick="addCardToList('${list.id}')" title="Ajouter une carte">
              <i class="bi bi-plus-lg"></i>
            </button>
            <button onclick="editList('${list.id}')" title="Modifier la liste">
              <i class="bi bi-pencil"></i>
            </button>
            <button onclick="deleteList('${list.id}')" title="Supprimer la liste">
              <i class="bi bi-trash"></i>
            </button>
          </div>
        </div>
        <div class="cards-container" data-list-id="${list.id}">
          ${cardsHtml}
        </div>
        <div class="list-footer">
          <button class="add-card-btn" onclick="showAddCardForm('${list.id}')">
            <i class="bi bi-plus-lg"></i> Ajouter une carte
          </button>
          <div class="add-card-form" id="addCardForm-${list.id}">
            <textarea placeholder="Titre de la carte..." id="newCardTitle-${list.id}" rows="2"></textarea>
            <div class="form-actions">
              <button class="btn btn-primary btn-sm" onclick="quickAddCard('${list.id}')">Ajouter</button>
              <button class="btn btn-link btn-sm" onclick="hideAddCardForm('${list.id}')">
                <i class="bi bi-x-lg"></i>
              </button>
            </div>
          </div>
        </div>
      </div>
    `;
  }

  function renderCard(card) {
    const typeBadge = getTypeBadge(card.type);
    const priorityClass = `priority-${card.priority}`;
    
    return `
      <div class="card-item" draggable="true" data-card-id="${card.id}" data-list-id="${card.listId || ''}">
        <div class="card-title">${card.title}</div>
        <div class="card-badges">
          <span class="badge-label ${typeBadge.class}">${typeBadge.label}</span>
          <span class="badge-label bg-${getPriorityColor(card.priority)}">${card.priority}</span>
        </div>
        ${card.description ? `<div style="font-size: 0.8rem; color: #6b778c; margin-bottom: 8px;">${card.description.substring(0, 50)}...</div>` : ''}
        <div class="card-footer">
          <div class="card-meta">
            ${card.comments > 0 ? `<span><i class="bi bi-chat"></i> ${card.comments}</span>` : ''}
            <span><i class="bi bi-paperclip"></i></span>
          </div>
          <div class="card-avatar" title="${card.assignee}">
            ${card.assignee.charAt(0)}
          </div>
        </div>
        <button class="btn btn-sm btn-outline-secondary mt-2" style="width: 100%;" onclick="editCard('${card.id}')">
          <i class="bi bi-pencil"></i> Modifier
        </button>
      </div>
    `;
  }

  function getTypeBadge(type) {
    const types = {
      feature: { label: 'Fonctionnalité', class: 'badge-feature' },
      bug: { label: 'Bug', class: 'badge-bug' },
      task: { label: 'Tâche', class: 'badge-label bg-secondary' }
    };
    return types[type] || types.task;
  }

  function getPriorityColor(priority) {
    const colors = { high: 'danger', medium: 'warning', low: 'success' };
    return colors[priority] || 'secondary';
  }

  // ===== DRAG & DROP =====
  function attachDragDropEvents() {
    const cards = document.querySelectorAll('.card-item');
    const containers = document.querySelectorAll('.cards-container');
    
    cards.forEach(card => {
      card.addEventListener('dragstart', handleDragStart);
      card.addEventListener('dragend', handleDragEnd);
    });
    
    containers.forEach(container => {
      container.addEventListener('dragover', handleDragOver);
      container.addEventListener('dragenter', handleDragEnter);
      container.addEventListener('dragleave', handleDragLeave);
      container.addEventListener('drop', handleDrop);
    });
  }

  function handleDragStart(e) {
    const card = e.target.closest('.card-item');
    draggedCard = {
      id: card.dataset.cardId,
      sourceListId: card.closest('.cards-container').dataset.listId,
      element: card
    };
    card.classList.add('dragging');
    e.dataTransfer.effectAllowed = 'move';
  }

  function handleDragEnd(e) {
    e.target.classList.remove('dragging');
    document.querySelectorAll('.cards-container').forEach(c => {
      c.classList.remove('drag-over');
    });
  }

  function handleDragOver(e) {
    e.preventDefault();
    e.dataTransfer.dropEffect = 'move';
  }

  function handleDragEnter(e) {
    e.preventDefault();
    e.target.closest('.cards-container')?.classList.add('drag-over');
  }

  function handleDragLeave(e) {
    e.target.closest('.cards-container')?.classList.remove('drag-over');
  }

  function handleDrop(e) {
    e.preventDefault();
    const container = e.target.closest('.cards-container');
    container.classList.remove('drag-over');
    
    const targetListId = container.dataset.listId;
    
    if (draggedCard && draggedCard.sourceListId !== targetListId) {
      // Trouver la carte dans la liste source
      const sourceList = boardData.lists.find(l => l.id === draggedCard.sourceListId);
      const targetList = boardData.lists.find(l => l.id === targetListId);
      
      if (sourceList && targetList) {
        const cardIndex = sourceList.cards.findIndex(c => c.id === draggedCard.id);
        if (cardIndex !== -1) {
          const [movedCard] = sourceList.cards.splice(cardIndex, 1);
          targetList.cards.push(movedCard);
          renderBoard();
        }
      }
    }
    
    draggedCard = null;
  }

  // ===== GESTION DES LISTES =====
  function showAddListForm() {
    document.getElementById('addListForm').classList.add('active');
    document.getElementById('showAddListBtn').style.display = 'none';
    document.getElementById('newListTitle').focus();
  }

  function hideAddListForm() {
    document.getElementById('addListForm').classList.remove('active');
    document.getElementById('showAddListBtn').style.display = 'block';
    document.getElementById('newListTitle').value = '';
  }

  function addNewList() {
    const title = document.getElementById('newListTitle').value.trim();
    if (!title) return;
    
    const newList = {
      id: 'list-' + Date.now(),
      title: title,
      cards: []
    };
    
    boardData.lists.push(newList);
    renderBoard();
    hideAddListForm();
  }

  function deleteList(listId) {
    if (confirm('Supprimer cette liste et toutes ses cartes ?')) {
      boardData.lists = boardData.lists.filter(l => l.id !== listId);
      renderBoard();
    }
  }

  function editList(listId) {
    const list = boardData.lists.find(l => l.id === listId);
    if (!list) return;
    
    const newTitle = prompt('Nouveau titre de la liste:', list.title);
    if (newTitle && newTitle.trim()) {
      list.title = newTitle.trim();
      renderBoard();
    }
  }

  // ===== GESTION DES CARTES =====
  function showAddCardForm(listId) {
    document.getElementById(`addCardForm-${listId}`).classList.add('active');
    document.querySelector(`[onclick="showAddCardForm('${listId}')"]`).style.display = 'none';
    document.getElementById(`newCardTitle-${listId}`).focus();
  }

  function hideAddCardForm(listId) {
    document.getElementById(`addCardForm-${listId}`).classList.remove('active');
    document.querySelector(`[onclick="showAddCardForm('${listId}')"]`).style.display = 'block';
    document.getElementById(`newCardTitle-${listId}`).value = '';
  }

  function quickAddCard(listId) {
    const title = document.getElementById(`newCardTitle-${listId}`).value.trim();
    if (!title) return;
    
    const list = boardData.lists.find(l => l.id === listId);
    if (list) {
      const newCard = {
        id: 'card-' + Date.now(),
        title: title,
        description: '',
        type: 'task',
        priority: 'medium',
        assignee: 'Moi',
        comments: 0
      };
      list.cards.push(newCard);
      renderBoard();
    }
    
    hideAddCardForm(listId);
  }

  function addCardToList(listId) {
    currentListId = listId;
    editingCardId = null;
    document.getElementById('modalTitle').textContent = 'Ajouter une carte';
    document.getElementById('cardForm').reset();
    document.getElementById('listId').value = listId;
    document.getElementById('cardPriority').value = 'medium';
    
    document.querySelectorAll('.priority-option').forEach(opt => {
      opt.classList.remove('selected');
      if (opt.dataset.priority === 'medium') {
        opt.classList.add('selected');
      }
    });
    
    cardModal.show();
  }

  function editCard(cardId) {
    let foundCard = null;
    let foundList = null;
    
    for (const list of boardData.lists) {
      const card = list.cards.find(c => c.id === cardId);
      if (card) {
        foundCard = card;
        foundList = list;
        break;
      }
    }
    
    if (!foundCard) return;
    
    editingCardId = cardId;
    currentListId = foundList.id;
    
    document.getElementById('modalTitle').textContent = 'Modifier la carte';
    document.getElementById('cardId').value = cardId;
    document.getElementById('listId').value = foundList.id;
    document.getElementById('cardTitle').value = foundCard.title;
    document.getElementById('cardDescription').value = foundCard.description || '';
    document.getElementById('cardType').value = foundCard.type;
    document.getElementById('cardPriority').value = foundCard.priority;
    document.getElementById('cardAssignee').value = foundCard.assignee;
    
    document.querySelectorAll('.priority-option').forEach(opt => {
      opt.classList.remove('selected');
      if (opt.dataset.priority === foundCard.priority) {
        opt.classList.add('selected');
      }
    });
    
    cardModal.show();
  }

  function selectPriority(priority) {
    document.querySelectorAll('.priority-option').forEach(opt => {
      opt.classList.remove('selected');
      if (opt.dataset.priority === priority) {
        opt.classList.add('selected');
      }
    });
    document.getElementById('cardPriority').value = priority;
  }

  function saveCard() {
    const cardId = document.getElementById('cardId').value;
    const listId = document.getElementById('listId').value;
    const title = document.getElementById('cardTitle').value.trim();
    const description = document.getElementById('cardDescription').value.trim();
    const type = document.getElementById('cardType').value;
    const priority = document.getElementById('cardPriority').value;
    const assignee = document.getElementById('cardAssignee').value;
    
    if (!title) {
      alert('Le titre est obligatoire');
      return;
    }
    
    const list = boardData.lists.find(l => l.id === listId);
    if (!list) return;
    
    if (cardId) {
      const card = list.cards.find(c => c.id === cardId);
      if (card) {
        card.title = title;
        card.description = description;
        card.type = type;
        card.priority = priority;
        card.assignee = assignee;
      }
    } else {
      const newCard = {
        id: 'card-' + Date.now(),
        title: title,
        description: description,
        type: type,
        priority: priority,
        assignee: assignee,
        comments: 0
      };
      list.cards.push(newCard);
    }
    
    renderBoard();
    cardModal.hide();
  }

  // ===== FONCTIONS UTILITAIRES =====
  function showBoardMenu() {
    alert('Menu du tableau\n- Paramètres\n- Activité\n- Partager');
  }

  function exportBoard() {
    const dataStr = JSON.stringify(boardData, null, 2);
    const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
    
    const exportFileDefaultName = 'trello-board.json';
    
    const linkElement = document.createElement('a');
    linkElement.setAttribute('href', dataUri);
    linkElement.setAttribute('download', exportFileDefaultName);
    linkElement.click();
  }

  console.log('✅ Trello Clone - Prêt !');
</script>

</body>
</html>

Ouvrir cet aperçu dans un nouvel onglet du navigateur

🔗 Ouvrir dans le navigateur