fix(rtime): use getApiBase() for subdomain-compatible URL routing

Replace all hardcoded /${space}/rtime paths with getApiBase() which
derives the correct API base from window.location.pathname. This
supports both subdomain routing (demo.rspace.online/rtime) and
path-based fallback (/demo/rtime).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-03-31 22:46:33 -07:00
parent 08cae267fe
commit 2907935c50
2 changed files with 15 additions and 8 deletions

View File

@ -316,12 +316,19 @@ class FolkTimebankApp extends HTMLElement {
this.fetchData();
}
/** Derive API base from the current pathname — works for both subdomain and path routing. */
private getApiBase(): string {
const path = window.location.pathname;
const match = path.match(/^(\/[^/]+)?\/rtime/);
return match ? match[0] : '/rtime';
}
disconnectedCallback() {
if (this.animFrame) cancelAnimationFrame(this.animFrame);
}
private async fetchData() {
const base = `/${this.space}/rtime`;
const base = this.getApiBase();
try {
const [cResp, tResp] = await Promise.all([
fetch(`${base}/api/commitments`),
@ -788,7 +795,7 @@ class FolkTimebankApp extends HTMLElement {
// Try server-side persist
try {
const resp = await fetch(`/${this.space}/rtime/api/commitments`, {
const resp = await fetch(`${this.getApiBase()}/api/commitments`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ memberName: name, skill, hours, desc }),
@ -1449,7 +1456,7 @@ class FolkTimebankApp extends HTMLElement {
const description = (this.shadow.getElementById('intentDesc') as HTMLInputElement).value;
try {
const resp = await fetch(`/${this.space}/rtime/api/intent`, {
const resp = await fetch(`${this.getApiBase()}/api/intent`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ type, skill, hours, description }),
@ -1468,7 +1475,7 @@ class FolkTimebankApp extends HTMLElement {
private async triggerSolver() {
try {
await fetch(`/${this.space}/rtime/api/solver/run`, { method: 'POST' });
await fetch(`${this.getApiBase()}/api/solver/run`, { method: 'POST' });
this.refreshCollaborate();
} catch {
// ignore
@ -1476,7 +1483,7 @@ class FolkTimebankApp extends HTMLElement {
}
private async refreshCollaborate() {
const base = `/${this.space}/rtime`;
const base = this.getApiBase();
try {
const [iResp, sResp, cResp] = await Promise.all([
fetch(`${base}/api/intents`),
@ -1600,7 +1607,7 @@ class FolkTimebankApp extends HTMLElement {
btn.addEventListener('click', async () => {
const resultId = (btn as HTMLElement).dataset.resultId;
try {
await fetch(`/${this.space}/rtime/api/solver-results/${resultId}/accept`, { method: 'POST' });
await fetch(`${this.getApiBase()}/api/solver-results/${resultId}/accept`, { method: 'POST' });
this.refreshCollaborate();
} catch { /* ignore */ }
});
@ -1610,7 +1617,7 @@ class FolkTimebankApp extends HTMLElement {
btn.addEventListener('click', async () => {
const resultId = (btn as HTMLElement).dataset.resultId;
try {
await fetch(`/${this.space}/rtime/api/solver-results/${resultId}/reject`, { method: 'POST' });
await fetch(`${this.getApiBase()}/api/solver-results/${resultId}/reject`, { method: 'POST' });
this.refreshCollaborate();
} catch { /* ignore */ }
});

View File

@ -427,6 +427,6 @@ export const timeModule: RSpaceModule = {
{ path: "collaborate", name: "Collaborate", icon: "🤝", description: "Intent-routed collaboration matching" },
],
onboardingActions: [
{ label: "Pledge Hours", icon: "⏳", description: "Add a commitment to the pool", type: 'create', href: '/{space}/rtime' },
{ label: "Pledge Hours", icon: "⏳", description: "Add a commitment to the pool", type: 'create', href: '/rtime' },
],
};