Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions design-system.html
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,16 @@ <h2>Buttons</h2>
<a class="button" href="#" style="background:rgba(120,100,70,0.12); border-color:var(--color-border-hover);">Hover State</a>
</div>

<p style="margin-top:18px;">
Add <code>.button-accent</code> for a single standout action on a page —
filled with <code>--color-accent</code>, parchment-light text, darkens
to <code>--color-accent-hover</code> on hover. Use sparingly: the
default <code>.button</code> remains the everyday affordance.
</p>
<div class="ds-button-row">
<a class="button button-accent" href="#">Accent Button</a>
</div>

<table class="ds-token-table">
<thead>
<tr><th>Property</th><th>Value</th></tr>
Expand Down
7 changes: 4 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,13 @@ <h1 class="hero-title">Windhover</h1>

<nav class="landing-nav" aria-label="Tools">
<a class="button landing-nav-link" href="apps/church-history-timeline.html">Church history timeline</a>
<a class="button landing-nav-link" href="pantheons-supabase.html">Pantheons database</a>
<a class="button landing-nav-link" href="apps/biblical-places.html">Biblical history atlas</a>
<a class="button landing-nav-link" href="apps/first-century-church.html">First century church directory</a>
<a class="button landing-nav-link" href="apps/biblical-places.html">Biblical history atlas</a>
<a class="button landing-nav-link" href="apps/index.html">Base timeline component</a>
<a class="button landing-nav-link" href="pantheons-supabase.html">Pantheons database</a>
<a class="button landing-nav-link admin-only" href="apps/african-kingdoms.html" style="display:none;">African kingdoms</a>
<a class="button landing-nav-link admin-only" href="design-system.html" style="display:none;">Design system</a>
<a class="button landing-nav-link" href="apps/index.html">Timeline component</a>
<a class="button button-accent landing-nav-link" href="apps/contributor-portal.html">Contributor portal</a>
</nav>

<footer class="site-footer">
Expand Down
16 changes: 16 additions & 0 deletions style.css
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,22 @@ code {
outline: none;
}

/* Filled accent variant — for a single standout action on a page.
Use sparingly: the default .button is the everyday affordance. */
.button-accent {
background: var(--color-accent);
border-color: var(--color-accent);
color: var(--color-parchment-light);
}

.button-accent:hover,
.button-accent:focus-visible {
background: var(--color-accent-hover);
border-color: var(--color-accent-hover);
color: var(--color-parchment-light);
outline: none;
}

/* ===================================================
SECONDARY ACTIONS (About, Substack, Sign In, More)
=================================================== */
Expand Down
14 changes: 14 additions & 0 deletions timeline-scratch/contributor-portal.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/windhover-logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Contributor Portal · Windhover</title>
</head>
<body>
<div id="root"></div>
<script src="/api/supabase-config"></script>
<script type="module" src="/src/main-contributor-portal.jsx"></script>
</body>
</html>
1 change: 1 addition & 0 deletions timeline-scratch/src/BiblicalPlacesApp.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ function BiblicalPlacesApp() {
)}
{hasClerk && (
<div className="bp-header-auth">
<a className="bp-auth-btn" href="./contributor-portal.html" style={{ textDecoration: 'none' }}>Contributor Portal</a>
<SignedOut>
<SignInButton mode="modal">
<button className="bp-auth-btn" title="Sign in to report issues">Sign In</button>
Expand Down
119 changes: 18 additions & 101 deletions timeline-scratch/src/ChurchHistorySupabaseApp.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
import { useState, useEffect, useCallback, useRef } from 'react';
import {
SignedIn,
SignedOut,
Expand All @@ -18,8 +18,6 @@ import { checkUserRole, ensureUserExists } from './services/adminService.js';
import { AdminSuggestionsPage } from './components/Suggestions/AdminSuggestionsPage.jsx';
import { SuggestNewModal } from './components/Suggestions/SuggestNewModal.jsx';
import { IssueCreatorButton } from './components/IssueCreator/IssueCreatorButton.jsx';
import { GettingStartedPage } from './components/GettingStarted/GettingStartedPage.jsx';
import { CollaboratorsOnlyNotice } from './components/GettingStarted/CollaboratorsOnlyNotice.jsx';
import { Icon } from './components/Timeline/components/Icon.jsx';
import { SiteNavPanel } from './components/SiteNavPanel.jsx';
import { useTour } from './components/Tour/useTour.js';
Expand Down Expand Up @@ -56,8 +54,6 @@ function ClerkAuthHeader({
isContributor,
onReviewSuggestions,
onSuggestNew,
onOpenGettingStarted,
hideIssueCreator = false,
getToken,
clerkUserId,
getPageContext,
Expand All @@ -83,21 +79,17 @@ function ClerkAuthHeader({
+ Suggest New Entry
</button>
)}
{!hideIssueCreator && (
<IssueCreatorButton
isContributor={isContributor}
isAdmin={isAdmin}
getToken={getToken}
clerkUserId={clerkUserId}
appId="ch-timeline"
getPageContext={getPageContext}
/>
)}
{(isContributor || isAdmin) && (
<button type="button" className="btn" onClick={onOpenGettingStarted}>
Contributor Portal
</button>
)}
<IssueCreatorButton
isContributor={isContributor}
isAdmin={isAdmin}
getToken={getToken}
clerkUserId={clerkUserId}
appId="ch-timeline"
getPageContext={getPageContext}
/>
<a className="btn" href="./contributor-portal.html">
Contributor Portal
</a>
{isAdmin && (
<button type="button" className="btn btn-warning" onClick={onReviewSuggestions}>
Review Suggestions
Expand Down Expand Up @@ -131,8 +123,7 @@ function AuthenticatedApp({ timelineData, loading, error, allPeople, onReloadDat
const [suggestNewOpen, setSuggestNewOpen] = useState(false);
const [isAdmin, setIsAdmin] = useState(false);
const [isContributor, setIsContributor] = useState(false);
const [userRole, setUserRole] = useState(null);
const [view, setView] = useState('timeline'); // 'timeline' | 'suggestions' | 'getting-started'
const [view, setView] = useState('timeline'); // 'timeline' | 'suggestions'
const [navOpen, setNavOpen] = useState(false);
const timelineRef = useRef(null);

Expand Down Expand Up @@ -163,38 +154,20 @@ function AuthenticatedApp({ timelineData, loading, error, allPeople, onReloadDat
if (!cancelled) {
setIsAdmin(result.isAdmin);
setIsContributor(result.isContributor);
setUserRole(result.role);
}
});

return () => { cancelled = true; };
}, [isSignedIn, userId, getToken, clerkUserLoaded]);

// Deep-link support: sync view ↔ location.hash for getting-started.
useEffect(() => {
if (typeof window === 'undefined') return undefined;
const syncFromHash = () => {
const hash = (window.location.hash || '').replace(/^#/, '');
if (hash === 'getting-started') setView('getting-started');
else if (view === 'getting-started') setView('timeline');
};
window.addEventListener('hashchange', syncFromHash);
syncFromHash();
return () => window.removeEventListener('hashchange', syncFromHash);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

// Legacy deep-link: the portal used to live at #getting-started inside this
// app. Redirect bookmarks to the standalone Contributor Portal page.
useEffect(() => {
if (typeof window === 'undefined') return;
const wantHash = view === 'getting-started' ? 'getting-started' : '';
const curHash = (window.location.hash || '').replace(/^#/, '');
if (wantHash && curHash !== wantHash) {
window.location.hash = wantHash;
} else if (!wantHash && curHash === 'getting-started') {
// Clear the hash without adding a history entry
history.replaceState(null, '', window.location.pathname + window.location.search);
if ((window.location.hash || '') === '#getting-started') {
window.location.replace('./contributor-portal.html');
}
}, [view]);
}, []);

const handleAddNoteClose = useCallback(() => {
setAddNoteOpen(false);
Expand Down Expand Up @@ -264,7 +237,6 @@ function AuthenticatedApp({ timelineData, loading, error, allPeople, onReloadDat
isContributor={isContributor}
onReviewSuggestions={() => setView('suggestions')}
onSuggestNew={() => setSuggestNewOpen(true)}
onOpenGettingStarted={() => setView('getting-started')}
getToken={getTokenForSupabase}
clerkUserId={userId}
getPageContext={getPageContext}
Expand All @@ -284,60 +256,6 @@ function AuthenticatedApp({ timelineData, loading, error, allPeople, onReloadDat
);
}

// Getting Started page for invited collaborators
if (view === 'getting-started') {
const email = clerkUser?.primaryEmailAddress?.emailAddress;
const displayName = clerkUser?.fullName || clerkUser?.firstName || null;
const canEnter = isContributor || isAdmin;

return (
<>
<header className="app-header">
<div className="header-content">
<SiteNavToggle onOpen={() => setNavOpen(true)} />
<div className="header-left">
<h1 className="site-title"><strong>History of the Christian Church</strong> <span>Lifespans</span></h1>
</div>
<div className="header-right">
<ClerkAuthHeader
onAddNote={() => setAddNoteOpen(true)}
onViewNotes={() => setViewNotesOpen(true)}
isAdmin={isAdmin}
isContributor={isContributor}
onReviewSuggestions={() => setView('suggestions')}
onSuggestNew={() => setSuggestNewOpen(true)}
onOpenGettingStarted={() => setView('getting-started')}
hideIssueCreator
getToken={getTokenForSupabase}
clerkUserId={userId}
getPageContext={getPageContext}
/>
<button type="button" className="btn" onClick={() => setView('timeline')}>
Back to Timeline
</button>
</div>
</div>
</header>
<div className="tab-content" style={{ overflow: 'auto' }}>
{canEnter ? (
<GettingStartedPage
getToken={getTokenForSupabase}
clerkUserId={userId}
displayName={displayName}
email={email}
role={userRole}
appId="ch-timeline"
getPageContext={getPageContext}
/>
) : (
<CollaboratorsOnlyNotice onBack={() => setView('timeline')} />
)}
</div>
<SiteNavPanel open={navOpen} onClose={() => setNavOpen(false)} activeKey="church-history" />
</>
);
}

return (
<>
<header className="app-header">
Expand Down Expand Up @@ -370,7 +288,6 @@ function AuthenticatedApp({ timelineData, loading, error, allPeople, onReloadDat
isContributor={isContributor}
onReviewSuggestions={() => setView('suggestions')}
onSuggestNew={() => setSuggestNewOpen(true)}
onOpenGettingStarted={() => setView('getting-started')}
getToken={getTokenForSupabase}
clerkUserId={userId}
getPageContext={getPageContext}
Expand Down
Loading
Loading