Add templates/acheivments/achievement_sample1_input.html

This commit is contained in:
Ghassan Yusuf 2026-03-08 17:24:45 +03:00
parent 6a49256399
commit d54bd3fa1d

View File

@ -0,0 +1,726 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Create ETA Achievement Input</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
:root {
--bg: #050816;
--panel: rgba(15, 23, 42, 0.96);
--border: rgba(148, 163, 184, 0.65);
--accent: #ffb800;
--accent-soft: rgba(255, 184, 0, 0.16);
--text: #f9fafb;
--muted: #9ca3af;
--radius: 18px;
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: flex-start;
padding: 24px 12px 32px;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background: radial-gradient(circle at top, #111827 0, #050816 55%, #020308 100%);
color: var(--text);
}
.wrap {
width: 100%;
max-width: 1120px;
display: grid;
grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
gap: 18px;
}
@media (max-width: 960px) {
.wrap { grid-template-columns: minmax(0, 1fr); }
}
.panel {
background: var(--panel);
border-radius: var(--radius);
border: 1px solid var(--border);
box-shadow: 0 18px 48px rgba(15, 23, 42, 0.9);
padding: 14px 16px 16px;
}
.panel-header {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
margin-bottom: 10px;
}
.panel-title {
font-size: 0.82rem;
text-transform: uppercase;
letter-spacing: 0.18em;
color: var(--muted);
}
.panel-subtitle {
font-size: 1.08rem;
font-weight: 600;
}
.tag {
font-size: 0.75rem;
padding: 3px 9px;
border-radius: 999px;
border: 1px solid var(--border);
text-transform: uppercase;
letter-spacing: 0.15em;
color: var(--muted);
}
form {
display: flex;
flex-direction: column;
gap: 10px;
font-size: 0.86rem;
}
.field-group { display: flex; flex-direction: column; gap: 4px; }
.field-row {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
}
@media (max-width: 640px) {
.field-row { grid-template-columns: minmax(0, 1fr); }
}
label.field-label {
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.16em;
color: var(--muted);
display: flex;
justify-content: space-between;
align-items: center;
}
.field-label span.hint {
font-size: 0.7rem;
color: var(--muted);
text-transform: none;
letter-spacing: normal;
}
.input,
.textarea {
width: 100%;
border-radius: 999px;
border: 1px solid rgba(55, 65, 81, 1);
background: rgba(15, 23, 42, 0.96);
color: var(--text);
padding: 7px 11px;
font-size: 0.86rem;
outline: none;
transition: border-color 0.14s ease-out, box-shadow 0.14s ease-out, background 0.14s ease-out;
}
.textarea {
border-radius: 14px;
min-height: 80px;
resize: vertical;
line-height: 1.4;
}
.input:focus,
.textarea:focus {
border-color: var(--accent);
box-shadow: 0 0 0 1px rgba(255, 184, 0, 0.45);
background: rgba(15, 23, 42, 0.99);
}
.input::placeholder,
.textarea::placeholder {
color: rgba(148, 163, 184, 0.7);
}
.pill-row { display: flex; flex-wrap: wrap; gap: 6px; }
.pill {
font-size: 0.74rem;
padding: 4px 9px;
border-radius: 999px;
border: 1px solid rgba(55, 65, 81, 1);
background: rgba(15, 23, 42, 0.96);
color: var(--muted);
cursor: pointer;
}
.pill.is-active {
border-color: var(--accent);
background: var(--accent-soft);
color: var(--accent);
}
.medal-row {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
}
.medal-input {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 9px;
border-radius: 14px;
border: 1px solid rgba(55, 65, 81, 1);
background: rgba(15, 23, 42, 0.98);
font-size: 0.8rem;
}
.medal-input span.badge {
width: 20px;
height: 20px;
border-radius: 999px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 0.78rem;
}
.medal-gold span.badge {
background: linear-gradient(135deg, #facc15, #f97316);
color: #111827;
}
.medal-silver span.badge {
background: linear-gradient(135deg, #e5e7eb, #9ca3af);
color: #020617;
}
.medal-bronze span.badge {
background: linear-gradient(135deg, #f97316, #b45309);
color: #020617;
}
.medal-input input {
width: 100%;
border: none;
outline: none;
background: transparent;
color: var(--text);
font-size: 0.8rem;
}
.medal-input input::-webkit-outer-spin-button,
.medal-input input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.medal-input input[type="number"] { -moz-appearance: textfield; }
.chips-input {
display: flex;
flex-wrap: wrap;
gap: 6px;
padding: 5px 6px;
border-radius: 16px;
border: 1px dashed rgba(75, 85, 99, 1);
background: rgba(15, 23, 42, 0.8);
}
.chip-token {
font-size: 0.74rem;
padding: 3px 8px;
border-radius: 999px;
border: 1px solid rgba(148, 163, 184, 0.8);
background: rgba(15, 23, 42, 0.96);
}
.chip-input-field {
border: none;
outline: none;
background: transparent;
color: var(--text);
font-size: 0.82rem;
padding: 3px 4px;
min-width: 80px;
flex: 1;
}
.athletes-list { display: flex; flex-direction: column; gap: 6px; }
.athlete-row {
display: grid;
grid-template-columns: 0.35fr 0.65fr;
gap: 6px;
}
.athlete-row .input { border-radius: 12px; padding: 6px 9px; }
.field-note {
font-size: 0.75rem;
color: var(--muted);
}
.footer-row {
display: flex;
justify-content: space-between;
align-items: center;
gap: 10px;
margin-top: 4px;
}
.btn-row { display: flex; align-items: center; gap: 8px; }
.btn {
border-radius: 999px;
font-size: 0.82rem;
padding: 7px 14px;
border: 1px solid var(--border);
background: rgba(15, 23, 42, 0.96);
color: var(--text);
cursor: pointer;
}
.btn-primary {
background: linear-gradient(135deg, var(--accent), #fb923c);
border-color: transparent;
color: #111827;
font-weight: 600;
}
.preview-card {
position: relative;
border-radius: 18px;
padding: 12px 14px;
background: radial-gradient(circle at top left, #1e293b, #020617 55%);
border: 1px solid rgba(148, 163, 184, 0.4);
min-height: 120px;
overflow: hidden;
}
.preview-header {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 8px;
margin-bottom: 4px;
font-size: 0.8rem;
}
.preview-eyebrow {
text-transform: uppercase;
letter-spacing: 0.18em;
color: var(--muted);
font-size: 0.72rem;
}
.preview-medals {
font-size: 0.78rem;
padding: 2px 10px;
border-radius: 999px;
border: 1px solid rgba(250, 204, 21, 0.9);
background: var(--accent-soft);
color: var(--accent);
}
.preview-title {
margin: 0 0 3px;
font-size: 1rem;
font-weight: 600;
}
.preview-meta {
margin: 0 0 8px;
font-size: 0.78rem;
color: var(--muted);
}
.preview-footer {
display: flex;
justify-content: space-between;
align-items: center;
gap: 10px;
font-size: 0.76rem;
}
.preview-avatars { display: flex; }
.preview-avatar {
width: 22px;
height: 22px;
border-radius: 999px;
border: 2px solid #020617;
background: linear-gradient(135deg, #f97316, #ec4899);
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 0.7rem;
font-weight: 700;
color: #020617;
margin-left: -7px;
}
.preview-avatar:first-child { margin-left: 0; }
.preview-cta {
display: inline-flex;
align-items: center;
gap: 4px;
color: var(--accent);
}
</style>
</head>
<body>
<div class="wrap">
<section class="panel">
<header class="panel-header">
<div>
<div class="panel-title">Achievement input</div>
<div class="panel-subtitle">Define card + detail data</div>
</div>
<span class="tag">JSON Output</span>
</header>
<form id="achievement-form">
<div class="field-group">
<label class="field-label">
ID / slug
<span class="hint">Used in data-achievement-id</span>
</label>
<input class="input" id="field-id" placeholder="thailand-2023" required />
</div>
<div class="field-group">
<label class="field-label">
Title
<span class="hint">Detail title</span>
</label>
<input class="input" id="field-title" placeholder="Thailand Open Championships 2023" required />
</div>
<div class="field-row">
<div class="field-group">
<label class="field-label">
Short title
<span class="hint">Card title</span>
</label>
<input class="input" id="field-short-title" placeholder="Thailand Open Championships" />
</div>
<div class="field-group">
<label class="field-label">
Type icon
<span class="hint">🏆 / 🥋 / ⭐</span>
</label>
<input class="input" id="field-type-icon" placeholder="🏆" maxlength="4" />
</div>
</div>
<div class="field-row">
<div class="field-group">
<label class="field-label">
Location
<span class="hint">City, Country</span>
</label>
<input class="input" id="field-location" placeholder="Bangkok, Thailand" required />
</div>
<div class="field-group">
<label class="field-label">
Date label
<span class="hint">Card label</span>
</label>
<input class="input" id="field-date-label" placeholder="Nov 2023" required />
</div>
</div>
<div class="field-group">
<label class="field-label">
Full date
<span class="hint">Detail header</span>
</label>
<input class="input" id="field-date-full" placeholder="November 2023" />
</div>
<div class="field-group">
<label class="field-label">
Image URL
<span class="hint">Card + detail cover</span>
</label>
<input class="input" id="field-image" placeholder="https://images.pexels.com/..." />
</div>
<div class="field-group">
<label class="field-label">
Medals
<span class="hint">Numbers only</span>
</label>
<div class="medal-row">
<div class="medal-input medal-gold">
<span class="badge">🥇</span>
<input type="number" min="0" id="field-gold" placeholder="1" />
</div>
<div class="medal-input medal-silver">
<span class="badge">🥈</span>
<input type="number" min="0" id="field-silver" placeholder="3" />
</div>
<div class="medal-input medal-bronze">
<span class="badge">🥉</span>
<input type="number" min="0" id="field-bronze" placeholder="0" />
</div>
</div>
</div>
<div class="field-row">
<div class="field-group">
<label class="field-label">
Total bouts
</label>
<input type="number" min="0" class="input" id="field-bouts" placeholder="6" />
</div>
<div class="field-group">
<label class="field-label">
Wins
</label>
<input type="number" min="0" class="input" id="field-wins" placeholder="4" />
</div>
</div>
<div class="field-group">
<label class="field-label">
Category
<span class="hint">Detail meta</span>
</label>
<input class="input" id="field-category" placeholder="World Taekwondo G2 Event" />
</div>
<div class="field-group">
<label class="field-label">
Story
<span class="hint">Detail description</span>
</label>
<textarea class="textarea" id="field-story" placeholder="Under the leadership of Grand Master Sami Al Manai, ..."></textarea>
</div>
<div class="field-group">
<label class="field-label">
Chips
<span class="hint">Enter + press comma or Enter</span>
</label>
<div class="chips-input" id="chips-wrapper">
<input class="chip-input-field" id="chip-input" placeholder="World Ranking Points" />
</div>
</div>
<div class="field-group">
<label class="field-label">
Athletes
<span class="hint">Used for avatars + detail list</span>
</label>
<div class="athletes-list" id="athletes-list">
<div class="athlete-row">
<input class="input" id="ath-name-1" placeholder="Hanan" />
<input class="input" id="ath-role-1" placeholder="Senior Poomsae • Gold" />
</div>
<div class="athlete-row">
<input class="input" id="ath-name-2" placeholder="Asmahan" />
<input class="input" id="ath-role-2" placeholder="Kyorugi -49kg • Silver" />
</div>
</div>
<div class="footer-row">
<span class="field-note">Add up to 4 athletes; initials appear on the card.</span>
<div class="btn-row">
<button type="button" class="btn" id="btn-add-athlete">+ Athlete</button>
<button type="button" class="btn" id="btn-clear-athletes">Clear</button>
</div>
</div>
</div>
<div class="footer-row">
<span class="field-note">Submit to send this payload to your backend.</span>
<div class="btn-row">
<button type="reset" class="btn">Reset</button>
<button type="submit" class="btn btn-primary">Generate payload</button>
</div>
</div>
</form>
</section>
<section class="panel">
<header class="panel-header">
<div>
<div class="panel-title">Card preview</div>
<div class="panel-subtitle">How it will render</div>
</div>
<span class="tag">Read-only</span>
</header>
<article class="preview-card">
<div class="preview-header">
<span class="preview-eyebrow" id="preview-type">International</span>
<span class="preview-medals" id="preview-medals">🥇 0 • 🥈 0 • 🥉 0</span>
</div>
<h3 class="preview-title" id="preview-title">Achievement title</h3>
<p class="preview-meta" id="preview-meta">Location • Date label</p>
<div class="preview-footer">
<div class="preview-avatars" id="preview-avatars"></div>
<div class="preview-cta">
<span>Tap to view story</span>
<span></span>
</div>
</div>
</article>
<pre id="payload-output" style="margin-top:12px;font-size:0.75rem;background:#020617;border-radius:12px;border:1px solid rgba(55,65,81,1);padding:8px 10px;white-space:pre-wrap;word-break:break-word;"></pre>
</section>
</div>
<script>
const titleEl = document.getElementById("field-title");
const shortTitleEl = document.getElementById("field-short-title");
const locationEl = document.getElementById("field-location");
const dateLabelEl = document.getElementById("field-date-label");
const goldEl = document.getElementById("field-gold");
const silverEl = document.getElementById("field-silver");
const bronzeEl = document.getElementById("field-bronze");
const athletesList = document.getElementById("athletes-list");
const addAthleteBtn = document.getElementById("btn-add-athlete");
const clearAthletesBtn = document.getElementById("btn-clear-athletes");
const chipWrapper = document.getElementById("chips-wrapper");
const chipInput = document.getElementById("chip-input");
const previewType = document.getElementById("preview-type");
const previewMedals = document.getElementById("preview-medals");
const previewTitle = document.getElementById("preview-title");
const previewMeta = document.getElementById("preview-meta");
const previewAvatars = document.getElementById("preview-avatars");
const payloadOutput = document.getElementById("payload-output");
function updatePreviewText() {
const title = titleEl.value.trim() || "Achievement title";
const shortTitle = shortTitleEl.value.trim() || title;
const loc = locationEl.value.trim() || "Location";
const dateLabel = dateLabelEl.value.trim() || "Date";
previewTitle.textContent = shortTitle;
previewMeta.textContent = loc + " • " + dateLabel;
}
function updatePreviewMedals() {
const g = Number(goldEl.value || 0);
const s = Number(silverEl.value || 0);
const b = Number(bronzeEl.value || 0);
previewMedals.textContent = `🥇 ${g} • 🥈 ${s} • 🥉 ${b}`;
}
function getAthletes() {
const rows = athletesList.querySelectorAll(".athlete-row");
const result = [];
rows.forEach((row) => {
const n = row.querySelector("input[id^='ath-name']").value.trim();
const r = row.querySelector("input[id^='ath-role']").value.trim();
if (n || r) result.push({ name: n, role: r });
});
return result.slice(0, 4);
}
function initials(name) {
if (!name) return "?";
const parts = name.trim().split(/\s+/);
if (parts.length === 1) return parts[0][0]?.toUpperCase() || "?";
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
}
function updatePreviewAvatars() {
const athletes = getAthletes();
previewAvatars.innerHTML = "";
athletes.forEach((a) => {
const span = document.createElement("span");
span.className = "preview-avatar";
span.textContent = initials(a.name);
previewAvatars.appendChild(span);
});
}
[titleEl, shortTitleEl, locationEl, dateLabelEl].forEach((el) =>
el.addEventListener("input", updatePreviewText)
);
[goldEl, silverEl, bronzeEl].forEach((el) =>
el.addEventListener("input", updatePreviewMedals)
);
function wireAthleteInputs() {
const nameInputs = athletesList.querySelectorAll("input[id^='ath-name']");
nameInputs.forEach((input) => {
input.removeEventListener("input", updatePreviewAvatars);
input.addEventListener("input", updatePreviewAvatars);
});
}
addAthleteBtn.addEventListener("click", () => {
const rows = athletesList.querySelectorAll(".athlete-row").length;
if (rows >= 4) return;
const idx = rows + 1;
const row = document.createElement("div");
row.className = "athlete-row";
row.innerHTML = `
<input class="input" id="ath-name-${idx}" placeholder="Name" />
<input class="input" id="ath-role-${idx}" placeholder="Role • Result" />
`;
athletesList.appendChild(row);
wireAthleteInputs();
updatePreviewAvatars();
});
clearAthletesBtn.addEventListener("click", () => {
athletesList.innerHTML = "";
for (let i = 1; i <= 2; i++) {
const row = document.createElement("div");
row.className = "athlete-row";
row.innerHTML = `
<input class="input" id="ath-name-${i}" placeholder="Name" />
<input class="input" id="ath-role-${i}" placeholder="Role • Result" />
`;
athletesList.appendChild(row);
}
wireAthleteInputs();
updatePreviewAvatars();
});
wireAthleteInputs();
updatePreviewText();
updatePreviewMedals();
updatePreviewAvatars();
function readChips() {
const chips = [];
chipWrapper.querySelectorAll(".chip-token").forEach((token) => {
chips.push(token.dataset.value);
});
const pending = chipInput.value.trim();
if (pending) chips.push(pending);
return chips;
}
function addChip(value) {
const v = value.trim();
if (!v) return;
const token = document.createElement("span");
token.className = "chip-token";
token.dataset.value = v;
token.textContent = v;
chipWrapper.insertBefore(token, chipInput);
}
chipInput.addEventListener("keydown", (e) => {
if (e.key === "Enter" || e.key === ",") {
e.preventDefault();
addChip(chipInput.value);
chipInput.value = "";
} else if (e.key === "Backspace" && !chipInput.value) {
const last = chipWrapper.querySelector(".chip-token:last-of-type");
if (last) chipWrapper.removeChild(last);
}
});
const form = document.getElementById("achievement-form");
form.addEventListener("submit", (e) => {
e.preventDefault();
const id = document.getElementById("field-id").value.trim();
const title = titleEl.value.trim();
const shortTitle = shortTitleEl.value.trim() || title;
const loc = locationEl.value.trim();
const dateLabel = dateLabelEl.value.trim();
const dateFull = document.getElementById("field-date-full").value.trim() || dateLabel;
const image = document.getElementById("field-image").value.trim();
const typeIcon = document.getElementById("field-type-icon").value.trim() || "🏆";
const bouts = Number(document.getElementById("field-bouts").value || 0);
const wins = Number(document.getElementById("field-wins").value || 0);
const category = document.getElementById("field-category").value.trim();
const story = document.getElementById("field-story").value.trim();
const g = Number(goldEl.value || 0);
const s = Number(silverEl.value || 0);
const b = Number(bronzeEl.value || 0);
const medalsText = `${g} Gold • ${s} Silver${b ? ` • ${b} Bronze` : ""}`;
const athletes = getAthletes();
const chips = readChips();
const payload = {
id: id,
title: title,
short_title: shortTitle,
location: loc,
date_label: dateLabel,
date_full: dateFull,
image_url: image,
medal_summary: medalsText,
type_icon: typeIcon,
athletes_count: athletes.length,
bouts_count: bouts,
bouts_label: bouts && wins ? `${bouts} Bouts • ${wins} Wins` : "",
category: category,
story: story,
chips: chips,
athletes: athletes
};
payloadOutput.textContent = JSON.stringify(payload, null, 2);
});
</script>
</body>
</html>