Merge branch 'dev'
CI/CD / deploy (push) Has been cancelled
Details
CI/CD / deploy (push) Has been cancelled
Details
This commit is contained in:
commit
c4bc26359c
|
|
@ -38,4 +38,5 @@ export const MODULE_META: Record<string, ModuleDisplayMeta> = {
|
||||||
rsocials: { badge: "rSo", color: "#f9a8d4", name: "rSocials", icon: "📱" },
|
rsocials: { badge: "rSo", color: "#f9a8d4", name: "rSocials", icon: "📱" },
|
||||||
rdesign: { badge: "rDe", color: "#7c3aed", name: "rDesign", icon: "🎨" },
|
rdesign: { badge: "rDe", color: "#7c3aed", name: "rDesign", icon: "🎨" },
|
||||||
rtime: { badge: "rTi", color: "#a78bfa", name: "rTime", icon: "⏳" },
|
rtime: { badge: "rTi", color: "#a78bfa", name: "rTime", icon: "⏳" },
|
||||||
|
rauctions: { badge: "rA", color: "#fca5a5", name: "rAuctions", icon: "🏛" },
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
/**
|
||||||
|
* <folk-auctions-hub> — Displays active auctions from the rauctions.online API.
|
||||||
|
*
|
||||||
|
* Attributes:
|
||||||
|
* space — current space slug
|
||||||
|
*/
|
||||||
|
|
||||||
|
const RAUCTIONS_API = "https://rauctions.online";
|
||||||
|
|
||||||
|
class FolkAuctionsHub extends HTMLElement {
|
||||||
|
#shadow: ShadowRoot;
|
||||||
|
#space = "demo";
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.#shadow = this.attachShadow({ mode: "open" });
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.#space = this.getAttribute("space") || "demo";
|
||||||
|
this.#render();
|
||||||
|
this.#fetchAuctions();
|
||||||
|
}
|
||||||
|
|
||||||
|
async #fetchAuctions() {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${RAUCTIONS_API}/api/auctions?status=active`, {
|
||||||
|
headers: { "Accept": "application/json" },
|
||||||
|
});
|
||||||
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||||
|
const data = await res.json();
|
||||||
|
const auctions = Array.isArray(data) ? data : (data.auctions || []);
|
||||||
|
this.#renderAuctions(auctions);
|
||||||
|
} catch (err) {
|
||||||
|
this.#renderError(err instanceof Error ? err.message : "Failed to load auctions");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#renderAuctions(auctions: Array<{ id: string; title: string; currentBid?: number; endTime?: string; imageUrl?: string; status?: string }>) {
|
||||||
|
const list = this.#shadow.getElementById("auction-list");
|
||||||
|
if (!list) return;
|
||||||
|
|
||||||
|
if (auctions.length === 0) {
|
||||||
|
list.innerHTML = `
|
||||||
|
<div class="empty">
|
||||||
|
<div class="empty-icon">🏛</div>
|
||||||
|
<p>No active auctions right now.</p>
|
||||||
|
<a href="${RAUCTIONS_API}/auctions/create" target="_blank" rel="noopener" class="btn">Create an Auction</a>
|
||||||
|
</div>`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.innerHTML = auctions.map((a) => {
|
||||||
|
const bid = a.currentBid != null ? `$${a.currentBid.toFixed(2)}` : "No bids";
|
||||||
|
const endStr = a.endTime ? new Date(a.endTime).toLocaleString() : "";
|
||||||
|
const img = a.imageUrl
|
||||||
|
? `<img src="${a.imageUrl}" alt="" class="thumb" />`
|
||||||
|
: `<div class="thumb-placeholder">🏛</div>`;
|
||||||
|
return `
|
||||||
|
<a href="${RAUCTIONS_API}/auctions/${a.id}" target="_blank" rel="noopener" class="auction-card">
|
||||||
|
${img}
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="card-title">${this.#esc(a.title)}</div>
|
||||||
|
<div class="card-meta">
|
||||||
|
<span class="bid">${bid}</span>
|
||||||
|
${endStr ? `<span class="ends">Ends ${endStr}</span>` : ""}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>`;
|
||||||
|
}).join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
#renderError(msg: string) {
|
||||||
|
const list = this.#shadow.getElementById("auction-list");
|
||||||
|
if (!list) return;
|
||||||
|
list.innerHTML = `
|
||||||
|
<div class="empty">
|
||||||
|
<p style="color:var(--rs-error,#ef4444);">⚠ ${this.#esc(msg)}</p>
|
||||||
|
<a href="${RAUCTIONS_API}" target="_blank" rel="noopener" class="btn">Open rAuctions</a>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
#esc(s: string) {
|
||||||
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
||||||
|
}
|
||||||
|
|
||||||
|
#render() {
|
||||||
|
this.#shadow.innerHTML = `
|
||||||
|
<style>
|
||||||
|
:host { display: block; padding: 1rem; }
|
||||||
|
.header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1rem; }
|
||||||
|
.header h2 { font-size: 1.1rem; font-weight: 700; margin: 0; }
|
||||||
|
.btn {
|
||||||
|
display: inline-flex; align-items: center; gap: 6px;
|
||||||
|
padding: 6px 14px; border-radius: 8px; border: none;
|
||||||
|
background: var(--rs-accent, #6366f1); color: #fff;
|
||||||
|
font-size: 0.8rem; font-weight: 600; cursor: pointer;
|
||||||
|
text-decoration: none; transition: opacity 0.15s;
|
||||||
|
}
|
||||||
|
.btn:hover { opacity: 0.85; }
|
||||||
|
#auction-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 1rem; }
|
||||||
|
.auction-card {
|
||||||
|
display: flex; flex-direction: column;
|
||||||
|
border-radius: 10px; overflow: hidden;
|
||||||
|
border: 1px solid var(--rs-border-subtle, #333);
|
||||||
|
background: var(--rs-bg-surface, #1a1a2e);
|
||||||
|
text-decoration: none; color: inherit;
|
||||||
|
transition: border-color 0.15s, transform 0.1s;
|
||||||
|
}
|
||||||
|
.auction-card:hover { border-color: var(--rs-accent, #6366f1); transform: translateY(-1px); }
|
||||||
|
.thumb { width: 100%; height: 160px; object-fit: cover; }
|
||||||
|
.thumb-placeholder {
|
||||||
|
width: 100%; height: 160px; display: flex; align-items: center; justify-content: center;
|
||||||
|
font-size: 3rem; background: var(--rs-bg-hover, #222);
|
||||||
|
}
|
||||||
|
.card-body { padding: 0.75rem 1rem; }
|
||||||
|
.card-title { font-weight: 600; font-size: 0.9rem; margin-bottom: 4px; }
|
||||||
|
.card-meta { display: flex; gap: 8px; font-size: 0.75rem; opacity: 0.6; }
|
||||||
|
.bid { color: var(--rs-success, #22c55e); font-weight: 600; opacity: 1; }
|
||||||
|
.empty { text-align: center; padding: 3rem 1rem; opacity: 0.6; }
|
||||||
|
.empty-icon { font-size: 3rem; margin-bottom: 0.5rem; }
|
||||||
|
.loading { text-align: center; padding: 2rem; opacity: 0.5; }
|
||||||
|
</style>
|
||||||
|
<div class="header">
|
||||||
|
<h2>🏛 Active Auctions</h2>
|
||||||
|
<a href="${RAUCTIONS_API}/auctions/create" target="_blank" rel="noopener" class="btn">+ New Auction</a>
|
||||||
|
</div>
|
||||||
|
<div id="auction-list">
|
||||||
|
<div class="loading">Loading auctions…</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("folk-auctions-hub", FolkAuctionsHub);
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
/** Landing page body HTML for rAuctions */
|
||||||
|
export function renderLanding(): string {
|
||||||
|
return `
|
||||||
|
<div style="max-width:640px;margin:2rem auto;padding:0 1rem;text-align:center;">
|
||||||
|
<div style="font-size:3rem;margin-bottom:0.5rem;">🏛</div>
|
||||||
|
<h2 style="font-size:1.5rem;font-weight:700;margin-bottom:0.5rem;">rAuctions</h2>
|
||||||
|
<p style="color:var(--rs-text-secondary);margin-bottom:1.5rem;">
|
||||||
|
Live community auctions with real-time bidding and USDC settlement.
|
||||||
|
Create, bid on, and manage auctions for artwork, collectibles, and unique items.
|
||||||
|
</p>
|
||||||
|
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:1rem;text-align:left;">
|
||||||
|
<div style="padding:1rem;border-radius:8px;background:var(--rs-bg-surface);border:1px solid var(--rs-border-subtle);">
|
||||||
|
<div style="font-size:1.5rem;margin-bottom:0.25rem;">🔨</div>
|
||||||
|
<strong>Live Bidding</strong>
|
||||||
|
<p style="font-size:0.8rem;opacity:0.6;margin:0;">Real-time bids with countdown timers</p>
|
||||||
|
</div>
|
||||||
|
<div style="padding:1rem;border-radius:8px;background:var(--rs-bg-surface);border:1px solid var(--rs-border-subtle);">
|
||||||
|
<div style="font-size:1.5rem;margin-bottom:0.25rem;">💰</div>
|
||||||
|
<strong>USDC Settlement</strong>
|
||||||
|
<p style="font-size:0.8rem;opacity:0.6;margin:0;">On-chain payments via Base</p>
|
||||||
|
</div>
|
||||||
|
<div style="padding:1rem;border-radius:8px;background:var(--rs-bg-surface);border:1px solid var(--rs-border-subtle);">
|
||||||
|
<div style="font-size:1.5rem;margin-bottom:0.25rem;">📸</div>
|
||||||
|
<strong>Rich Listings</strong>
|
||||||
|
<p style="font-size:0.8rem;opacity:0.6;margin:0;">Photos, descriptions, and reserve prices</p>
|
||||||
|
</div>
|
||||||
|
<div style="padding:1rem;border-radius:8px;background:var(--rs-bg-surface);border:1px solid var(--rs-border-subtle);">
|
||||||
|
<div style="font-size:1.5rem;margin-bottom:0.25rem;">🔐</div>
|
||||||
|
<strong>EncryptID Auth</strong>
|
||||||
|
<p style="font-size:0.8rem;opacity:0.6;margin:0;">Secure identity for bidders & sellers</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
@ -1,20 +1,69 @@
|
||||||
/**
|
/**
|
||||||
* rAuctions module — Community auctions with USDC (stub).
|
* rAuctions module — community auctions with USDC bidding.
|
||||||
* TODO: Implement auction logic.
|
*
|
||||||
|
* Embeds the standalone rauctions.online Next.js app via externalApp iframe.
|
||||||
|
* Provides a hub page listing active auctions proxied from the rauctions API.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Hono } from 'hono';
|
import { Hono } from "hono";
|
||||||
import type { RSpaceModule } from '../../shared/module';
|
import { renderShell, renderExternalAppShell } from "../../server/shell";
|
||||||
|
import { getModuleInfoList } from "../../shared/module";
|
||||||
|
import type { RSpaceModule } from "../../shared/module";
|
||||||
|
import { renderLanding } from "./landing";
|
||||||
|
|
||||||
|
const RAUCTIONS_URL = process.env.RAUCTIONS_URL || "https://rauctions.online";
|
||||||
|
|
||||||
const routes = new Hono();
|
const routes = new Hono();
|
||||||
|
|
||||||
routes.get('/', (c) => c.text('rAuctions — coming soon'));
|
// ── Page route ──
|
||||||
|
routes.get("/", (c) => {
|
||||||
|
const spaceSlug = c.req.param("space") || "demo";
|
||||||
|
const view = c.req.query("view");
|
||||||
|
|
||||||
|
if (view === "app") {
|
||||||
|
return c.html(renderExternalAppShell({
|
||||||
|
title: `${spaceSlug} — Auctions | rSpace`,
|
||||||
|
moduleId: "rauctions",
|
||||||
|
spaceSlug,
|
||||||
|
modules: getModuleInfoList(),
|
||||||
|
appUrl: RAUCTIONS_URL,
|
||||||
|
appName: "rAuctions",
|
||||||
|
theme: "dark",
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.html(renderShell({
|
||||||
|
title: `${spaceSlug} — Auctions | rSpace`,
|
||||||
|
moduleId: "rauctions",
|
||||||
|
spaceSlug,
|
||||||
|
modules: getModuleInfoList(),
|
||||||
|
theme: "dark",
|
||||||
|
body: `<div class="rapp-nav" style="padding:0 1rem;margin-top:8px">
|
||||||
|
<span class="rapp-nav__title">Auctions</span>
|
||||||
|
<a href="?view=app" class="rapp-nav__btn--app-toggle">Open Full App</a>
|
||||||
|
</div>
|
||||||
|
<folk-auctions-hub space="${spaceSlug}"></folk-auctions-hub>`,
|
||||||
|
scripts: `<script type="module" src="/modules/rauctions/folk-auctions-hub.js"></script>`,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── API: proxy health ──
|
||||||
|
routes.get("/api/health", (c) => {
|
||||||
|
return c.json({ status: "ok", service: "rauctions" });
|
||||||
|
});
|
||||||
|
|
||||||
export const auctionsModule: RSpaceModule = {
|
export const auctionsModule: RSpaceModule = {
|
||||||
id: 'rauctions',
|
id: "rauctions",
|
||||||
name: 'rAuctions',
|
name: "rAuctions",
|
||||||
icon: '🎭',
|
icon: "🏛",
|
||||||
description: 'Community auctions with USDC',
|
description: "Live auctions with USDC bidding",
|
||||||
routes,
|
|
||||||
scoping: { defaultScope: 'space', userConfigurable: true },
|
scoping: { defaultScope: 'space', userConfigurable: true },
|
||||||
|
routes,
|
||||||
|
landingPage: renderLanding,
|
||||||
|
standaloneDomain: "rauctions.online",
|
||||||
|
externalApp: { url: RAUCTIONS_URL, name: "rAuctions" },
|
||||||
|
outputPaths: [
|
||||||
|
{ path: "live", name: "Live Auctions", icon: "🔨", description: "Currently active auctions" },
|
||||||
|
{ path: "ended", name: "Ended", icon: "🏆", description: "Completed auction results" },
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue