fix: remove rvote proposals from space dashboard + fix protocol in redirects
1. Remove all rvote/proposals fetching from rstack-user-dashboard.
rApp-specific data (proposals) should stay within the rVote module,
not leak into the space-level dashboard.
2. Fix url.protocol in bare-domain redirects — TLS is terminated by
Cloudflare/Traefik so url.protocol is always http: internally.
Use https: for production domains.
3. Rewrite /{space}/api/... paths internally on bare domain instead
of redirecting to subdomain (avoids CORS + mixed content issues).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6e4d1436e0
commit
4536b5cab1
|
|
@ -2,7 +2,7 @@
|
|||
* <rstack-user-dashboard> — Space-centric dashboard shown when all tabs are closed.
|
||||
*
|
||||
* Sections: space header + stats, members, tools open, recent activity,
|
||||
* active votes, and quick actions.
|
||||
* and quick actions.
|
||||
*
|
||||
* Attributes:
|
||||
* space — current space slug
|
||||
|
|
@ -26,15 +26,6 @@ interface NotificationItem {
|
|||
read: boolean;
|
||||
}
|
||||
|
||||
interface ProposalInfo {
|
||||
id: string;
|
||||
title: string;
|
||||
status: string;
|
||||
score: number;
|
||||
vote_count: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
interface TabLayer {
|
||||
moduleId: string;
|
||||
label?: string;
|
||||
|
|
@ -47,11 +38,9 @@ export class RStackUserDashboard extends HTMLElement {
|
|||
#shadow: ShadowRoot;
|
||||
#members: MemberInfo[] = [];
|
||||
#notifications: NotificationItem[] = [];
|
||||
#proposals: ProposalInfo[] = [];
|
||||
#openTabs: TabLayer[] = [];
|
||||
#membersLoading = true;
|
||||
#notifLoading = true;
|
||||
#proposalsLoading = true;
|
||||
#lastFetch = 0;
|
||||
|
||||
constructor() {
|
||||
|
|
@ -91,7 +80,6 @@ export class RStackUserDashboard extends HTMLElement {
|
|||
}
|
||||
this.#membersLoading = true;
|
||||
this.#notifLoading = true;
|
||||
this.#proposalsLoading = true;
|
||||
this.#render();
|
||||
this.#loadData();
|
||||
}
|
||||
|
|
@ -101,7 +89,6 @@ export class RStackUserDashboard extends HTMLElement {
|
|||
await Promise.all([
|
||||
this.#fetchMembers(),
|
||||
this.#fetchNotifications(),
|
||||
this.#fetchProposals(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -139,25 +126,6 @@ export class RStackUserDashboard extends HTMLElement {
|
|||
this.#render();
|
||||
}
|
||||
|
||||
async #fetchProposals() {
|
||||
this.#proposalsLoading = true;
|
||||
try {
|
||||
const slug = encodeURIComponent(this.space);
|
||||
const res = await fetch(`/${slug}/rvote/api/proposals?space_slug=${slug}&limit=5`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
this.#proposals = (data.proposals || []).filter(
|
||||
(p: ProposalInfo) => p.status !== "completed" && p.status !== "rejected",
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
// rvote not installed or offline — hide section
|
||||
this.#proposals = [];
|
||||
}
|
||||
this.#proposalsLoading = false;
|
||||
this.#render();
|
||||
}
|
||||
|
||||
#timeAgo(iso: string): string {
|
||||
const diff = Date.now() - new Date(iso).getTime();
|
||||
const mins = Math.floor(diff / 60_000);
|
||||
|
|
@ -219,9 +187,6 @@ export class RStackUserDashboard extends HTMLElement {
|
|||
if (!this.#membersLoading) {
|
||||
statPills.push(`<span class="stat-pill">${this.#members.length} member${this.#members.length !== 1 ? "s" : ""}</span>`);
|
||||
}
|
||||
if (!this.#proposalsLoading && this.#proposals.length > 0) {
|
||||
statPills.push(`<span class="stat-pill">${this.#proposals.length} active proposal${this.#proposals.length !== 1 ? "s" : ""}</span>`);
|
||||
}
|
||||
if (this.#openTabs.length > 0) {
|
||||
statPills.push(`<span class="stat-pill">${this.#openTabs.length} tab${this.#openTabs.length !== 1 ? "s" : ""} open</span>`);
|
||||
}
|
||||
|
|
@ -277,32 +242,7 @@ export class RStackUserDashboard extends HTMLElement {
|
|||
`).join("");
|
||||
}
|
||||
|
||||
// ── Active Votes ──
|
||||
let votesHTML = "";
|
||||
if (!this.#proposalsLoading && this.#proposals.length > 0) {
|
||||
votesHTML = `
|
||||
<div class="section">
|
||||
<div class="section-header"><h2>Active Votes</h2></div>
|
||||
<div class="votes-list">
|
||||
${this.#proposals.map(p => `
|
||||
<button class="vote-item" data-navigate="rvote">
|
||||
<div class="vote-info">
|
||||
<div class="vote-title">${p.title}</div>
|
||||
<div class="vote-meta">
|
||||
<span class="vote-status">${p.status}</span>
|
||||
<span class="vote-count">${p.vote_count} vote${p.vote_count !== "1" ? "s" : ""}</span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="vote-score">${p.score > 0 ? "+" : ""}${p.score}</span>
|
||||
</button>
|
||||
`).join("")}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// ── Quick Actions ──
|
||||
const hasProposals = this.#proposals.length > 0;
|
||||
|
||||
this.#shadow.innerHTML = `
|
||||
<style>${STYLES}</style>
|
||||
|
|
@ -332,8 +272,6 @@ export class RStackUserDashboard extends HTMLElement {
|
|||
<div class="activity-list">${activityHTML}</div>
|
||||
</div>
|
||||
|
||||
${votesHTML}
|
||||
|
||||
<div class="section">
|
||||
<div class="section-header"><h2>Quick Actions</h2></div>
|
||||
<div class="actions-row">
|
||||
|
|
@ -346,11 +284,6 @@ export class RStackUserDashboard extends HTMLElement {
|
|||
<button class="action-btn" data-action-module="rspace">
|
||||
<span class="action-icon">\u{1F30C}</span><span>Canvas</span>
|
||||
</button>
|
||||
${hasProposals ? `
|
||||
<button class="action-btn" data-action-module="rvote">
|
||||
<span class="action-icon">\u{1F5F3}</span><span>Vote</span>
|
||||
</button>
|
||||
` : ""}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -388,13 +321,6 @@ export class RStackUserDashboard extends HTMLElement {
|
|||
});
|
||||
});
|
||||
|
||||
// Vote items
|
||||
this.#shadow.querySelectorAll(".vote-item").forEach(el => {
|
||||
el.addEventListener("click", () => {
|
||||
this.#dispatch((el as HTMLElement).dataset.navigate || "rvote");
|
||||
});
|
||||
});
|
||||
|
||||
// Quick action buttons
|
||||
this.#shadow.querySelectorAll(".action-btn").forEach(el => {
|
||||
el.addEventListener("click", () => {
|
||||
|
|
@ -668,77 +594,6 @@ const STYLES = `
|
|||
margin-top: 2px;
|
||||
}
|
||||
|
||||
/* ── Active Votes ── */
|
||||
|
||||
.votes-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 10px;
|
||||
background: var(--rs-bg-surface, #1e293b);
|
||||
border: 1px solid var(--rs-border, rgba(255,255,255,0.08));
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.vote-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
padding: 10px 16px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--rs-border, rgba(255,255,255,0.06));
|
||||
background: none;
|
||||
text-align: left;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
width: 100%;
|
||||
}
|
||||
.vote-item:last-child { border-bottom: none; }
|
||||
.vote-item:hover {
|
||||
background: var(--rs-bg-hover, rgba(255,255,255,0.05));
|
||||
}
|
||||
|
||||
.vote-info { flex: 1; min-width: 0; }
|
||||
|
||||
.vote-title {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
color: var(--rs-text-primary, #e2e8f0);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.vote-meta {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.vote-status {
|
||||
font-size: 0.65rem;
|
||||
padding: 1px 6px;
|
||||
border-radius: 4px;
|
||||
background: rgba(251,191,36,0.15);
|
||||
color: #fbbf24;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.vote-count {
|
||||
font-size: 0.7rem;
|
||||
color: var(--rs-text-muted, #94a3b8);
|
||||
}
|
||||
|
||||
.vote-score {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 700;
|
||||
color: var(--rs-accent, #06b6d4);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ── Quick Actions ── */
|
||||
|
||||
.actions-row {
|
||||
|
|
|
|||
Loading…
Reference in New Issue