diff --git a/frontend/src/pages/proposals.astro b/frontend/src/pages/proposals.astro
index adbccb8..a26cd3b 100644
--- a/frontend/src/pages/proposals.astro
+++ b/frontend/src/pages/proposals.astro
@@ -44,7 +44,7 @@ import { API_BASE as apiBase } from '../lib/api';
-
@@ -78,6 +78,62 @@ import { API_BASE as apiBase } from '../lib/api';
container.innerHTML = html;
}
+ function renderErrorState(message) {
+ const container = document.getElementById('proposals-list');
+ if (!container) return;
+
+ container.innerHTML = `
+
+
${message}
+
Check your connection and try again.
+
+
+ `;
+
+ document.getElementById('retry-load')?.addEventListener('click', loadProposals);
+ }
+
+ function renderEmptyState(isFiltered) {
+ const container = document.getElementById('proposals-list');
+ if (!container) return;
+
+ container.innerHTML = `
+
+
${isFiltered ? 'No proposals match your filters.' : 'No proposals found.'}
+
Try adjusting search, status, or sort.
+
+
+ `;
+
+ document.getElementById('reset-filters')?.addEventListener('click', () => {
+ const searchInput = document.getElementById('search-input');
+ const statusFilter = document.getElementById('status-filter');
+ const sortFilter = document.getElementById('sort-filter');
+ if (searchInput) searchInput.value = '';
+ if (statusFilter) statusFilter.value = '';
+ if (sortFilter) sortFilter.value = 'newest';
+ filterProposals();
+ });
+ }
+
+ function hasActiveFilters() {
+ const searchInput = document.getElementById('search-input');
+ const statusFilter = document.getElementById('status-filter');
+ const sortFilter = document.getElementById('sort-filter');
+
+ const query = searchInput?.value.toLowerCase().trim() || '';
+ const status = statusFilter?.value || '';
+ const sort = sortFilter?.value || 'newest';
+
+ return Boolean(query || status || sort !== 'newest');
+ }
+
async function loadProposals() {
const container = document.getElementById('proposals-list');
if (!container) return;
@@ -88,7 +144,7 @@ import { API_BASE as apiBase } from '../lib/api';
allProposals = await res.json();
renderProposals(allProposals);
} catch (error) {
- container.innerHTML = 'Failed to load proposals.
';
+ renderErrorState('Failed to load proposals.');
}
}
@@ -97,7 +153,7 @@ import { API_BASE as apiBase } from '../lib/api';
if (!container) return;
if (proposals.length === 0) {
- container.innerHTML = '';
+ renderEmptyState(hasActiveFilters());
return;
}
@@ -171,6 +227,14 @@ import { API_BASE as apiBase } from '../lib/api';
gap: 1rem;
}
+ .state-actions {
+ margin-top: 1.25rem;
+ display: flex;
+ gap: 0.75rem;
+ justify-content: center;
+ flex-wrap: wrap;
+ }
+
.proposal-card {
display: block;
padding: 1.1rem;
diff --git a/frontend/src/pages/proposals/[id].astro b/frontend/src/pages/proposals/[id].astro
index aa9dfc2..4bbc571 100644
--- a/frontend/src/pages/proposals/[id].astro
+++ b/frontend/src/pages/proposals/[id].astro
@@ -28,6 +28,24 @@ const proposalId = id ?? '';
let quadraticAllocations = {};
let starRatings = {};
+ function renderProposalErrorState(message) {
+ const container = document.getElementById('proposal-content');
+ if (!container) return;
+
+ container.innerHTML = `
+
+
${escapeHtml(message)}
+
It may have been deleted, or you may not have access.
+
+
+ `;
+
+ document.getElementById('retry-proposal')?.addEventListener('click', loadProposal);
+ }
+
function escapeHtml(value) {
return String(value || '').replace(/[&<>"']/g, function(ch) {
switch (ch) {
@@ -407,7 +425,7 @@ const proposalId = id ?? '';
}
} catch (error) {
- container.innerHTML = 'Proposal not found
';
+ renderProposalErrorState('Proposal not found.');
}
}
@@ -600,7 +618,12 @@ const proposalId = id ?? '';
const comments = await res.json();
if (comments.length === 0) {
- container.innerHTML = '';
+ container.innerHTML = `
+
+
No comments yet.
+
Be the first to share your thoughts.
+
+ `;
return;
}
@@ -619,7 +642,17 @@ const proposalId = id ?? '';
`;
}).join('');
} catch (e) {
- container.innerHTML = 'Failed to load comments
';
+ container.innerHTML = `
+
+
Failed to load comments.
+
Try again in a moment.
+
+
+
+
+ `;
+
+ document.getElementById('retry-comments')?.addEventListener('click', loadComments);
}
}
@@ -659,6 +692,14 @@ const proposalId = id ?? '';
max-width: 880px;
}
+ .state-actions {
+ margin-top: 1.25rem;
+ display: flex;
+ gap: 0.75rem;
+ justify-content: center;
+ flex-wrap: wrap;
+ }
+
.results-chart-host {
display: none;
}