🎮ArcadeLab

Loja e Palëve të Interesuara

by PixelPirate96
701 lines17.1 KB
▶ Play
<!DOCTYPE html>
<html lang="sq">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Loja e Palëve të Interesuara</title>
<style>
  * { box-sizing: border-box; }
  body {
    margin: 0;
    min-height: 100vh;
    background: #f5f5f6;
    color: #303039;
    font-family: Arial, Helvetica, sans-serif;
    font-size: 13px;
  }

  .app {
    width: min(1000px, 96vw);
    margin: 16px auto;
  }

  .game-card {
    background: #fff;
    border-radius: 18px;
    box-shadow: 0 7px 24px rgba(50, 35, 70, .08);
    padding: 16px;
  }

  .top {
    display: grid;
    grid-template-columns: 205px 1fr;
    gap: 14px;
    align-items: stretch;
  }

  .stakeholders {
    background: #fff;
    border-radius: 16px;
    padding: 12px 14px;
  }

  .heading {
    color: #7d3fa0;
    font-size: 10px;
    font-weight: 800;
    letter-spacing: 1.3px;
    margin-bottom: 9px;
    padding-bottom: 8px;
    border-bottom: 1px solid #eadcf0;
  }

  .stakeholder-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
  }

  .stakeholder {
    min-height: 46px;
    border-radius: 10px;
    background: #f7f7f8;
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 7px 10px;
    cursor: grab;
    user-select: none;
    border: 1px solid transparent;
    transition: .15s ease;
  }

  .stakeholder:hover { transform: translateY(-1px); }
  .stakeholder.selected {
    border-color: #9a66ae;
    box-shadow: 0 0 0 2px rgba(154,102,174,.12);
  }
  .stakeholder.placed { opacity: .56; cursor: default; }

  .icon {
    width: 22px;
    text-align: center;
    font-size: 17px;
    flex: 0 0 22px;
  }

  .stakeholder-name {
    flex: 1;
    color: #77777d;
    font-size: 12px;
    line-height: 1.15;
  }

  .tick {
    font-size: 17px;
    color: #999;
  }

  .placed-count {
    margin-top: 9px;
    color: #aaa;
    font-size: 11px;
    display: flex;
    align-items: center;
    gap: 3px;
  }
  .dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: #82429f;
    display: inline-block;
  }

  .matrix-wrap {
    position: relative;
    padding: 15px 0 22px 35px;
  }

  .axis-top {
    position: absolute;
    left: 35px;
    right: 0;
    top: 0;
    display: flex;
    justify-content: space-between;
    color: #999;
    font-size: 9px;
    letter-spacing: .6px;
  }

  .axis-top .arrow {
    flex: 1;
    margin: 0 9px;
    height: 1px;
    margin-top: 6px;
    background: linear-gradient(to right, #c5a6dc 0 48%, transparent 48% 52%, #c5a6dc 52%);
    position: relative;
  }
  .axis-top .arrow:after {
    content: "→";
    position: absolute;
    left: 50%;
    top: -8px;
    color: #b99acb;
  }

  .axis-left {
    position: absolute;
    left: 7px;
    top: 47px;
    bottom: 28px;
    width: 12px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    align-items: center;
    color: #999;
    font-size: 9px;
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    letter-spacing: .5px;
  }

  .matrix {
    height: 396px;
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: 1fr 1fr;
  }

  .quadrant {
    position: relative;
    padding: 12px 10px;
    border: 2px dashed;
    overflow: hidden;
  }
  .quadrant:nth-child(1) {
    border-color: #94bfff;
    border-radius: 14px 0 0 0;
  }
  .quadrant:nth-child(2) {
    border-color: #91d89a;
    border-radius: 0 14px 0 0;
  }
  .quadrant:nth-child(3) {
    border-color: #d0d0d0;
    border-radius: 0 0 0 14px;
  }
  .quadrant:nth-child(4) {
    border-color: #f1ae53;
    border-radius: 0 0 14px 0;
  }

  .q-title {
    font-weight: 800;
    font-size: 13px;
    margin-bottom: 2px;
  }
  .q-subtitle {
    font-size: 9px;
    color: #aaa;
    margin-bottom: 14px;
  }
  .blue { color: #3e83e9; }
  .green { color: #4d9b55; }
  .gray { color: #777; }
  .orange { color: #ed971d; }

  .placed-items {
    display: flex;
    flex-wrap: wrap;
    gap: 7px;
    align-content: flex-start;
  }

  .pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border: 1px solid #ff3c39;
    color: #29292f;
    background: #fff;
    border-radius: 18px;
    padding: 7px 10px;
    font-size: 10px;
    white-space: nowrap;
  }

  .pill .score {
    color: #e42b2b;
    font-weight: 700;
  }

  .recommend {
    border: 1px dashed #48a85a;
    color: #48a85a;
    background: #f9fff9;
    border-radius: 18px;
    padding: 7px 10px;
    font-size: 9px;
    font-weight: 700;
    cursor: default;
  }

  .legend {
    position: absolute;
    left: 35px;
    bottom: 0;
    color: #777;
    font-size: 9px;
    display: flex;
    gap: 10px;
  }
  .legend span { display: inline-flex; align-items: center; gap: 4px; }
  .legend i {
    width: 9px; height: 9px; border-radius: 50%; display: inline-block;
  }

  .buttons {
    display: flex;
    gap: 10px;
    margin-top: 14px;
    margin-left: 219px;
  }

  button {
    border: 0;
    font: inherit;
    cursor: pointer;
  }

  #revealBtn {
    flex: 1;
    height: 44px;
    border-radius: 11px;
    background: #d7c2eb;
    color: #fff;
    font-weight: 800;
    font-size: 15px;
  }
  #revealBtn:not(:disabled):hover { background: #c8ace1; }
  #revealBtn:disabled { cursor: default; opacity: .8; }

  #resetBtn {
    width: 92px;
    height: 44px;
    border-radius: 11px;
    background: #fff;
    color: #7d3fa0;
    border: 1px solid #7d3fa0;
    font-weight: 700;
  }

  .results {
    margin-top: 18px;
    background: #fff;
    border-radius: 18px;
    box-shadow: 0 7px 24px rgba(50, 35, 70, .08);
    padding: 16px 18px 18px;
    display: none;
  }
  .results.show { display: block; }

  .results-title {
    color: #7d3fa0;
    font-size: 10px;
    font-weight: 800;
    letter-spacing: 1.2px;
    margin-bottom: 12px;
  }

  .reason-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px 10px;
  }

  .reason {
    border-radius: 11px;
    padding: 12px 13px;
    min-height: 105px;
  }
  .reason:nth-child(1),
  .reason:nth-child(2),
  .reason:nth-child(3),
  .reason:nth-child(7),
  .reason:nth-child(8) { background: #fff9f0; }
  .reason:nth-child(4),
  .reason:nth-child(5),
  .reason:nth-child(6) { background: #f1faf2; }

  .reason h3 {
    margin: 0 0 4px;
    font-size: 12px;
    color: #2d2b30;
  }
  .placement {
    font-weight: 800;
    font-size: 10px;
    margin-bottom: 7px;
  }
  .reason p {
    margin: 0;
    color: #666;
    font-size: 10px;
    line-height: 1.42;
  }

  .toast {
    position: fixed;
    left: 50%;
    bottom: 18px;
    transform: translateX(-50%) translateY(20px);
    background: #35263d;
    color: #fff;
    padding: 9px 14px;
    border-radius: 20px;
    opacity: 0;
    pointer-events: none;
    transition: .2s;
    font-size: 11px;
  }
  .toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

  @media (max-width: 800px) {
    .top { grid-template-columns: 1fr; }
    .buttons { margin-left: 0; }
    .matrix-wrap { padding-left: 28px; }
    .axis-top { left: 28px; }
    .legend { left: 28px; }
    .matrix { height: 460px; }
    .reason-grid { grid-template-columns: 1fr; }
  }

  @media (max-width: 520px) {
    .matrix { height: 540px; grid-template-columns: 1fr; grid-template-rows: repeat(4, 1fr); }
    .quadrant { border-radius: 12px !important; }
    .buttons { flex-direction: column; }
    #resetBtn { width: 100%; }
    .axis-top { display: none; }
  }
</style>
</head>
<body>
<div class="app">

  <div class="game-card">
    <div class="top">

      <aside class="stakeholders">
        <div class="heading">PALËT E INTERESUARA PËR T'U VENDOSUR</div>
        <div id="stakeholderList" class="stakeholder-list"></div>
        <div class="placed-count">Të vendosura:
          <span id="dots"></span>
        </div>
      </aside>

      <main class="matrix-wrap">
        <div class="axis-top">
          <span>INTERES I ULËT</span>
          <span class="arrow"></span>
          <span>INTERES I LARTË</span>
        </div>

        <div class="axis-left">
          <span>FUQI E ULËT</span>
          <span>FUQI E LARTË</span>
        </div>

        <div class="matrix">
          <section class="quadrant" data-zone="keepSatisfied">
            <div class="q-title blue">Mbaji të kënaqur</div>
            <div class="q-subtitle">Fuqi e lartë · Interes i ulët</div>
            <div class="placed-items" id="keepSatisfied"></div>
          </section>

          <section class="quadrant" data-zone="manageClosely">
            <div class="q-title green">Menaxho nga afër</div>
            <div class="q-subtitle">Fuqi e lartë · Interes i lartë</div>
            <div class="placed-items" id="manageClosely"></div>
          </section>

          <section class="quadrant" data-zone="monitor">
            <div class="q-title gray">Monitoro</div>
            <div class="q-subtitle">Fuqi e ulët · Interes i ulët</div>
            <div class="placed-items" id="monitor"></div>
          </section>

          <section class="quadrant" data-zone="keepInformed">
            <div class="q-title orange">Mbaji të informuar</div>
            <div class="q-subtitle">Fuqi e ulët · Interes i lartë</div>
            <div class="placed-items" id="keepInformed"></div>
          </section>
        </div>

        <div class="legend">
          <span><i style="background:#4384e6"></i> Mbaji të kënaqur</span>
          <span><i style="background:#51a25b"></i> Menaxho nga afër</span>
          <span><i style="background:#999"></i> Monitoro</span>
          <span><i style="background:#ef9b27"></i> Mbaji të informuar</span>
        </div>
      </main>
    </div>

    <div class="buttons">
      <button id="revealBtn">✓ Shfaq arsyetimin</button>
      <button id="resetBtn">↻ Rivendos</button>
    </div>
  </div>

  <section id="results" class="results">
    <div class="results-title">VENDOSJET E REKOMANDUARA DHE ARSYETIMI</div>
    <div class="reason-grid" id="reasonGrid"></div>
  </section>

</div>

<div id="toast" class="toast"></div>

<script>
const stakeholders = [
  {
    id: "principal",
    icon: "🏫",
    name: "Drejtori i shkollës",
    zone: "manageClosely",
    placement: "→ Menaxho nga afër",
    reason: "Autoriteti më i lartë në terren — mund ta miratojë ose ta ndalojë projektin. Ndikon drejtpërdrejt në reputacionin e shkollës dhe mirëqenien e nxënësve."
  },
  {
    id: "funder",
    icon: "💶",
    name: "Financuesi / Sponsori i projektit",
    zone: "manageClosely",
    placement: "→ Menaxho nga afër",
    reason: "Kontrollon buxhetin. Pa mbështetje të vazhdueshme financiare, projekti nuk mund të vazhdojë — duhet menaxhuar nga afër në çdo fazë."
  },
  {
    id: "permit",
    icon: "🏛️",
    name: "Autoriteti bashkiak i lejeve",
    zone: "keepSatisfied",
    placement: "→ Mbaji të kënaqur",
    reason: "Ka fuqinë ligjore për të bllokuar ose miratuar ndërtimin, por nuk ka interes të përditshëm në çështjet e shkollës. Mbajini të kënaqur përmes pajtueshmërisë."
  },
  {
    id: "maintenance",
    icon: "🔧",
    name: "Departamenti i mirëmbajtjes",
    zone: "keepInformed",
    placement: "✓ Mbaji të informuar",
    reason: "Do të jetë përgjegjës për mirëmbajtjen afatgjatë të fushës dhe ka interes të lartë, por fuqi të kufizuar mbi vendimin strategjik për ndërtimin."
  },
  {
    id: "students",
    icon: "🧑‍🎓",
    name: "Nxënësit",
    zone: "keepInformed",
    placement: "→ Mbaji të informuar",
    reason: "Përdoruesit kryesorë dhe pala më e interesuar, por kanë pak fuqi formale institucionale mbi vendimet për infrastrukturën."
  },
  {
    id: "parents",
    icon: "👨‍👩‍👧",
    name: "Prindërit",
    zone: "keepInformed",
    placement: "✓ Mbaji të informuar",
    reason: "Kanë interes të lartë si kujdestarë të përdoruesve kryesorë; megjithatë, kanë fuqi formale të kufizuar mbi vendimet për infrastrukturën e shkollës."
  },
  {
    id: "public",
    icon: "🤝",
    name: "Publiku i gjerë (larg)",
    zone: "monitor",
    placement: "→ Monitoro",
    reason: "Nuk ka gjasa të ndikohet drejtpërdrejt nga një fushë sportive e shkollës në anën tjetër të qytetit. Interes i ulët dhe pa fuqi domethënëse — vetëm mbajeni nën vëzhgim."
  },
  {
    id: "business",
    icon: "🏪",
    name: "Biznes lokal i palidhur",
    zone: "monitor",
    placement: "→ Monitoro",
    reason: "Nuk ka interes të drejtpërdrejtë në projekt. Vetëm mbajeni nën vëzhgim në rast se më vonë shfaqen ndikime të tërthorta, si zhurma ose trafiku."
  }
];

const state = {
  placements: {},
  selected: null
};

const list = document.getElementById("stakeholderList");
const dots = document.getElementById("dots");
const revealBtn = document.getElementById("revealBtn");
const resetBtn = document.getElementById("resetBtn");
const results = document.getElementById("results");
const reasonGrid = document.getElementById("reasonGrid");
const toast = document.getElementById("toast");

function renderList() {
  list.innerHTML = "";
  stakeholders.forEach(s => {
    const el = document.createElement("div");
    el.className = "stakeholder" +
      (state.placements[s.id] ? " placed" : "") +
      (state.selected === s.id ? " selected" : "");
    el.dataset.id = s.id;
    el.draggable = !state.placements[s.id];

    el.innerHTML = `
      <span class="icon">${s.icon}</span>
      <span class="stakeholder-name">${s.name}</span>
      <span class="tick">${state.placements[s.id] ? "✓" : "✓"}</span>
    `;

    el.addEventListener("click", () => {
      if (state.placements[s.id]) return;
      state.selected = state.selected === s.id ? null : s.id;
      renderList();
      showToast(state.selected ? "Tani zgjidh kuadrantin ku dëshiron ta vendosësh." : "");
    });

    el.addEventListener("dragstart", e => {
      if (state.placements[s.id]) return;
      state.selected = s.id;
      e.dataTransfer.setData("text/plain", s.id);
    });

    list.appendChild(el);
  });

  renderDots();
}

function renderDots() {
  dots.innerHTML = "";
  const count = Object.keys(state.placements).length;
  for (let i = 0; i < count; i++) {
    const d = document.createElement("i");
    d.className = "dot";
    dots.appendChild(d);
  }
  for (let i = count; i < stakeholders.length; i++) {
    const d = document.createElement("i");
    d.className = "dot";
    d.style.opacity = ".18";
    dots.appendChild(d);
  }
}

function addPill(s, zone) {
  const container = document.getElementById(zone);
  const pill = document.createElement("div");
  pill.className = "pill";
  pill.dataset.id = s.id;
  pill.innerHTML = `${s.icon} ${s.name} <span class="score">${zone === s.zone ? "✓" : "✕"}</span>`;
  container.appendChild(pill);

  if (zone === s.zone) {
    const rec = document.createElement("div");
    rec.className = "recommend";
    rec.textContent = "✓ Rekomandohet";
    container.appendChild(rec);
  }
}

function place(id, zone) {
  const s = stakeholders.find(x => x.id === id);
  if (!s || state.placements[id]) return;

  state.placements[id] = zone;
  state.selected = null;
  addPill(s, zone);
  renderList();

  if (zone === s.zone) {
    showToast("Vendosje e saktë!");
  } else {
    showToast("Vendosje e gabuar — provo një kuadrant tjetër.");
  }

  if (Object.keys(state.placements).length === stakeholders.length) {
    revealBtn.disabled = false;
  }
}

document.querySelectorAll(".quadrant").forEach(q => {
  q.addEventListener("dragover", e => {
    e.preventDefault();
    q.style.background = "rgba(125,63,160,.04)";
  });
  q.addEventListener("dragleave", () => q.style.background = "");
  q.addEventListener("drop", e => {
    e.preventDefault();
    q.style.background = "";
    const id = e.dataTransfer.getData("text/plain");
    place(id, q.dataset.zone);
  });
  q.addEventListener("click", () => {
    if (state.selected) place(state.selected, q.dataset.zone);
  });
});

function reveal() {
  if (Object.keys(state.placements).length < stakeholders.length) {
    showToast("Vendos të gjitha palët e interesuara fillimisht.");
    return;
  }

  reasonGrid.innerHTML = "";
  stakeholders.forEach(s => {
    const correct = state.placements[s.id] === s.zone;
    const card = document.createElement("article");
    card.className = "reason";
    card.innerHTML = `
      <h3>${s.icon} ${s.name}</h3>
      <div class="placement" style="color:${correct ? "#4d9b55" : "#e89a24"}">
        ${correct ? "✓ " : "✕ "}${s.placement}
      </div>
      <p>${s.reason}</p>
    `;
    reasonGrid.appendChild(card);
  });

  results.classList.add("show");
  results.scrollIntoView({ behavior: "smooth", block: "start" });
}

function resetGame() {
  state.placements = {};
  state.selected = null;
  results.classList.remove("show");
  revealBtn.disabled = false;
  ["keepSatisfied","manageClosely","monitor","keepInformed"].forEach(id => {
    document.getElementById(id).innerHTML = "";
  });
  renderList();
  window.scrollTo({top: 0, behavior: "smooth"});
}

function showToast(text) {
  if (!text) {
    toast.classList.remove("show");
    return;
  }
  toast.textContent = text;
  toast.classList.add("show");
  clearTimeout(showToast.timer);
  showToast.timer = setTimeout(() => toast.classList.remove("show"), 1700);
}

revealBtn.addEventListener("click", reveal);
resetBtn.addEventListener("click", resetGame);

renderList();
</script>
</body>
</html>

Game Source: Loja e Palëve të Interesuara

Creator: PixelPirate96

Libraries: none

Complexity: complex (701 lines, 17.1 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: loja-e-pal-ve-t-interesuara-pixelpirate96" to link back to the original. Then publish at arcadelab.ai/publish.