feat: add functionalities overview
This commit is contained in:
+157
-1
@@ -9,6 +9,7 @@ import type { AppState, Feature, FeatureColumn, ParkingLotItem, PulseEvent, Risk
|
||||
import { arrayToLines, formatDateTime, linesToArray, nowIso, slugify } from './utils/format'
|
||||
|
||||
const TABS: Array<{ key: TabKey; label: string }> = [
|
||||
{ key: 'functionalities', label: 'Functionalities' },
|
||||
{ key: 'feature-plan', label: 'Feature Plan' },
|
||||
{ key: 'parking-lot', label: 'Parking Lot' },
|
||||
{ key: 'pulse-log', label: 'Pulse Log' },
|
||||
@@ -65,7 +66,7 @@ const downloadText = (filename: string, text: string, contentType = 'text/plain;
|
||||
|
||||
function App() {
|
||||
const [appState, setAppState] = useState<AppState>(() => loadAppState())
|
||||
const [activeTab, setActiveTab] = useState<TabKey>('feature-plan')
|
||||
const [activeTab, setActiveTab] = useState<TabKey>('functionalities')
|
||||
const [statusMessage, setStatusMessage] = useState('Seeded with BuildPulse so you can dogfood it immediately.')
|
||||
const [selectedFeatureId, setSelectedFeatureId] = useState<string | null>(null)
|
||||
const [selectedParkingId, setSelectedParkingId] = useState<string | null>(null)
|
||||
@@ -611,6 +612,63 @@ function App() {
|
||||
|
||||
const currentFeatureCount = groupedFeatures.now.length
|
||||
const recentPulsePreview = [...appState.pulses].sort((a, b) => b.timestamp.localeCompare(a.timestamp)).slice(0, 3)
|
||||
const completedFeatureCount = groupedFeatures.done.length
|
||||
const functionalityCards = [
|
||||
{
|
||||
title: 'Project Cockpit',
|
||||
status: 'live',
|
||||
description: 'Single-project mission, goal, notes, and focus statistics stay visible before the board tries to swallow the room.',
|
||||
signal: appState.project.current_goal ? 'Goal set' : 'Needs current goal',
|
||||
metric: appState.project.name,
|
||||
action: 'Edit summary',
|
||||
tab: 'functionalities' as TabKey,
|
||||
},
|
||||
{
|
||||
title: 'Feature Plan',
|
||||
status: currentFeatureCount ? 'active' : 'ready',
|
||||
description: 'Now / Next / Later / Done columns keep work small, shaped, and movable without becoming Jira in a fake moustache.',
|
||||
signal: `${appState.features.length} total · ${currentFeatureCount} now · ${completedFeatureCount} done`,
|
||||
metric: `${currentFeatureCount} now`,
|
||||
action: 'Open board',
|
||||
tab: 'feature-plan' as TabKey,
|
||||
},
|
||||
{
|
||||
title: 'Parking Lot',
|
||||
status: appState.parking_lot.length ? 'active' : 'ready',
|
||||
description: 'Useful distractions get captured, risk-tagged, and converted into features only when they earn their keep.',
|
||||
signal: `${appState.parking_lot.length} parked idea${appState.parking_lot.length === 1 ? '' : 's'}`,
|
||||
metric: `${appState.parking_lot.length} parked`,
|
||||
action: 'Review parked',
|
||||
tab: 'parking-lot' as TabKey,
|
||||
},
|
||||
{
|
||||
title: 'Pulse Log',
|
||||
status: appState.pulses.length ? 'active' : 'ready',
|
||||
description: 'Intent, decisions, blockers, test results, and outcomes form a future-compatible trail for agents and humans.',
|
||||
signal: recentPulsePreview[0] ? `Latest: ${recentPulsePreview[0].pulse_type}` : 'No pulses yet',
|
||||
metric: `${appState.pulses.length} pulses`,
|
||||
action: 'Open log',
|
||||
tab: 'pulse-log' as TabKey,
|
||||
},
|
||||
{
|
||||
title: 'AI Handoff + Export',
|
||||
status: 'live',
|
||||
description: 'Generate JSON, JSONL, Markdown packages, and focused session prompts so coding agents get context without soup.',
|
||||
signal: `${Object.keys(markdownPackage).length} markdown files ready`,
|
||||
metric: 'handoff ready',
|
||||
action: 'Export context',
|
||||
tab: 'export' as TabKey,
|
||||
},
|
||||
{
|
||||
title: 'Appwrite Sync',
|
||||
status: backendMode === 'appwrite' && syncStatus === 'synced' ? 'live' : syncStatus === 'degraded' ? 'degraded' : 'syncing',
|
||||
description: 'State persists through the Appwrite runtime document, with explicit refresh and force-sync controls for operator recovery.',
|
||||
signal: backendMode === 'appwrite' ? `Sync status: ${syncStatus}` : 'Local cache fallback active',
|
||||
metric: backendMode === 'appwrite' ? 'Appwrite' : 'cache',
|
||||
action: 'Refresh state',
|
||||
tab: 'functionalities' as TabKey,
|
||||
},
|
||||
]
|
||||
const backendLabel =
|
||||
backendMode === 'appwrite' ? 'Appwrite backend · Unraid server' : backendMode === 'connecting' ? 'Connecting to Appwrite…' : 'Local cache fallback'
|
||||
const syncLabel =
|
||||
@@ -722,6 +780,9 @@ function App() {
|
||||
</nav>
|
||||
|
||||
<div className="quick-actions card">
|
||||
<button type="button" className="ghost" onClick={() => setActiveTab('functionalities')}>
|
||||
Show Functionalities
|
||||
</button>
|
||||
<button type="button" onClick={() => { setActiveTab('feature-plan'); resetFeatureDraft() }}>
|
||||
Add Feature
|
||||
</button>
|
||||
@@ -736,6 +797,101 @@ function App() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{activeTab === 'functionalities' && (
|
||||
<section className="view-stack">
|
||||
<div className="section-heading">
|
||||
<div>
|
||||
<h2>Functionalities</h2>
|
||||
<p>The living map of what BuildPulse actually does right now — no brochure fog, no phantom roadmap theatre.</p>
|
||||
</div>
|
||||
<div className="functionality-summary">
|
||||
<span className="pill status-healthy">NPM live</span>
|
||||
<span className="pill status-healthy">Appwrite backed</span>
|
||||
<span className="pill">Unraid runtime</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section className="card functionality-hero">
|
||||
<div>
|
||||
<p className="eyebrow">Capability map</p>
|
||||
<h3>{appState.project.name} is a pulse-compatible feature cockpit.</h3>
|
||||
<p>
|
||||
It keeps the product thread visible: define the mission, shape features, park distractions, log movement, sync state,
|
||||
and hand clean context to AI agents without turning the app into a bloated command bunker.
|
||||
</p>
|
||||
</div>
|
||||
<div className="functionality-scorecard">
|
||||
<div>
|
||||
<span>Live functions</span>
|
||||
<strong>{functionalityCards.filter((card) => card.status === 'live' || card.status === 'active').length}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Operator recovery</span>
|
||||
<strong>{backendMode === 'appwrite' ? 'Ready' : 'Cache'}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Context packages</span>
|
||||
<strong>{Object.keys(markdownPackage).length}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="functionality-grid">
|
||||
{functionalityCards.map((card) => (
|
||||
<article key={card.title} className={`card functionality-card functionality-${card.status}`}>
|
||||
<div className="item-card-header">
|
||||
<div>
|
||||
<p className="eyebrow">{card.status}</p>
|
||||
<h3>{card.title}</h3>
|
||||
</div>
|
||||
<span className="pill">{card.metric}</span>
|
||||
</div>
|
||||
<p>{card.description}</p>
|
||||
<div className="functionality-signal">
|
||||
<span>{card.signal}</span>
|
||||
<button
|
||||
type="button"
|
||||
className="ghost small"
|
||||
onClick={() => {
|
||||
if (card.title === 'Appwrite Sync') {
|
||||
void refreshFromBackend()
|
||||
} else {
|
||||
setActiveTab(card.tab)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{card.action}
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<section className="card functionality-roadmap">
|
||||
<div className="section-heading compact">
|
||||
<div>
|
||||
<h3>What this is deliberately not yet</h3>
|
||||
<p>Guardrails matter. The small cockpit wins because it refuses to cosplay as a whole enterprise suite.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="roadmap-grid">
|
||||
<div>
|
||||
<strong>Not Jira</strong>
|
||||
<p>No issue jungle, sprint ceremony, or fake certainty factory.</p>
|
||||
</div>
|
||||
<div>
|
||||
<strong>Not an autonomous agent framework</strong>
|
||||
<p>Agent ingestion can come later; manual pulse truth comes first.</p>
|
||||
</div>
|
||||
<div>
|
||||
<strong>Not multi-project yet</strong>
|
||||
<p>Single-project discipline keeps v0.1 sharp enough to dogfood.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{activeTab === 'feature-plan' && (
|
||||
<section className="view-stack">
|
||||
<div className="section-heading">
|
||||
|
||||
Reference in New Issue
Block a user