mirror of
https://codeberg.org/likwid/likwid.git
synced 2026-02-09 21:13:09 +00:00
demo: add guided start here journeys
This commit is contained in:
parent
a98895d935
commit
49579e9286
1 changed files with 1065 additions and 827 deletions
|
|
@ -32,6 +32,18 @@ const nextQuery = nextParam ? `&next=${encodeURIComponent(nextParam)}` : '';
|
|||
<p>Loading demo status...</p>
|
||||
</div>
|
||||
|
||||
<section class="demo-start" aria-labelledby="demo-start-title">
|
||||
<h2 id="demo-start-title">Start here</h2>
|
||||
<p class="section-intro">
|
||||
Pick a short journey. Each link drops you into a real community or proposal, so you can learn by doing.
|
||||
</p>
|
||||
<div class="start-grid" id="demo-start-grid" aria-live="polite">
|
||||
<div class="start-card ui-card ui-card-pad-lg">
|
||||
<p class="loading-text">Loading suggested journeys…</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="demo-intro">
|
||||
<div class="intro-content">
|
||||
<h2>Why a Demo Instance?</h2>
|
||||
|
|
@ -283,6 +295,60 @@ const nextQuery = nextParam ? `&next=${encodeURIComponent(nextParam)}` : '';
|
|||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.demo-start {
|
||||
padding: 3rem 0;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.demo-start h2 {
|
||||
font-size: 1.75rem;
|
||||
text-align: center;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.start-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1.25rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.start-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.start-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.start-card:hover {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.start-card h3 {
|
||||
margin: 0;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.start-card p {
|
||||
margin: 0;
|
||||
color: var(--color-text-muted);
|
||||
font-size: 0.9375rem;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.start-link {
|
||||
margin-top: auto;
|
||||
font-weight: 600;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* Intro Section */
|
||||
.demo-intro {
|
||||
padding: 3rem 0;
|
||||
|
|
@ -701,6 +767,176 @@ const nextQuery = nextParam ? `&next=${encodeURIComponent(nextParam)}` : '';
|
|||
const nextParam = currentUrl.searchParams.get('next') || '';
|
||||
const nextPath = nextParam && nextParam.startsWith('/') ? nextParam : '';
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value || '').replace(/[&<>"']/g, function(ch) {
|
||||
switch (ch) {
|
||||
case '&': return '&';
|
||||
case '<': return '<';
|
||||
case '>': return '>';
|
||||
case '"': return '"';
|
||||
case "'": return ''';
|
||||
default: return ch;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeStatus(status) {
|
||||
const s = String(status || '').toLowerCase();
|
||||
if (s === 'draft' || s === 'discussion' || s === 'voting' || s === 'closed') return s;
|
||||
return 'draft';
|
||||
}
|
||||
|
||||
function renderStartGrid(cards) {
|
||||
const grid = document.getElementById('demo-start-grid');
|
||||
if (!grid) return;
|
||||
|
||||
if (!cards || !cards.length) {
|
||||
grid.innerHTML = '<div class="start-card ui-card ui-card-pad-lg"><p class="loading-text">No journeys available.</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
grid.innerHTML = cards.map(function(c) {
|
||||
return (
|
||||
'<a href="' + c.href + '" class="start-card ui-card ui-card-pad-lg ui-card-interactive">' +
|
||||
'<h3>' + escapeHtml(c.title) + '</h3>' +
|
||||
'<p>' + escapeHtml(c.body) + '</p>' +
|
||||
'<span class="start-link">' + escapeHtml(c.cta) + '</span>' +
|
||||
'</a>'
|
||||
);
|
||||
}).join('');
|
||||
}
|
||||
|
||||
async function loadSuggestedJourneys() {
|
||||
const grid = document.getElementById('demo-start-grid');
|
||||
if (!grid) return;
|
||||
|
||||
grid.innerHTML = '<div class="start-card ui-card ui-card-pad-lg"><p class="loading-text">Loading suggested journeys…</p></div>';
|
||||
|
||||
const fallback = function() {
|
||||
renderStartGrid([
|
||||
{
|
||||
title: 'Explore a community overview',
|
||||
body: 'See members, recent proposals, and moderation history in context.',
|
||||
cta: 'Open Aurora →',
|
||||
href: '/demo?enter=1&next=%2Fcommunities%2Faurora'
|
||||
},
|
||||
{
|
||||
title: 'Browse proposals in one community',
|
||||
body: 'Jump straight to a proposal list and pick a closed or voting item.',
|
||||
cta: 'Open proposals →',
|
||||
href: '/demo?enter=1&next=%2Fcommunities%2Faurora%2Fproposals'
|
||||
},
|
||||
{
|
||||
title: 'Log in and participate',
|
||||
body: 'Use a demo account to vote and see the member experience.',
|
||||
cta: 'Log in →',
|
||||
href: '/demo?enter=1&next=%2Flogin'
|
||||
}
|
||||
]);
|
||||
};
|
||||
|
||||
try {
|
||||
const statusRes = await fetch(API_BASE + '/api/demo/status');
|
||||
const status = await statusRes.json();
|
||||
if (!status || !status.demo_mode) {
|
||||
fallback();
|
||||
return;
|
||||
}
|
||||
|
||||
const demoCommRes = await fetch(API_BASE + '/api/demo/communities');
|
||||
const demoCommData = await demoCommRes.json();
|
||||
const slugs = (demoCommData && demoCommData.communities ? demoCommData.communities.map(function(c) { return c.slug; }) : [])
|
||||
.filter(Boolean);
|
||||
const knownSlugs = slugs.length ? slugs : ['aurora', 'civic-commons', 'makers'];
|
||||
|
||||
const commRes = await fetch(API_BASE + '/api/communities');
|
||||
const communities = await commRes.json();
|
||||
const commBySlug = {};
|
||||
for (var i = 0; i < knownSlugs.length; i++) {
|
||||
var s = knownSlugs[i];
|
||||
commBySlug[s] = (communities || []).find(function(c) { return c.slug === s; }) || null;
|
||||
}
|
||||
|
||||
const proposalFetches = knownSlugs.map(function(s) {
|
||||
const comm = commBySlug[s];
|
||||
if (!comm || !comm.id) return Promise.resolve([]);
|
||||
return fetch(API_BASE + '/api/communities/' + encodeURIComponent(String(comm.id)) + '/proposals')
|
||||
.then(function(r) { return r.ok ? r.json() : []; })
|
||||
.then(function(ps) {
|
||||
if (!Array.isArray(ps)) return [];
|
||||
return ps.map(function(p) {
|
||||
return Object.assign({}, p, { community_slug: s, community_name: comm.name || s });
|
||||
});
|
||||
})
|
||||
.catch(function() { return []; });
|
||||
});
|
||||
|
||||
const lists = await Promise.all(proposalFetches);
|
||||
const all = [].concat.apply([], lists);
|
||||
const pickByStatus = function(target) {
|
||||
for (var j = 0; j < all.length; j++) {
|
||||
if (normalizeStatus(all[j].status) === target) return all[j];
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const voting = pickByStatus('voting');
|
||||
const closed = pickByStatus('closed');
|
||||
const discussion = pickByStatus('discussion');
|
||||
|
||||
const cards = [];
|
||||
cards.push({
|
||||
title: 'Explore a community overview',
|
||||
body: 'Start with a full picture: members, proposals, and governance activity in one place.',
|
||||
cta: 'Open Aurora →',
|
||||
href: '/demo?enter=1&next=%2Fcommunities%2Faurora'
|
||||
});
|
||||
|
||||
if (voting && voting.id) {
|
||||
cards.push({
|
||||
title: 'Vote on an active decision',
|
||||
body: 'Open a proposal that is currently in voting. Log in as Contributor to cast a ballot.',
|
||||
cta: 'Open voting proposal →',
|
||||
href: '/demo?enter=1&next=' + encodeURIComponent('/proposals/' + String(voting.id))
|
||||
});
|
||||
} else if (discussion && discussion.id) {
|
||||
cards.push({
|
||||
title: 'Read an ongoing discussion',
|
||||
body: 'See how proposals evolve before the vote begins.',
|
||||
cta: 'Open discussion proposal →',
|
||||
href: '/demo?enter=1&next=' + encodeURIComponent('/proposals/' + String(discussion.id))
|
||||
});
|
||||
} else {
|
||||
cards.push({
|
||||
title: 'Browse proposals',
|
||||
body: 'Pick a community and explore drafts, discussions, and votes.',
|
||||
cta: 'Open proposals →',
|
||||
href: '/demo?enter=1&next=%2Fcommunities%2Faurora%2Fproposals'
|
||||
});
|
||||
}
|
||||
|
||||
if (closed && closed.id) {
|
||||
cards.push({
|
||||
title: 'See a completed decision',
|
||||
body: 'Review a closed proposal and inspect the outcome and voting results.',
|
||||
cta: 'Open closed proposal →',
|
||||
href: '/demo?enter=1&next=' + encodeURIComponent('/proposals/' + String(closed.id))
|
||||
});
|
||||
} else {
|
||||
cards.push({
|
||||
title: 'Log in and participate',
|
||||
body: 'Use a demo account to vote, comment, and explore member-only areas.',
|
||||
cta: 'Log in →',
|
||||
href: '/demo?enter=1&next=%2Flogin'
|
||||
});
|
||||
}
|
||||
|
||||
renderStartGrid(cards.slice(0, 3));
|
||||
} catch (e) {
|
||||
fallback();
|
||||
}
|
||||
}
|
||||
|
||||
async function loadDemoData() {
|
||||
try {
|
||||
// Fetch demo status
|
||||
|
|
@ -818,10 +1054,12 @@ const nextQuery = nextParam ? `&next=${encodeURIComponent(nextParam)}` : '';
|
|||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
loadDemoData();
|
||||
loadSuggestedJourneys();
|
||||
setupDemoLoginButtons();
|
||||
});
|
||||
} else {
|
||||
loadDemoData();
|
||||
loadSuggestedJourneys();
|
||||
setupDemoLoginButtons();
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Reference in a new issue