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 = `
+
+
+ ${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
+
+
+
+