Harjutus 7: Gitgub API päring

index.mjs:

import "./styles.css";

let givenProfile = "";
let profileName = "";
let profileId = "";
let profileLink = "";
let profileRepos = "";
let profileFollowers = "";
let profileFollowing = "";
let profileAvatar = "";
let isLoading = false;
let isDarkTheme = true;

// ======== FUNKTSIOONID ========

// Lehe joonistamine/renderdamine
function renderPage() {
  document.getElementById("app").innerHTML = `
    <div>
      <h1>Github profile viewer</h1>
      <p>Please enter profile name:</p>
      <div class="input-row">
        <input value="${givenProfile}" placeholder="Enter GitHub username" />
        <button id="clearBtn" title="Clear input">✖️</button>
      </div>
      <div class="actions-row">
        <button id="copyLinkBtn" ${
          profileLink ? "" : "disabled"
        } title="Copy profile link">📋 Copy Link</button>
        <label class="theme-switch">
          <input type="checkbox" id="themeToggle" ${
            isDarkTheme ? "checked" : ""
          }>
          <span class="slider"></span>
          <span class="label-text">Dark Mode</span>
        </label>
      </div>
      ${
        isLoading
          ? '<div class="loading" title="Loading..."></div>'
          : `<img id="avatar" src="${
              profileAvatar
                ? profileAvatar
                : "https://via.placeholder.com/120?text=No+Avatar"
            }" alt="Avatar" />`
      }
      <div class="content">
        <h1 id="name">Name: ${profileName ? profileName : "-"}</h1>
        <p id="id">Id: ${profileId ? profileId : "-"}</p>
        <p id="repos">Public repos: ${profileRepos ? profileRepos : "-"}</p>
        <p id="followers">Followers: ${
          profileFollowers ? profileFollowers : "-"
        }</p>
        <p id="following">Following: ${
          profileFollowing ? profileFollowing : "-"
        }</p>
        <p id="profileurl">Link: ${
          profileLink && profileName
            ? `<a href="${profileLink}" target="_blank" rel="noopener noreferrer">${profileLink}</a>`
            : "-"
        }</p>
      </div>
    </div>
  `;

  const input = document.querySelector("input");
  input.addEventListener("change", updateValue);

  document.getElementById("clearBtn").addEventListener("click", clearInput);
  document
    .getElementById("copyLinkBtn")
    .addEventListener("click", copyProfileLink);
  document
    .getElementById("themeToggle")
    .addEventListener("change", toggleTheme);
}

// Sisendi muutuste töötlemine
function updateValue(e) {
  givenProfile = e.target.value.trim();
  fetchProfile();
}

// Andmete tühjendamine
function clearInput() {
  givenProfile = "";
  profileName = "";
  profileId = "";
  profileLink = "";
  profileRepos = "";
  profileFollowers = "";
  profileFollowing = "";
  profileAvatar = "";
  renderPage();
}

// Profiili lingi kopeerimine
function copyProfileLink() {
  if (!profileLink) return;
  navigator.clipboard.writeText(profileLink).then(() => {
    alert("Profile link copied to clipboard!");
  });
}

// Teemade vahetamine (tume/hele)
function toggleTheme(e) {
  isDarkTheme = e.target.checked;
  if (isDarkTheme) {
    document.body.classList.add("dark-theme");
    document.body.classList.remove("light-theme");
  } else {
    document.body.classList.add("light-theme");
    document.body.classList.remove("dark-theme");
  }
}

// GitHub profiili päringu tegemine
async function fetchProfile() {
  if (!givenProfile) {
    clearInput();
    return;
  }
  isLoading = true;
  renderPage();
  try {
    const response = await fetch(
      `https://api.github.com/users/${givenProfile}`
    );
    const rateLimitRemaining = response.headers.get("X-RateLimit-Remaining");
    isLoading = false;
    if (!response.ok) {
      profileName = "User not found";
      profileId = "-";
      profileLink = "";
      profileRepos = "-";
      profileFollowers = "-";
      profileFollowing = "-";
      profileAvatar = "";
    } else {
      const data = await response.json();
      profileName = data.login || "-";
      profileId = data.id || "-";
      profileLink = data.html_url || "";
      profileRepos = data.public_repos || "-";
      profileFollowers = data.followers || "-";
      profileFollowing = data.following || "-";
      profileAvatar = data.avatar_url || "";
    }

    if (rateLimitRemaining === "0") {
      profileName = "API rate limit reached. Try again later.";
      profileId = "";
      profileLink = "";
      profileRepos = "";
    }
    renderPage();
  } catch (e) {
    isLoading = false;
    profileName = "Error";
    profileId = "-";
    profileLink = "";
    profileRepos = "-";
    profileFollowers = "-";
    profileFollowing = "-";
    profileAvatar = "";
    renderPage();
  }
}

// Algne lehe renderdus
renderPage();

styles.css:

body {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
  color: #f0f0f0;
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: center;
  min-height: 100vh;
  align-items: center;
  font-size: 16px;
  user-select: none;
  transition: background 0.4s ease, color 0.4s ease;
}

/* Põhikonteiner */
#app > div {
  background: rgba(255, 255, 255, 0.12);
  backdrop-filter: blur(18px);
  border-radius: 18px;
  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.4);
  padding: 35px 45px;
  width: 360px;
  max-width: 95vw;
  text-align: center;
  transition: box-shadow 0.3s ease, transform 0.3s ease;
  position: relative;
}

/* Efekt hiirega */
#app > div:hover {
  box-shadow: 0 16px 45px rgba(118, 75, 162, 0.75);
  transform: translateY(-5px);
}

/* Pealkiri */
h1 {
  font-weight: 800;
  font-size: 2.4rem;
  margin-bottom: 20px;
  letter-spacing: 1.4px;
  color: #fff;
  text-shadow: 0 0 15px #9f7aea;
}

/* Sisendväli */
input {
  width: 100%;
  padding: 14px 18px;
  border-radius: 12px;
  border: none;
  font-size: 1.05rem;
  outline: none;
  transition: background-color 0.3s ease, box-shadow 0.4s ease;
  margin-bottom: 30px;
  box-shadow: inset 0 0 12px rgba(255, 255, 255, 0.25);
  background-color: rgba(255, 255, 255, 0.18);
  color: #fff;
  font-weight: 600;
}

/* Kohatäitja (placeholder) stiil */
input::placeholder {
  color: #e3d7ffcc;
  font-style: italic;
}

/* Aktiivne sisend */
input:focus {
  background-color: rgba(255, 255, 255, 0.35);
  box-shadow: 0 0 18px #8b62ff;
}

/* Nupud */
button {
  cursor: pointer;
  border: none;
  border-radius: 10px;
  background: #764ba2cc;
  color: #fff;
  font-weight: 700;
  padding: 8px 14px;
  margin-left: 10px;
  font-size: 1rem;
  transition: background-color 0.3s ease;
  box-shadow: 0 4px 12px rgba(118, 75, 162, 0.5);
  user-select: none;
  height: 42px;
}

/* Nupu hover-efekt */
button:hover:not(:disabled) {
  background-color: #9f7aea;
  box-shadow: 0 6px 18px rgba(159, 122, 234, 0.7);
}

/* Keelatud nupp */
button:disabled {
  background-color: #55555555;
  cursor: default;
  box-shadow: none;
}

/* Sisendi ja nupu rida */
.input-row {
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Toimingute rida */
.actions-row {
  margin-bottom: 20px;
  display: flex;
  justify-content: center;
  gap: 15px;
  flex-wrap: wrap;
}

/* Teemalüliti */
.theme-switch {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  color: #ddd;
  user-select: none;
  font-size: 0.95rem;
}

/* Peidame checkboxi */
.theme-switch input[type="checkbox"] {
  display: none;
}

/* Lüliti taust */
.theme-switch .slider {
  width: 44px;
  height: 22px;
  background-color: #555;
  border-radius: 12px;
  position: relative;
  transition: background-color 0.3s ease;
  cursor: pointer;
}

/* Lüliti nupp */
.theme-switch .slider::before {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  background-color: #fff;
  border-radius: 50%;
  transition: transform 0.3s ease;
}

/* Kui lüliti on aktiveeritud */
.theme-switch input:checked + .slider {
  background-color: #764ba2;
}

.theme-switch input:checked + .slider::before {
  transform: translateX(22px);
}

/* Profiili info sektsioon */
.content {
  margin-top: 15px;
  text-align: left;
  color: #ddd;
  font-weight: 500;
  user-select: text;
  line-height: 1.5;
}

/* Kasutajanimi */
.content h1#name {
  font-size: 1.8rem;
  color: #ffd700;
  text-shadow: 0 0 10px #ffd700cc;
  margin-bottom: 10px;
}

/* Profiili tekst */
.content p {
  margin: 8px 0;
  font-size: 1.05rem;
  display: flex;
  align-items: center;
}

/* Ikoonid iga välja ees */
.content p#id::before {
  content: "🆔";
  margin-right: 8px;
}

.content p#repos::before {
  content: "📁";
  margin-right: 8px;
}

.content p#followers::before {
  content: "👥";
  margin-right: 8px;
}

.content p#following::before {
  content: "➡️";
  margin-right: 8px;
}

.content p#profileurl::before {
  content: "🔗";
  margin-right: 8px;
}

/* Lingid */
.content a {
  color: #ffeb3b;
  text-decoration: none;
  font-weight: 600;
  transition: color 0.3s ease;
  word-break: break-word;
}

.content a:hover {
  color: #fff176;
  text-decoration: underline;
}

/* Avatar */
#avatar {
  width: 120px;
  height: 120px;
  border-radius: 50%;
  margin: 0 auto 25px;
  box-shadow: 0 0 15px rgba(255, 215, 0, 0.7);
  border: 3.5px solid #ffd700;
  object-fit: cover;
  display: block;
  transition: transform 0.4s ease;
}

#avatar:hover {
  transform: scale(1.1) rotate(5deg);
}

/* Laadimise animatsioon */
@keyframes pulse {
  0%,
  100% {
    box-shadow: 0 0 15px #764ba2;
  }
  50% {
    box-shadow: 0 0 30px #9f7aea;
  }
}

.loading {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  margin: 0 auto 20px;
  background: #764ba2;
  animation: pulse 1.5s infinite ease-in-out;
  box-shadow: 0 0 15px #764ba2;
}

p#profileurl {
  word-break: break-all;
}

/* Hele teema */
body.light-theme {
  background: linear-gradient(135deg, #f0f0f0 0%, #d1d1d1 100%);
  color: #222;
}

body.light-theme #app > div {
  background: rgba(255, 255, 255, 0.9);
  color: #222;
  box-shadow: 0 12px 36px rgba(0, 0, 0, 0.1);
}

body.light-theme input {
  background-color: #fff;
  color: #333;
  box-shadow: inset 0 0 12px rgba(0, 0, 0, 0.1);
}

body.light-theme input::placeholder {
  color: #888;
  font-style: normal;
}

body.light-theme input:focus {
  background-color: #f0f0f0;
  box-shadow: 0 0 12px #764ba2;
}

body.light-theme button {
  background: #764ba2;
  color: #fff;
  box-shadow: 0 4px 12px rgba(118, 75, 162, 0.3);
}

body.light-theme button:hover:not(:disabled) {
  background-color: #9f7aea;
  box-shadow: 0 6px 18px rgba(159, 122, 234, 0.5);
}

body.light-theme .theme-switch {
  color: #444;
}

body.light-theme .content a {
  color: #764ba2;
}

body.light-theme .content a:hover {
  color: #9f7aea;
}

/* Mobiilne kohandamine */
@media (max-width: 420px) {
  #app > div {
    padding: 25px 30px;
    width: 95vw;
  }

  h1 {
    font-size: 2rem;
  }

  #avatar {
    width: 100px;
    height: 100px;
    margin-bottom: 20px;
  }

  button {
    margin-left: 5px;
    padding: 8px 10px;
  }
}

Kokkuvõte:

  • renderPage() – loob ja uuendab veebilehe sisu
  • updateValue() – jälgib sisestusvälja muutusi
  • fetchProfile() – teostab API päringud ja töödeldab vastused

On loodud leht mis teeb päringud GitHubi ja näitab valiku infot (ID, repos number, followers,followings ja link Githubile)