diff --git a/codewars-badge.js b/codewars-badge.js index 7c26060..0623288 100644 --- a/codewars-badge.js +++ b/codewars-badge.js @@ -6,8 +6,9 @@ class CodeWarsBadge extends HTMLElement { constructor() { super(); this.attachShadow({ mode: "open" }); - this.userName = "CodeYourFuture"; + this.userName = "avatarit"; this.userData = []; + } connectedCallback() { @@ -30,22 +31,169 @@ class CodeWarsBadge extends HTMLElement { } render() { + const overall = this.userData?.ranks?.overall || {}; + const name = this.userData?.name || this.userName; + const clan = this.userData?.clan || "No Clan"; + const honor = this.userData?.honor ?? "n/a"; + const rankName = overall?.name || "n/a"; + const score = overall?.score ?? "n/a"; + const rankColor = overall?.color || "#"; + this.shadowRoot.innerHTML = ` - + + + Name: ${name} +
Rank: ${rankName}
+
Clan: ${clan}
+
Honor: ${honor}
+
Score: ${score}
+
+ `; + } + +} +class CodeWarsRecent extends HTMLElement { + static get observedAttributes() { return ['username', 'limit', 'page']; } + + constructor() { + super(); + this.attachShadow({ mode: 'open' }); + this.username = this.getAttribute('username') || 'CodeYourFuture'; + this.limit = Number(this.getAttribute('limit') || 5); + this.page = Number(this.getAttribute('page') || 0); + this.renderSkeleton(); + } + + connectedCallback() { + this.load(); + } + + attributeChangedCallback(name, _old, _val) { + if (!this.isConnected) return; + this.username = this.getAttribute('username') || this.username; + this.limit = Number(this.getAttribute('limit') || this.limit); + this.page = Number(this.getAttribute('page') || this.page); + this.renderSkeleton(); + this.load(); + } + + $(sel) { return this.shadowRoot.querySelector(sel); } + + renderSkeleton() { + this.shadowRoot.innerHTML = ` + - - ${this.userData.ranks.overall.name} - `; +
+
+ Loading recent katas for "${this.username}"… +
+ +
+ `; + } + + async load() { + const status = this.$('.status'); + const list = this.$('#list'); + list.innerHTML = ''; + + try { + const url = `https://www.codewars.com/api/v1/users/${encodeURIComponent(this.username)}/code-challenges/completed?page=${this.page}`; + const res = await fetch(url, { headers: { 'Accept': 'application/json' } }); + if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`); + + const data = await res.json(); + console.log('[codewars-recent] completed:', data); // helpful for debugging + + const items = (data.data || []).slice(0, this.limit); + if (!items.length) { + status.textContent = 'No completed katas found (yet).'; + return; + } + + const frag = document.createDocumentFragment(); + for (const item of items) { + const li = document.createElement('li'); + const id = item.id || item.slug || ''; + const kataUrl = id ? `https://www.codewars.com/kata/${id}` : '#'; + const title = item.name || item.slug || 'Untitled kata'; + const when = item.completedAt ? new Date(item.completedAt) : null; + const langs = item.completedLanguages || []; + + li.innerHTML = ` +
+ ${this.escape(title)} +
+
+ ${when ? `Completed ${when.toLocaleDateString()}` : 'Completed (date unknown)'} + ${langs.length ? ` • ${langs.map(l => `${this.escape(l)}`).join('')}` : ''} +
+ `; + frag.appendChild(li); + } + + list.appendChild(frag); + status.textContent = ''; + } catch (err) { + console.error('[codewars-recent] fetch error:', err); + status.classList.add('error'); + status.textContent = 'Could not load recent katas. Check the username or try again later.'; + } + } + + escape(str) { + return String(str) + .replaceAll('&','&') + .replaceAll('<','<') + .replaceAll('>','>') + .replaceAll('"','"') + .replaceAll("'",'''); } } +customElements.define('codewars-recent', CodeWarsRecent) + customElements.define("codewars-badge", CodeWarsBadge); diff --git a/index.html b/index.html index bbb3149..a3e39da 100644 --- a/index.html +++ b/index.html @@ -12,6 +12,13 @@ + +
+

Recently Completed

+ + +
+