2026-01-27 16:21:58 +00:00
---
import Layout from '../layouts/Layout.astro';
2026-01-28 23:47:14 +00:00
import { API_BASE as apiBase } from '../lib/api';
2026-01-27 16:21:58 +00:00
---
<Layout title="Communities">
<section class="communities">
<div class="header-row">
<div>
<h1>Communities</h1>
<p class="subtitle">Browse organizations and communities</p>
</div>
<a href="/communities/new" class="btn-create" id="create-btn" style="display: none;">+ Create Community</a>
</div>
<div class="search-bar">
<input type="text" id="search-input" placeholder="Search communities..." />
</div>
<div id="communities-list" class="list">
<p class="loading">Loading communities...</p>
</div>
2026-01-28 23:47:14 +00:00
<script is:inline define:vars={{ apiBase }}>
2026-01-27 16:21:58 +00:00
(function() {
2026-01-28 23:47:14 +00:00
var API_BASE = apiBase;
2026-01-27 16:21:58 +00:00
var allCommunities = [];
function renderCommunities(communities) {
var container = document.getElementById('communities-list');
if (!container) return;
if (!communities || communities.length === 0) {
container.innerHTML = '<div class="empty"><p>No communities found.</p></div>';
return;
}
var html = '';
for (var i = 0; i < communities.length; i++) {
var c = communities[i];
html += '<a href="/communities/' + c.slug + '" class="community-card">' +
'<h3>' + c.name + '</h3>' +
'<p class="slug">/' + c.slug + '</p>' +
'<p class="description">' + (c.description || 'No description') + '</p>' +
'</a>';
}
container.innerHTML = html;
}
function filterCommunities(query) {
var q = query.toLowerCase().trim();
if (!q) {
renderCommunities(allCommunities);
return;
}
var filtered = allCommunities.filter(function(c) {
return c.name.toLowerCase().indexOf(q) >= 0 ||
c.slug.toLowerCase().indexOf(q) >= 0 ||
(c.description && c.description.toLowerCase().indexOf(q) >= 0);
});
renderCommunities(filtered);
}
function loadCommunities() {
var container = document.getElementById('communities-list');
if (!container) return;
fetch(API_BASE + '/api/communities')
.then(function(res) {
if (!res.ok) throw new Error('HTTP ' + res.status);
return res.json();
})
.then(function(data) {
allCommunities = Array.isArray(data) ? data : (data.value || data.communities || []);
renderCommunities(allCommunities);
})
.catch(function(error) {
console.error('Failed to load communities:', error);
container.innerHTML = '<div class="error"><p>Failed to load communities.</p><p class="hint">Make sure the backend is running on localhost:3000</p></div>';
});
}
// Initialize
loadCommunities();
// Setup search
var searchInput = document.getElementById('search-input');
if (searchInput) {
searchInput.addEventListener('input', function(e) {
filterCommunities(e.target.value);
});
}
// Show create button if logged in
var token = localStorage.getItem('token');
var createBtn = document.getElementById('create-btn');
if (token && createBtn) {
createBtn.style.display = 'block';
}
})();
</script>
</section>
</Layout>
<style>
.communities {
padding: 1.5rem 0;
}
.header-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
gap: 1rem;
}
.btn-create {
background: var(--color-primary);
color: var(--color-text-inverse);
padding: 0.625rem 1.25rem;
border-radius: var(--radius-sm);
font-weight: 600;
font-size: 0.875rem;
white-space: nowrap;
transition: all var(--motion-fast) var(--easing-standard);
}
.btn-create:hover {
background: var(--color-primary-hover);
color: var(--color-text-inverse);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(129, 140, 248, 0.25);
}
.btn-create:active {
transform: translateY(0);
box-shadow: var(--shadow-sm);
}
.search-bar {
margin-bottom: 1.5rem;
}
.search-bar input {
width: 100%;
max-width: 400px;
border-radius: var(--radius-md);
}
.search-bar input::placeholder {
color: var(--color-text-muted);
}
h1 {
font-size: 2rem;
font-weight: 700;
letter-spacing: -0.02em;
margin-bottom: 0.25rem;
}
.subtitle {
color: var(--color-text-muted);
font-size: 0.9375rem;
}
.list {
display: grid;
gap: 1rem;
}
.community-card {
display: block;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
padding: 1.25rem 1.5rem;
color: var(--color-text);
transition: all var(--motion-normal) var(--easing-standard);
}
.community-card:hover {
border-color: var(--color-border-hover);
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.community-card h3 {
font-size: 1.125rem;
font-weight: 600;
margin-bottom: 0.25rem;
}
.slug {
color: var(--color-primary);
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
font-size: 0.8125rem;
margin-bottom: 0.5rem;
opacity: 0.9;
}
.description {
color: var(--color-text-muted);
font-size: 0.9375rem;
line-height: 1.5;
}
.loading, .empty, .error {
text-align: center;
padding: 4rem 2rem;
color: var(--color-text-muted);
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
}
.hint {
font-size: 0.875rem;
margin-top: 0.5rem;
}
.error {
color: var(--color-error);
}
</style>