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>
|
<p>Loading demo status...</p>
|
||||||
</div>
|
</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">
|
<section class="demo-intro">
|
||||||
<div class="intro-content">
|
<div class="intro-content">
|
||||||
<h2>Why a Demo Instance?</h2>
|
<h2>Why a Demo Instance?</h2>
|
||||||
|
|
@ -283,6 +295,60 @@ const nextQuery = nextParam ? `&next=${encodeURIComponent(nextParam)}` : '';
|
||||||
line-height: 1.6;
|
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 */
|
/* Intro Section */
|
||||||
.demo-intro {
|
.demo-intro {
|
||||||
padding: 3rem 0;
|
padding: 3rem 0;
|
||||||
|
|
@ -701,6 +767,176 @@ const nextQuery = nextParam ? `&next=${encodeURIComponent(nextParam)}` : '';
|
||||||
const nextParam = currentUrl.searchParams.get('next') || '';
|
const nextParam = currentUrl.searchParams.get('next') || '';
|
||||||
const nextPath = nextParam && nextParam.startsWith('/') ? nextParam : '';
|
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() {
|
async function loadDemoData() {
|
||||||
try {
|
try {
|
||||||
// Fetch demo status
|
// Fetch demo status
|
||||||
|
|
@ -818,10 +1054,12 @@ const nextQuery = nextParam ? `&next=${encodeURIComponent(nextParam)}` : '';
|
||||||
if (document.readyState === 'loading') {
|
if (document.readyState === 'loading') {
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
loadDemoData();
|
loadDemoData();
|
||||||
|
loadSuggestedJourneys();
|
||||||
setupDemoLoginButtons();
|
setupDemoLoginButtons();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
loadDemoData();
|
loadDemoData();
|
||||||
|
loadSuggestedJourneys();
|
||||||
setupDemoLoginButtons();
|
setupDemoLoginButtons();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue