mirror of
https://codeberg.org/likwid/likwid.git
synced 2026-02-09 21:13:09 +00:00
ux: improve communities states
This commit is contained in:
parent
512ed13889
commit
e084c0d235
2 changed files with 168 additions and 13 deletions
|
|
@ -96,6 +96,56 @@ import { API_BASE as apiBase } from '../lib/api';
|
||||||
container.innerHTML = html;
|
container.innerHTML = html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderErrorState(message) {
|
||||||
|
var container = document.getElementById('communities-list');
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
container.innerHTML =
|
||||||
|
'<div class="state-card ui-card">' +
|
||||||
|
'<p class="error">' + escapeHtml(message) + '</p>' +
|
||||||
|
'<p class="hint">Check your connection and try again.</p>' +
|
||||||
|
'<div class="state-actions">' +
|
||||||
|
'<button type="button" class="ui-btn ui-btn-primary" id="retry-communities">Retry</button>' +
|
||||||
|
'<a class="ui-btn ui-btn-secondary" href="/proposals">Browse proposals</a>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>';
|
||||||
|
|
||||||
|
var retryBtn = document.getElementById('retry-communities');
|
||||||
|
if (retryBtn) {
|
||||||
|
retryBtn.addEventListener('click', function() {
|
||||||
|
loadCommunities();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderEmptyState(isFiltered) {
|
||||||
|
var container = document.getElementById('communities-list');
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
var label = isFiltered ? 'No communities match your search.' : 'No communities found.';
|
||||||
|
var hint = isFiltered ? 'Try a different keyword or clear the search.' : 'Please try again later.';
|
||||||
|
|
||||||
|
container.innerHTML =
|
||||||
|
'<div class="state-card ui-card">' +
|
||||||
|
'<p class="empty">' + label + '</p>' +
|
||||||
|
'<p class="hint">' + hint + '</p>' +
|
||||||
|
'<div class="state-actions">' +
|
||||||
|
(isFiltered ? '<button type="button" class="ui-btn ui-btn-secondary" id="reset-community-search">Reset search</button>' : '') +
|
||||||
|
'<a class="ui-btn ui-btn-primary" href="/communities">Refresh</a>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>';
|
||||||
|
|
||||||
|
var resetBtn = document.getElementById('reset-community-search');
|
||||||
|
if (resetBtn) {
|
||||||
|
resetBtn.addEventListener('click', function() {
|
||||||
|
var searchInput = document.getElementById('search-input');
|
||||||
|
if (searchInput) searchInput.value = '';
|
||||||
|
currentQuery = '';
|
||||||
|
applyFilterSortAndRender();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function sortCommunities(list) {
|
function sortCommunities(list) {
|
||||||
var sorted = list.slice();
|
var sorted = list.slice();
|
||||||
sorted.sort(function(a, b) {
|
sorted.sort(function(a, b) {
|
||||||
|
|
@ -128,7 +178,7 @@ import { API_BASE as apiBase } from '../lib/api';
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
if (!communities || communities.length === 0) {
|
if (!communities || communities.length === 0) {
|
||||||
container.innerHTML = '<div class="state-card ui-card"><p>No communities found.</p></div>';
|
renderEmptyState(!!String(currentQuery || '').trim());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -189,7 +239,8 @@ import { API_BASE as apiBase } from '../lib/api';
|
||||||
})
|
})
|
||||||
.catch(function(error) {
|
.catch(function(error) {
|
||||||
console.error('Failed to load communities:', error);
|
console.error('Failed to load communities:', error);
|
||||||
container.innerHTML = '<div class="state-card ui-card"><p class="error">Failed to load communities.</p><p class="hint">Please try again later.</p></div>';
|
updateKpis('—', '—', '');
|
||||||
|
renderErrorState('Failed to load communities.');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,24 @@ const { slug } = Astro.params;
|
||||||
<script define:vars={{ slug, apiBase }}>
|
<script define:vars={{ slug, apiBase }}>
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
|
|
||||||
|
function renderCommunityErrorState(message) {
|
||||||
|
const container = document.getElementById('community-content');
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="state-card ui-card">
|
||||||
|
<p class="error">${escapeHtml(message)}</p>
|
||||||
|
<p class="hint">Check your connection and try again.</p>
|
||||||
|
<div class="state-actions">
|
||||||
|
<button type="button" class="ui-btn ui-btn-primary" id="retry-community">Retry</button>
|
||||||
|
<a class="ui-btn ui-btn-secondary" href="/communities">Back to communities</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.getElementById('retry-community')?.addEventListener('click', loadCommunity);
|
||||||
|
}
|
||||||
|
|
||||||
function escapeHtml(value) {
|
function escapeHtml(value) {
|
||||||
return String(value || '').replace(/[&<>"']/g, function(ch) {
|
return String(value || '').replace(/[&<>"']/g, function(ch) {
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
|
|
@ -41,7 +59,15 @@ const { slug } = Astro.params;
|
||||||
const community = communities.find(c => c.slug === slug);
|
const community = communities.find(c => c.slug === slug);
|
||||||
|
|
||||||
if (!community) {
|
if (!community) {
|
||||||
container.innerHTML = '<div class="state-card ui-card"><p class="error">Community not found</p></div>';
|
container.innerHTML = `
|
||||||
|
<div class="state-card ui-card">
|
||||||
|
<p class="error">Community not found.</p>
|
||||||
|
<p class="hint">It may have been deleted, or the link is incorrect.</p>
|
||||||
|
<div class="state-actions">
|
||||||
|
<a class="ui-btn ui-btn-primary" href="/communities">Back to communities</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -181,7 +207,7 @@ const { slug } = Astro.params;
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
container.innerHTML = '<div class="error">Failed to load community</div>';
|
renderCommunityErrorState('Failed to load community.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,7 +233,16 @@ const { slug } = Astro.params;
|
||||||
const proposals = await res.json();
|
const proposals = await res.json();
|
||||||
|
|
||||||
if (proposals.length === 0) {
|
if (proposals.length === 0) {
|
||||||
container.innerHTML = '<p class="empty-small">No proposals yet</p>';
|
var communitySlug = encodeURIComponent(String(slug || ''));
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="state-card ui-card ui-card-soft">
|
||||||
|
<p class="empty">No proposals yet.</p>
|
||||||
|
<p class="hint">When someone creates one, it’ll show up here.</p>
|
||||||
|
<div class="state-actions">
|
||||||
|
<a class="ui-btn ui-btn-secondary" href="/communities/${communitySlug}/proposals">All proposals</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,7 +262,17 @@ const { slug } = Astro.params;
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
container.innerHTML = '<p class="error-small">Failed to load</p>';
|
container.innerHTML = `
|
||||||
|
<div class="state-card ui-card ui-card-soft">
|
||||||
|
<p class="error">Failed to load proposals.</p>
|
||||||
|
<p class="hint">Please try again.</p>
|
||||||
|
<div class="state-actions">
|
||||||
|
<button type="button" class="ui-btn ui-btn-secondary" id="retry-recent-proposals">Retry</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.getElementById('retry-recent-proposals')?.addEventListener('click', () => loadProposals(communityId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -240,7 +285,12 @@ const { slug } = Astro.params;
|
||||||
const members = await res.json();
|
const members = await res.json();
|
||||||
|
|
||||||
if (members.length === 0) {
|
if (members.length === 0) {
|
||||||
container.innerHTML = '<p class="empty-small">No members yet</p>';
|
container.innerHTML = `
|
||||||
|
<div class="state-card ui-card ui-card-soft">
|
||||||
|
<p class="empty">No members yet.</p>
|
||||||
|
<p class="hint">Once people join, they’ll appear here.</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -259,7 +309,17 @@ const { slug } = Astro.params;
|
||||||
`;
|
`;
|
||||||
}).join('');
|
}).join('');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
container.innerHTML = '<p class="error-small">Failed to load</p>';
|
container.innerHTML = `
|
||||||
|
<div class="state-card ui-card ui-card-soft">
|
||||||
|
<p class="error">Failed to load members.</p>
|
||||||
|
<p class="hint">Please try again.</p>
|
||||||
|
<div class="state-actions">
|
||||||
|
<button type="button" class="ui-btn ui-btn-secondary" id="retry-members">Retry</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.getElementById('retry-members')?.addEventListener('click', () => loadMembers(communityId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -350,7 +410,15 @@ const { slug } = Astro.params;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!token) {
|
if (!token) {
|
||||||
container.innerHTML = '<p class="empty-small"><a href="/login">Login</a> to view moderation history</p>';
|
container.innerHTML = `
|
||||||
|
<div class="state-card ui-card ui-card-soft">
|
||||||
|
<p class="empty">Login required.</p>
|
||||||
|
<p class="hint">Sign in to view moderation history.</p>
|
||||||
|
<div class="state-actions">
|
||||||
|
<a class="ui-btn ui-btn-primary" href="/login">Login</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -359,19 +427,37 @@ const { slug } = Astro.params;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status === 401) {
|
if (res.status === 401) {
|
||||||
container.innerHTML = '<p class="error-small"><a href="/login">Login</a> to view moderation history</p>';
|
container.innerHTML = `
|
||||||
|
<div class="state-card ui-card ui-card-soft">
|
||||||
|
<p class="error">Login required.</p>
|
||||||
|
<p class="hint">Sign in to view moderation history.</p>
|
||||||
|
<div class="state-actions">
|
||||||
|
<a class="ui-btn ui-btn-primary" href="/login">Login</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res.status === 403) {
|
if (res.status === 403) {
|
||||||
container.innerHTML = '<p class="error-small">You do not have permission to view moderation history</p>';
|
container.innerHTML = `
|
||||||
|
<div class="state-card ui-card ui-card-soft">
|
||||||
|
<p class="error">Not allowed.</p>
|
||||||
|
<p class="hint">You do not have permission to view moderation history.</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const entries = await res.json();
|
const entries = await res.json();
|
||||||
|
|
||||||
if (entries.length === 0) {
|
if (entries.length === 0) {
|
||||||
container.innerHTML = '<p class="empty-small">No moderation actions yet</p>';
|
container.innerHTML = `
|
||||||
|
<div class="state-card ui-card ui-card-soft">
|
||||||
|
<p class="empty">No moderation actions yet.</p>
|
||||||
|
<p class="hint">When something happens, it’ll show up here.</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -395,7 +481,17 @@ const { slug } = Astro.params;
|
||||||
`;
|
`;
|
||||||
}).join('');
|
}).join('');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
container.innerHTML = '<p class="error-small">Failed to load</p>';
|
container.innerHTML = `
|
||||||
|
<div class="state-card ui-card ui-card-soft">
|
||||||
|
<p class="error">Failed to load moderation history.</p>
|
||||||
|
<p class="hint">Please try again.</p>
|
||||||
|
<div class="state-actions">
|
||||||
|
<button type="button" class="ui-btn ui-btn-secondary" id="retry-mod-log">Retry</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.getElementById('retry-mod-log')?.addEventListener('click', () => loadModerationLog(communityId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -407,6 +503,14 @@ const { slug } = Astro.params;
|
||||||
--ui-hero-mb: 1.25rem;
|
--ui-hero-mb: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.state-actions {
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.75rem;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
.hero-slug {
|
.hero-slug {
|
||||||
margin-top: 0.25rem;
|
margin-top: 0.25rem;
|
||||||
font-family: 'SF Mono', Monaco, 'Cascadia Code', ui-monospace, monospace;
|
font-family: 'SF Mono', Monaco, 'Cascadia Code', ui-monospace, monospace;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue