avec ce code cree 4 modele diferent   <!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Ganache Web UI avancé</title>
</head>
<body>
<div class="container">

  <header>
    <h1>Ganache Web UI avancé</h1>
    <nav>
      <ul>
        <li><a href="#accounts">Comptes</a></li>
        <li><a href="#transactions">Transactions</a></li>
        <li><a href="#blocks">Blocs</a></li>
        <li><a href="#contracts">Contrats</a></li>
        <li><a href="#deploy-contract">Déployer contrat</a></li>
        <li><a href="#send-tx">Envoyer TX</a></li>
      </ul>
    </nav>
  </header>

  <section id="accounts">
    <h2 class="section-title">Comptes</h2>
    <table id="accounts-table">
      <thead><tr><th>Adresse</th><th>Solde (ETH)</th></tr></thead>
      <tbody></tbody>
    </table>
  </section>

  <section id="transactions">
    <h2 class="section-title">Transactions <input id="searchTx" placeholder="Rechercher hash ou adresse..." style="float:right; width:300px;"/></h2>
    <table id="transactions-table">
      <thead><tr><th>Hash</th><th>De</th><th>À</th><th>Valeur (ETH)</th><th>Voir logs</th></tr></thead>
      <tbody></tbody>
    </table>
  </section>

  <section id="blocks">
    <h2 class="section-title">Blocs</h2>
    <table id="blocks-table">
      <thead><tr><th>Numéro</th><th>Mineur</th><th>Timestamp</th><th>Tx count</th></tr></thead>
      <tbody></tbody>
    </table>
    <div style="text-align:center;">
      <button id="prevPageBtn">Précédent</button>
      <span id="pageNumber">1</span>
      <button id="nextPageBtn">Suivant</button>
    </div>
  </section>

  <section id="contracts">
    <h2 class="section-title">Contrats</h2>
    <table id="contracts-table">
      <thead><tr><th>Nom</th><th>Adresse</th><th>Bloc déploiement</th></tr></thead>
      <tbody></tbody>
    </table>
  </section>

  <section id="deploy-contract">
    <h2 class="section-title">Déployer un contrat</h2>
    <form id="deployForm">
      <label>Nom du contrat:<input type="text" id="contractName" required/></label>
      <label>Code Solidity:<textarea id="contractCode" rows="10" required>pragma solidity ^0.8.0;

contract SimpleStorage {
  uint256 storedData;
  event DataStored(uint256 data);
  function set(uint256 x) public {
    storedData = x;
    emit DataStored(x);
  }
  function get() public view returns (uint256) {
    return storedData;
  }
}</textarea></label>
      <button type="submit">Déployer</button>
    </form>
    <p id="deployResult"></p>
  </section>

  <section id="send-tx">
    <h2 class="section-title">Envoyer une transaction</h2>
    <form id="sendTxForm">
      <label>De:
        <select id="txFrom" required></select>
      </label>
      <label>À:
        <input type="text" id="txTo" placeholder="Adresse destination" required/>
      </label>
      <label>Valeur (ETH):
        <input type="number" id="txValue" min="0" step="0.0001" required/>
      </label>
      <button type="submit">Envoyer</button>
    </form>
    <p id="sendTxResult"></p>
  </section>

</div>

<!-- Modal logs -->
<div id="modal">
  <div id="modalContent">
    <span id="closeModal">&times;</span>
    <pre id="modalText"></pre>
  </div>
</div>

<script>
  let currentBlockPage = 1;
  const blocksPerPage = 5;
  let transactions = [];

  // Modal
  const modal = document.getElementById('modal');
  const modalText = document.getElementById('modalText');
  const closeModal = document.getElementById('closeModal');
  closeModal.onclick = () => modal.style.display = 'none';
  window.onclick = e => {
    if (e.target == modal) modal.style.display = 'none';
  };

  // --- Chargement Comptes ---
  async function loadAccounts() {
    const tbody = document.querySelector('#accounts-table tbody');
    tbody.innerHTML = '';
    try {
      const resp = await fetch('/api/accounts');
      const data = await resp.json();
      data.accounts.forEach(addr => {
        const tr = document.createElement('tr');
        tr.innerHTML = `<td>${addr}</td><td>${data.balances[addr].toFixed(4)}</td>`;
        tbody.appendChild(tr);
      });
    } catch (e) {
      tbody.innerHTML = '<tr><td colspan="2">Erreur chargement comptes</td></tr>';
      console.error(e);
    }
  }

  // --- Chargement Transactions ---
  async function loadTransactions() {
    const tbody = document.querySelector('#transactions-table tbody');
    tbody.innerHTML = '';
    try {
      const resp = await fetch('/api/transactions');
      const data = await resp.json();
      transactions = data.transactions;

      displayTransactions(transactions);

    } catch (e) {
      tbody.innerHTML = '<tr><td colspan="5">Erreur chargement transactions</td></tr>';
      console.error(e);
    }
  }

  // Affichage filtré des transactions
  function displayTransactions(txList) {
    const tbody = document.querySelector('#transactions-table tbody');
    tbody.innerHTML = '';
    if (!txList.length) {
      tbody.innerHTML = '<tr><td colspan="5">Aucune transaction trouvée</td></tr>';
      return;
    }
    txList.forEach(tx => {
      const valueEth = ethers.utils.formatEther(tx.value);
      const from = tx.from ?? 'N/A';
      const to = tx.to ?? 'Création contrat';
      const tr = document.createElement('tr');
      tr.innerHTML = `
        <td style="font-family: monospace;">${tx.hash}</td>
        <td>${from}</td>
        <td>${to}</td>
        <td>${valueEth}</td>
        <td><button data-hash="${tx.hash}">Voir logs</button></td>
      `;
      tbody.appendChild(tr);
    });
  }

  // Recherche sur transactions
  document.getElementById('searchTx').addEventListener('input', (e) => {
    const search = e.target.value.toLowerCase();
    const filtered = transactions.filter(tx =>
      tx.hash.toLowerCase().includes(search) ||
      (tx.from && tx.from.toLowerCase().includes(search)) ||
      (tx.to && tx.to.toLowerCase().includes(search))
    );
    displayTransactions(filtered);
  });

  // Gestion clics voir logs
  document.querySelector('#transactions-table tbody').addEventListener('click', async (e) => {
    if (e.target.tagName === 'BUTTON') {
      const hash = e.target.getAttribute('data-hash');
      try {
        const resp = await fetch(`/api/txreceipt/${hash}`);
        const receipt = await resp.json();
        if (!receipt) {
          modalText.textContent = 'Aucun receipt trouvé pour cette transaction.';
        } else {
          modalText.textContent = JSON.stringify(receipt.logs || [], null, 2);
        }
        modal.style.display = 'block';
      } catch (err) {
        modalText.textContent = 'Erreur récupération logs : ' + err.message;
        modal.style.display = 'block';
      }
    }
  });

  // --- Chargement Blocs ---
  async function loadBlocks(page=1) {
    const tbody = document.querySelector('#blocks-table tbody');
    const pageNumSpan = document.getElementById('pageNumber');
    tbody.innerHTML = '';
    try {
      const resp = await fetch(`/api/blocks?page=${page}&limit=${blocksPerPage}`);
      const data = await resp.json();
      if (data.blocks.length === 0) {
        tbody.innerHTML = '<tr><td colspan="4">Aucun bloc trouvé.</td></tr>';
        return;
      }
      data.blocks.forEach(block => {
        const ts = new Date(block.timestamp * 1000).toLocaleString();
        const tr = document.createElement('tr');
        tr.innerHTML = `
          <td>${block.number}</td>
          <td>${block.miner}</td>
          <td>${ts}</td>
          <td>${block.transactions.length}</td>
        `;
        tbody.appendChild(tr);
      });
      pageNumSpan.textContent = page;
      currentBlockPage = page;
    } catch (e) {
      tbody.innerHTML = '<tr><td colspan="4">Erreur chargement blocs</td></tr>';
      console.error(e);
    }
  }
  document.getElementById('prevPageBtn').addEventListener('click', () => {
    if (currentBlockPage > 1) loadBlocks(currentBlockPage - 1);
  });
  document.getElementById('nextPageBtn').addEventListener('click', () => {
    loadBlocks(currentBlockPage + 1);
  });

  // --- Chargement Contrats ---
  async function loadContracts() {
    const tbody = document.querySelector('#contracts-table tbody');
    tbody.innerHTML = '';
    try {
      const resp = await fetch('/api/contracts');
      const data = await resp.json();
      if (!data.contracts.length) {
        tbody.innerHTML = '<tr><td colspan="3">Aucun contrat déployé.</td></tr>';
        return;
      }
      data.contracts.forEach(c => {
        const tr = document.createElement('tr');
        tr.innerHTML = `
          <td>${c.name}</td>
          <td style="font-family: monospace;">${c.address}</td>
          <td>${c.blockNumber}</td>
        `;
        tbody.appendChild(tr);
      });
    } catch (e) {
      tbody.innerHTML = '<tr><td colspan="3">Erreur chargement contrats</td></tr>';
      console.error(e);
    }
  }

  // --- Déploiement contrat formulaire ---
  const deployForm = document.getElementById('deployForm');
  const deployResult = document.getElementById('deployResult');
  deployForm.addEventListener('submit', async (e) => {
    e.preventDefault();
    deployResult.style.color = 'black';
    deployResult.textContent = 'Déploiement en cours...';
    const code = document.getElementById('contractCode').value;
    const contractName = document.getElementById('contractName').value.trim();
    if (!code || !contractName) {
      deployResult.style.color = 'red';
      deployResult.textContent = 'Nom et code requis.';
      return;
    }
    try {
      const resp = await fetch('/api/contracts/deploy', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ code, contractName }),
      });
      const data = await resp.json();
      if (data.error) {
        deployResult.style.color = 'red';
        deployResult.textContent = data.error;
      } else {
        deployResult.style.color = 'green';
        deployResult.textContent = `Contrat déployé à l'adresse ${data.address}`;
        deployForm.reset();
        await loadContracts();
      }
    } catch (e) {
      deployResult.style.color = 'red';
      deployResult.textContent = 'Erreur serveur : ' + e.message;
    }
  });

  // --- Envoi transaction complet ---
  const sendTxForm = document.getElementById('sendTxForm');
  const sendTxResult = document.getElementById('sendTxResult');
  sendTxForm.addEventListener('submit', async e => {
    e.preventDefault();
    sendTxResult.style.color = 'black';
    sendTxResult.textContent = 'Envoi en cours...';
    const from = document.getElementById('txFrom').value;
    const to = document.getElementById('txTo').value.trim();
    const value = parseFloat(document.getElementById('txValue').value);
    if (!from || !to || isNaN(value) || value <= 0) {
      sendTxResult.style.color = 'red';
      sendTxResult.textContent = 'Veuillez remplir tous les champs correctement.';
      return;
    }
    try {
      const resp = await fetch('/api/sendTransaction', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ from, to, value }),
      });
      const data = await resp.json();
      if (data.error) {
        sendTxResult.style.color = 'red';
        sendTxResult.textContent = 'Erreur : ' + data.error;
      } else {
        sendTxResult.style.color = 'green';
        sendTxResult.textContent = 'Transaction envoyée ! Hash: ' + data.txHash;
        sendTxForm.reset();
        await loadAccounts();
        await loadTransactions();
        await loadBlocks(currentBlockPage);
      }
    } catch (e) {
      sendTxResult.style.color = 'red';
      sendTxResult.textContent = 'Erreur serveur : ' + e.message;
    }
  });

  // --- Chargement initial ---
  async function autoReload() {
    await Promise.all([loadAccounts(), loadTransactions(), loadBlocks(currentBlockPage), loadContracts()]);
  }
  autoReload();
  setInterval(autoReload, 15000);
</script>

<script src="https://cdn.jsdelivr.net/npm/ethers@5.7.2/dist/ethers.min.js"></script>
</body>
</html>