cratery 1.11.1

Cratery -- a private cargo registry
<!DOCTYPE html>
<html lang="en" class="dark">

<head>
  <meta charset="UTF-8">
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/png" href="/webapp/favicon.png">
  <title>
    Cratery
  </title>
  <script src="https://cdn.tailwindcss.com"></script>
</head>

<header style="position: sticky; top: 0;">
  <nav class="bg-white border-gray-200 px-4 lg:px-6 py-2.5 dark:bg-gray-800">
      <div class="flex flex-wrap justify-between items-center mx-auto max-w-screen-xl">
          <a href="/webapp/index.html" class="flex items-center">
              <picture>
                  <source srcset="./logo-white.svg" media="(prefers-color-scheme: dark)" />
                  <source srcset="./logo-black.svg" media="(prefers-color-scheme: light)" />
                  <img src="./logo-white.svg" class="mr-3 h-6 sm:h-9" style="min-width: 200px;" alt="Cratery Logo" />
              </picture>
          </a>
          <div class="flex items-center lg:order-2">
            <a id="link-admin" href="/webapp/admin.html" style="cursor: pointer;" class="text-gray-800 dark:text-white hover:bg-gray-50 focus:ring-4 focus:ring-gray-300 font-medium rounded-lg text-sm px-4 lg:px-5 py-2 lg:py-2.5 mr-2 dark:hover:bg-gray-700 focus:outline-none dark:focus:ring-gray-800">Admin</a>
            <a id="link-account" href="/webapp/account.html" style="cursor: pointer;" class="text-gray-800 dark:text-white hover:bg-gray-50 focus:ring-4 focus:ring-gray-300 font-medium rounded-lg text-sm px-4 lg:px-5 py-2 lg:py-2.5 mr-2 dark:hover:bg-gray-700 focus:outline-none dark:focus:ring-gray-800">My Account</a>
            <a onclick="doLogout()" style="cursor: pointer;" class="text-gray-800 dark:text-white hover:bg-gray-50 focus:ring-4 focus:ring-gray-300 font-medium rounded-lg text-sm px-4 lg:px-5 py-2 lg:py-2.5 mr-2 dark:hover:bg-gray-700 focus:outline-none dark:focus:ring-gray-800">Logout</a>
          </div>
      </div>
  </nav>
</header>
<body onload="doPageLoad()" class="bg-white dark:bg-gray-800 content-center">
  <section class="bg-gray-50 dark:bg-gray-900 py-4 max-w-screen-lg mx-auto">
    <div class="p-6 space-y-4 md:space-y-6 sm:p-8 w-full">
      <h1 class="text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white" style="text-align: center;">
          Search for crates
      </h1>
      <form class="space-y-4 md:space-y-6" onsubmit="onSearch(); return false;" action="">
        <div>
          <input type="text" name="lookup" id="lookup" class="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="crate name" required="" autofocus="true">
        </div>
        <button type="button" class="w-full text-white bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800" onsubmit="onSearch()" onclick="onSearch()">Search</button>
      </form>
    </div>
    <div id="results" class="items-center justify-center px-6 py-8 mx-auto lg:py-0 flex flex-col space-y-4">
    </div>
    <div class="p-6 mb-4 flex flex-row flex-wrap">
      <a href="/webapp/index-outdated.html" class="font-medium text-blue-600 dark:text-blue-500 hover:underline">See crates with outdated dependencies</a>
    </div>
    <div class="p-6 mb-4 flex flex-row flex-wrap">
      <div class="w-1/2">
        <div class="block mx-4 p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
          <h5 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">
            <span style="display: inline-block;">
              <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
                <path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3" />
              </svg>
            </span>
            <span id="stats-total-downloads"></span>
          </h5>
          <p class="font-normal text-gray-700 dark:text-gray-400">Total downloads</p>
        </div>
      </div>
      <div class="w-1/2">
        <div class="block mx-4 p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
          <h5 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">
            <span>📦</span>
            <span id="stats-total-crates"></span>
          </h5>
          <p class="font-normal text-gray-700 dark:text-gray-400">Total published crates</p>
        </div>
      </div>
    </div>
    <div class="p-6 mb-4 flex flex-row flex-wrap">
      <div class="w-1/3">
        <h1 class="text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white" style="text-align: center;">
          New crates
        </h1>
      </div>
      <div class="w-1/3">
        <h1 class="text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white" style="text-align: center;">
          Most downloaded
        </h1>
      </div>
      <div class="w-1/3">
        <h1 class="text-xl font-bold leading-tight tracking-tight text-gray-900 md:text-2xl dark:text-white" style="text-align: center;">
          Just updated
        </h1>
      </div>
      <div class="w-1/3 flex flex-col" id="stats-crates-newest">
      </div>
      <div class="w-1/3 flex flex-col" id="stats-crates-most-downloaded">
      </div>
      <div class="w-1/3 flex flex-col" id="stats-crates-last-updated">
      </div>
    </div>
  </section>
</body>
<footer class="p-4 bg-white md:p-8 lg:p-10 dark:bg-gray-800">
  <div class="mx-auto max-w-screen-xl text-center">
      <span class="text-sm text-gray-500 sm:text-center dark:text-gray-400">Version <span id="version"></span>, Copyright © <span id="year"></span> <a href="https://cenotelie.fr/" target="_blank" class="hover:underline">Cénotélie</a>. All Rights Reserved.</span>
  </div>
</footer>

<link href="/webapp/index.css" rel="stylesheet" />
<script src="/webapp/api.js"></script>
<script src="/webapp/index.js"></script>
<script>
  function doPageLoad() {
    onPageLoad().then((_) => {
      apiGetCratesStats().then(renderStats);
    });
  }

  function renderStats(stats) {
    document.getElementById("stats-total-downloads").appendChild(document.createTextNode(stats.totalDownloads.toString()));
    document.getElementById("stats-total-crates").appendChild(document.createTextNode(stats.totalCrates.toString()));
    const listNewest = document.getElementById("stats-crates-newest");
    const listMostDownloaded = document.getElementById("stats-crates-most-downloaded");
    const listLastUpdated = document.getElementById("stats-crates-last-updated");
    for (const crate of stats.cratesNewest) {
      listNewest.appendChild(renderCrateLink(crate, true));
    }
    for (const crate of stats.cratesMostDownloaded) {
      listMostDownloaded.appendChild(renderCrateLink(crate, false));
    }
    for (const crate of stats.cratesLastUpdated) {
      listLastUpdated.appendChild(renderCrateLink(crate, true));
    }
  }

  function renderCrateLink(crate, withVersion, hasWarning) {
    const color = hasWarning ? "yellow" : "gray";
    const card = document.createElement("a");
    card.className = `block m-2 p-2 bg-white border border-${color}-200 rounded-lg shadow hover:bg-${color}-100 dark:bg-${color}-800 dark:border-${color}-700 dark:hover:bg-${color}-700`;
    card.href = withVersion ? `/crates/${crate.package}/${crate.version}` : `/crates/${crate.package}`;
    const title = document.createElement("h5");
    title.className = `mb-1 text-xl font-bold tracking-tight text-${color}-900 dark:text-${color}-100`;
    title.appendChild(document.createTextNode(crate.package));
    card.appendChild(title);
    if (withVersion) {
      const sub = document.createElement("p");
      sub.className = `font-normal text-${color}-700 dark:text-${color}-400`;
      sub.appendChild(document.createTextNode(crate.version));
      card.appendChild(sub);
    }
    return card;
  }

  function onSearch() {
    const search = document.getElementById("lookup").value;
    apiLookupCrates(search).then((crates) => {
      if (crates === null) {
        return;
      }
      const results = document.getElementById("results");
      while (results.children.length > 0) {
        results.removeChild(results.lastChild);
      }
      for (const crate of crates) {
        results.appendChild(renderCrate(crate));
      }
    });
    return false;
  }

  function renderCrate(crate) {
    const div = document.createElement("div");
    div.className = "w-full p-6 bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700";
    const a = document.createElement("a");
    a.setAttribute("href", `/crates/${crate.name}`);
    const h5 = document.createElement("h3");
    h5.className = "mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white";
    h5.appendChild(document.createTextNode(crate.name));
    a.appendChild(h5);
    const p1 = document.createElement("p");
    p1.className = "mb-3 font-normal text-gray-700 dark:text-gray-400"
    p1.appendChild(document.createTextNode(`last version: ${crate.max_version}`));
    const p2 = document.createElement("p");
    p2.className = "mb-3 font-normal text-gray-700 dark:text-gray-400"
    p2.appendChild(document.createTextNode(crate.description));
    div.appendChild(a);
    div.appendChild(p1);
    div.appendChild(p2);
    return div;
  }
</script>
</html>