From 3b4a741aea27376fc3b17a8720f9f3fcca9fa7c4 Mon Sep 17 00:00:00 2001 From: Adam Spitz Date: Tue, 8 Sep 2026 10:34:56 -0400 Subject: [PATCH 1/4] Restore a first-visit CauseStarter landing on `/`. Keep the role cards as the way in rather than a Start-a-cause-board CTA, and send `/welcome` to the same page. --- causestarter/README.md | 4 +- causestarter/e2e/connect-and-start.spec.ts | 5 +- ui/src/causestarter/pages/HomePage.test.tsx | 9 +- ui/src/causestarter/pages/HomePage.tsx | 172 +++++++++++++------- ui/src/causestarter/pages/WelcomePage.tsx | 107 +----------- ui/src/domains/causestarter/manifest.tsx | 6 +- 6 files changed, 128 insertions(+), 175 deletions(-) diff --git a/causestarter/README.md b/causestarter/README.md index d12dac1e..89acc321 100644 --- a/causestarter/README.md +++ b/causestarter/README.md @@ -303,8 +303,8 @@ Then **restart Grok** so MCP tools load. | `wallet-account-menu` | Hardhat account picker (localhost only) | | `wallet-hardhat-0` … `wallet-hardhat-9` | Pick Hardhat account | | `wallet-disconnect` | Disconnect | -| `home-start-cause` | Home / `/welcome` CTA → create a draft and open the cause editor | -| `home-dashboard` | Occupied home (connected wallet and/or cause boards on this device) | +| `home-landing` | Root landing (first-visit pitch plus role cards) | +| `home-dashboard` | Role-card grid on the home landing | | `home-dashboard-board` | Occupied-home teaser of the personal fundable-projects board | | `home-dashboard-see-all` | Occupied home → `/dashboard` (full personal list) | | `personal-dashboard-page` | Full personal fundable-projects board at `/dashboard` | diff --git a/causestarter/e2e/connect-and-start.spec.ts b/causestarter/e2e/connect-and-start.spec.ts index b8667232..1a0b169f 100644 --- a/causestarter/e2e/connect-and-start.spec.ts +++ b/causestarter/e2e/connect-and-start.spec.ts @@ -55,15 +55,16 @@ test.describe('CauseStarter agent smoke', () => { await page.goto(appPath('/')) }) - test('occupied home shows the personal fundable-projects board after connect', async ({ page }) => { + test('personal fundable-projects board lives under Fund after connect', async ({ page }) => { await connectHardhat0(page) + await page.getByTestId('nav-fund').click() await expect(page.getByTestId('home-dashboard-board')).toBeVisible({ timeout: 15_000 }) await expect(page.getByRole('heading', { name: /your fundable projects/i })).toBeVisible() }) test('starts a cause and lands on its editable page', async ({ page }) => { await expect(page.getByTestId('wallet-connect-button')).toBeVisible() - await expect(page.getByTestId('home-start-cause')).toBeVisible() + await expect(page.getByTestId('home-landing')).toBeVisible() await connectHardhat0(page) await startCause(page) diff --git a/ui/src/causestarter/pages/HomePage.test.tsx b/ui/src/causestarter/pages/HomePage.test.tsx index efdf1c6c..49fcf2e2 100644 --- a/ui/src/causestarter/pages/HomePage.test.tsx +++ b/ui/src/causestarter/pages/HomePage.test.tsx @@ -26,11 +26,16 @@ function renderHome({ connected = false, statements = 0, pledges = 0, notes = 0 render() } -describe('HomePage role launcher', () => { +describe('HomePage landing', () => { afterEach(cleanup) - it('gives every visitor the focused role cards', () => { + it('explains the product to a first-time visitor and still offers the role cards', () => { renderHome() + expect(screen.getByTestId('home-landing')).toBeInTheDocument() + expect(screen.getByRole('heading', { name: 'There are enough of us. We just couldn’t work together.' })).toBeInTheDocument() + expect(screen.getByText('A cause board is a bulletin of crowdfundable projects', { exact: false })).toBeInTheDocument() + expect(screen.queryByRole('button', { name: 'Start a cause board' })).not.toBeInTheDocument() + expect(screen.getByRole('link', { name: 'Read the short version' })).toHaveAttribute('href', '/docs') expect(screen.getByRole('heading', { name: 'What would you like to do?' })).toBeInTheDocument() expect(screen.getByTestId('home-role-sign')).toHaveAttribute('href', '/statements') expect(screen.getByTestId('home-role-donate')).toHaveAttribute('href', '/donate') diff --git a/ui/src/causestarter/pages/HomePage.tsx b/ui/src/causestarter/pages/HomePage.tsx index df103cbe..4a53bb5c 100644 --- a/ui/src/causestarter/pages/HomePage.tsx +++ b/ui/src/causestarter/pages/HomePage.tsx @@ -1,5 +1,5 @@ import ArrowForwardIcon from '@mui/icons-material/ArrowForward' -import { Box, Card, CardActionArea, CardContent, CircularProgress, Stack, Typography } from '@mui/material' +import { Box, Button, Card, CardActionArea, CardContent, CircularProgress, Paper, Stack, Typography } from '@mui/material' import type { ReactNode } from 'react' import { Link as RouterLink } from 'react-router-dom' import { useAccount } from 'wagmi' @@ -51,69 +51,121 @@ export function HomePage() { const fundingBoard = readPersonalFundingBoard(address) return ( - - - + + + theme.palette.mode === 'light' + ? 'linear-gradient(160deg, rgba(15,118,110,0.10) 0%, rgba(255,252,247,0.95) 55%, #fff 100%)' + : 'linear-gradient(160deg, rgba(45,212,191,0.14) 0%, rgba(15,23,42,0.9) 60%, #0b1220 100%)', + }} + > + + CauseStarter + + + There are enough of us. We just couldn’t work together. + + + A cause board is a bulletin of crowdfundable projects: raise a specific + amount by a date to do a specific piece of work. If the crowd doesn’t + show, contributors get their money back. That is a third way besides + “government does it” and “a big charity does it.” + + + You do not have to watch the board — pledge a monthly amount to someone + you already trust. You do not have to bet on pitches — reimburse work + that already delivered. Signing a statement does not spend money. + + + + + + + What would you like to do? - - Choose a job. Each workspace stays focused, and you can come back here whenever you want to switch roles. + + Money, judgment, skilled work, and organizing do not have to arrive in + one organization. Pick the job you would do anyway. Come back here to + switch. - - - 0 - ? {countLabel(statements.length, 'signed statement')} - : Find a statement worth standing behind.} - action={statements.length > 0 ? 'Continue signing' : 'Explore statements'} - /> - {countLabel(donation.activePledgeCount, 'monthly pledge')} · {countLabel(donation.activeNoteCount, 'active fund')} - : Set up giving that does not need your daily attention.} - action={hasDonateActivity ? 'Manage donations' : 'Set up a donation'} - /> - {countLabel(fundingBoard.statementCids.length, 'statement')} in your board{fundingBoard.geographicWithin?.length ? ` · ${fundingBoard.geographicWithin.join(', ')}` : ''} - : statements.length > 0 - ? {countLabel(statements.length, 'signed statement')} (default board) - : Set the scope of your personal funding board.} - action={fundingBoard || statements.length > 0 ? 'Review projects' : 'Set up your funding board'} - /> - 0 - ? {countLabel(createdProjects, 'created project')} - : Publish a piece of work people can fund.} - action={createdProjects > 0 ? 'Continue your work' : 'Start a project'} - /> - 0 - ? {countLabel(causes.length, 'cause board')} - : Turn a useful mix of statements into a board people can share.} - action={causes.length > 0 ? 'Continue organizing' : 'Start organizing'} - /> + + 0 + ? {countLabel(statements.length, 'signed statement')} + : Find a statement worth standing behind.} + action={statements.length > 0 ? 'Continue signing' : 'Explore statements'} + /> + {countLabel(donation.activePledgeCount, 'monthly pledge')} · {countLabel(donation.activeNoteCount, 'active fund')} + : Set up giving that does not need your daily attention.} + action={hasDonateActivity ? 'Manage donations' : 'Set up a donation'} + /> + {countLabel(fundingBoard.statementCids.length, 'statement')} in your board{fundingBoard.geographicWithin?.length ? ` · ${fundingBoard.geographicWithin.join(', ')}` : ''} + : statements.length > 0 + ? {countLabel(statements.length, 'signed statement')} (default board) + : Set the scope of your personal funding board.} + action={fundingBoard || statements.length > 0 ? 'Review projects' : 'Set up your funding board'} + /> + 0 + ? {countLabel(createdProjects, 'created project')} + : Publish a piece of work people can fund.} + action={createdProjects > 0 ? 'Continue your work' : 'Start a project'} + /> + 0 + ? {countLabel(causes.length, 'cause board')} + : Turn a useful mix of statements into a board people can share.} + action={causes.length > 0 ? 'Continue organizing' : 'Start organizing'} + /> + ) diff --git a/ui/src/causestarter/pages/WelcomePage.tsx b/ui/src/causestarter/pages/WelcomePage.tsx index 56894230..9e31d920 100644 --- a/ui/src/causestarter/pages/WelcomePage.tsx +++ b/ui/src/causestarter/pages/WelcomePage.tsx @@ -1,106 +1 @@ -import { Box, Button, Paper, Stack, Typography } from '@mui/material' -import { Link as RouterLink, useNavigate } from 'react-router-dom' -import { CrowdJobs } from '../components/CrowdJobs' -import { createCausePath } from '../lib/causeStore' -import { JOBS_DOC_PATH } from '../lib/jobs' - -/** Always the first-visit pitch, even if this device already has causes. */ -export function WelcomePage() { - const navigate = useNavigate() - - return ( - - - theme.palette.mode === 'light' - ? 'linear-gradient(160deg, rgba(15,118,110,0.10) 0%, rgba(255,252,247,0.95) 55%, #fff 100%)' - : 'linear-gradient(160deg, rgba(45,212,191,0.14) 0%, rgba(15,23,42,0.9) 60%, #0b1220 100%)', - }} - > - - CauseStarter - - - There are enough of us. We just couldn’t work together. - - - Give money without becoming a grant officer. Spot projects without bankrolling them. - Do the work without knowing a foundation. Sign what you actually mean. The rest is - optional. - - - - - - - - - - - Take the job you’d take anyway - - - Nobody has to agree on a leader, a manifesto, or a treasury. Overlap is enough. - - - - - - - Docs - - - How to start a cause board, the full ugh-catalog, walkthroughs, and the longer argument. - There is no directory of other people’s cause boards — you get there by their link. - - - - - ) -} +export { HomePage as WelcomePage } from './HomePage' diff --git a/ui/src/domains/causestarter/manifest.tsx b/ui/src/domains/causestarter/manifest.tsx index c13fe73f..996ff756 100644 --- a/ui/src/domains/causestarter/manifest.tsx +++ b/ui/src/domains/causestarter/manifest.tsx @@ -2,7 +2,7 @@ import type { ReactNode } from 'react' import { Navigate, Route } from 'react-router-dom' import type { DomainManifest } from '../types' import { lazyRoute } from '../lazyRoute' -import { WelcomePage } from '../../causestarter/pages/WelcomePage' +import { HomePage } from '../../causestarter/pages/HomePage' import { CauseShell } from '../../causestarter/shell/CauseShell' const routes: ReactNode = ( @@ -10,7 +10,7 @@ const routes: ReactNode = ( import('../../causestarter/pages/HomePage'), 'HomePage')} /> import('../../causestarter/pages/PersonalDashboardPage'), 'PersonalDashboardPage')} /> import('../../delegation/pages/MyNotesPage'), 'DonatePage')} /> - import('../../causestarter/pages/WelcomePage'), 'WelcomePage')} /> + } /> import('../../causestarter/pages/StartCauseRedirect'), 'StartCauseRedirect')} /> import('../../causestarter/pages/StartBridgeRedirect'), 'StartBridgeRedirect')} /> import('../../causestarter/pages/BridgeTriplePage'), 'BridgeTriplePage')} /> @@ -84,5 +84,5 @@ export const causestarterManifest: DomainManifest = { basePath: '/', routes, Shell: CauseShell, - LandingPage: WelcomePage, + LandingPage: HomePage, } From 51ebc35344574b33f5c1a5980030f5a183ad9062 Mon Sep 17 00:00:00 2001 From: Adam Spitz Date: Tue, 8 Sep 2026 11:03:11 -0400 Subject: [PATCH 2/4] Add a CauseStarter /profile page for stuff this wallet has done. Keep home as what you could do next. Profile lists pledges, receipts, created projects, published cause boards, signed statements, and alignment attestations, with a header icon and a home link into it. --- .../causestarter/hooks/useUserAlignments.ts | 59 +++++ ui/src/causestarter/pages/HomePage.test.tsx | 1 + ui/src/causestarter/pages/HomePage.tsx | 9 + .../causestarter/pages/ProfilePage.test.tsx | 73 ++++++ ui/src/causestarter/pages/ProfilePage.tsx | 219 ++++++++++++++++++ ui/src/causestarter/shell/CauseShell.tsx | 10 + ui/src/domains/causestarter/manifest.tsx | 2 + 7 files changed, 373 insertions(+) create mode 100644 ui/src/causestarter/hooks/useUserAlignments.ts create mode 100644 ui/src/causestarter/pages/ProfilePage.test.tsx create mode 100644 ui/src/causestarter/pages/ProfilePage.tsx diff --git a/ui/src/causestarter/hooks/useUserAlignments.ts b/ui/src/causestarter/hooks/useUserAlignments.ts new file mode 100644 index 00000000..07988d4e --- /dev/null +++ b/ui/src/causestarter/hooks/useUserAlignments.ts @@ -0,0 +1,59 @@ +import { useCallback, useEffect, useState } from 'react' +import { useAccount } from 'wagmi' +import { getAlignmentsByAttester, type AlignmentAttestation } from '@commonality/sdk/fundingportals' +import { useMachinery } from '../../shared' + +export function useUserAlignments(): { + attestations: AlignmentAttestation[] + loading: boolean + connected: boolean + error: string | null + refresh: () => void +} { + const machinery = useMachinery() + const { address } = useAccount() + const [attestations, setAttestations] = useState([]) + const [loading, setLoading] = useState(Boolean(address)) + const [error, setError] = useState(null) + const [tick, setTick] = useState(0) + + const refresh = useCallback(() => setTick((n) => n + 1), []) + + useEffect(() => { + let cancelled = false + + const run = async () => { + if (!address) { + if (!cancelled) { + setAttestations([]) + setError(null) + setLoading(false) + } + return + } + + if (!cancelled) setLoading(true) + try { + const next = await getAlignmentsByAttester(machinery, address) + if (!cancelled) { + setAttestations(next) + setError(null) + } + } catch (cause) { + if (!cancelled) { + setAttestations([]) + setError(cause instanceof Error ? cause.message : 'Could not load alignment attestations') + } + } finally { + if (!cancelled) setLoading(false) + } + } + + void run() + return () => { + cancelled = true + } + }, [machinery, address, tick]) + + return { attestations, loading, connected: Boolean(address), error, refresh } +} diff --git a/ui/src/causestarter/pages/HomePage.test.tsx b/ui/src/causestarter/pages/HomePage.test.tsx index 49fcf2e2..04a09f1d 100644 --- a/ui/src/causestarter/pages/HomePage.test.tsx +++ b/ui/src/causestarter/pages/HomePage.test.tsx @@ -42,6 +42,7 @@ describe('HomePage landing', () => { expect(screen.getByTestId('home-role-fund')).toHaveAttribute('href', '/dashboard') expect(screen.getByTestId('home-role-work')).toHaveAttribute('href', '/work') expect(screen.getByTestId('home-role-organize')).toHaveAttribute('href', '/causes') + expect(screen.getByTestId('home-profile-link')).toHaveAttribute('href', '/profile') expect(screen.getByText('Signing does not commit money.', { exact: false })).toBeInTheDocument() }) diff --git a/ui/src/causestarter/pages/HomePage.tsx b/ui/src/causestarter/pages/HomePage.tsx index 4a53bb5c..cf8cafe7 100644 --- a/ui/src/causestarter/pages/HomePage.tsx +++ b/ui/src/causestarter/pages/HomePage.tsx @@ -167,6 +167,15 @@ export function HomePage() { /> + + ) } diff --git a/ui/src/causestarter/pages/ProfilePage.test.tsx b/ui/src/causestarter/pages/ProfilePage.test.tsx new file mode 100644 index 00000000..b4973e77 --- /dev/null +++ b/ui/src/causestarter/pages/ProfilePage.test.tsx @@ -0,0 +1,73 @@ +import { render, screen } from '@testing-library/react' +import { MemoryRouter } from 'react-router-dom' +import { describe, expect, it, vi } from 'vitest' +import { ProfilePage } from './ProfilePage' + +vi.mock('wagmi', () => ({ + useAccount: () => ({ address: '0xabc', isConnected: true }), +})) + +vi.mock('../../shared', () => ({ + AddressDisplay: ({ address }: { address: string }) =>
{address}
, +})) + +vi.mock('../hooks/useDonationSummary', () => ({ + useDonationSummary: () => ({ + activePledgeCount: 1, + activeNoteCount: 2, + delegatedNoteCount: 0, + loading: false, + }), +})) + +vi.mock('../hooks/useUserProjects', () => ({ + useUserProjects: () => ({ projects: [], loading: false, connected: true }), +})) + +vi.mock('../hooks/useUserStatements', () => ({ + useUserStatements: () => ({ statements: [], loading: false, error: null }), +})) + +vi.mock('../hooks/useUserCauses', () => ({ + useUserCauses: () => ({ causes: [], loading: false }), +})) + +vi.mock('../hooks/useUserAlignments', () => ({ + useUserAlignments: () => ({ attestations: [], loading: false, error: null, refresh: () => undefined }), +})) + +vi.mock('../components/YourProjects', () => ({ + YourProjects: ({ testId, heading }: { testId: string; heading: string }) => ( +
{heading}
+ ), +})) + +vi.mock('../components/CauseCard', () => ({ + CauseCard: () => null, +})) + +vi.mock('../components/ConnectWalletHint', () => ({ + ConnectWalletHint: ({ children }: { children: string }) =>
{children}
, +})) + +describe('ProfilePage', () => { + it('is a record of past activity, not a workspace for next actions', () => { + render( + + + , + ) + expect(screen.getByTestId('profile-page')).toBeInTheDocument() + expect(screen.getByRole('heading', { name: 'Stuff you’ve done' })).toBeInTheDocument() + expect(screen.getByTestId('profile-giving')).toBeInTheDocument() + expect(screen.getByText('1 monthly pledge · 2 active funds')).toBeInTheDocument() + expect(screen.getByTestId('profile-contributed-projects')).toBeInTheDocument() + expect(screen.getByTestId('profile-created-projects')).toBeInTheDocument() + expect(screen.getByTestId('profile-causes')).toBeInTheDocument() + expect(screen.getByTestId('profile-statements')).toBeInTheDocument() + expect(screen.getByTestId('profile-alignments')).toBeInTheDocument() + expect(screen.getByRole('link', { name: 'Open Donate' })).toHaveAttribute('href', '/donate') + expect(screen.getByRole('link', { name: 'Open Organize' })).toHaveAttribute('href', '/causes') + expect(screen.getByRole('link', { name: 'Open Sign' })).toHaveAttribute('href', '/statements') + }) +}) diff --git a/ui/src/causestarter/pages/ProfilePage.tsx b/ui/src/causestarter/pages/ProfilePage.tsx new file mode 100644 index 00000000..238030cb --- /dev/null +++ b/ui/src/causestarter/pages/ProfilePage.tsx @@ -0,0 +1,219 @@ +import { Alert, Box, Button, CircularProgress, Paper, Stack, Typography } from '@mui/material' +import { Link as RouterLink } from 'react-router-dom' +import { useAccount } from 'wagmi' +import { AddressDisplay } from '../../shared' +import { CauseCard } from '../components/CauseCard' +import { ConnectWalletHint } from '../components/ConnectWalletHint' +import { YourProjects } from '../components/YourProjects' +import { useDonationSummary } from '../hooks/useDonationSummary' +import { useUserAlignments } from '../hooks/useUserAlignments' +import { useUserCauses } from '../hooks/useUserCauses' +import { useUserProjects } from '../hooks/useUserProjects' +import { useUserStatements } from '../hooks/useUserStatements' +import { isLive } from '../lib/causeStore' + +function countLabel(count: number, singular: string, plural = `${singular}s`) { + return `${count} ${count === 1 ? singular : plural}` +} + +function paddedAddressSubject(subjectId: string): string | null { + const hex = subjectId.toLowerCase().replace(/^0x/, '') + if (hex.length !== 64 || !hex.startsWith('0'.repeat(24))) return null + return `0x${hex.slice(24)}` +} + +export function ProfilePage() { + const { address, isConnected } = useAccount() + const donation = useDonationSummary() + const { projects, loading: projectsLoading, connected } = useUserProjects() + const { statements, loading: statementsLoading, error: statementsError } = useUserStatements() + const { causes, loading: causesLoading } = useUserCauses() + const { attestations, loading: alignmentsLoading, error: alignmentsError, refresh: refreshAlignments } = useUserAlignments() + + const contributed = projects.filter((project) => project.relations.includes('contributed')) + const created = projects.filter((project) => project.relations.includes('created')) + const addressLc = address?.toLowerCase() + const organized = causes.filter((cause) => isLive(cause) && cause.founderAddress?.toLowerCase() === addressLc) + + return ( + + + + Profile + + + Stuff you’ve done + + + This is a record for this wallet, not a workspace. Home is for what you + could do next. Signing, donating, funding, working, and organizing stay + in their own places. + + {address && ( + + + + )} + + + {!isConnected && ( + Connect a wallet to see pledges, receipts, causes, signatures, and attestations on this device. + )} + + + Giving + + Monthly pledges and funds you still have open. The Donate workspace is + where you change them. + + {donation.loading ? ( + + + Loading giving… + + ) : ( + + {countLabel(donation.activePledgeCount, 'monthly pledge')} · {countLabel(donation.activeNoteCount, 'active fund')} + + )} + + + + + + + + + + Cause boards you published + + {causesLoading && organized.length === 0 ? ( + + + Loading cause boards… + + ) : organized.length === 0 ? ( + + No published cause boards from this wallet on this device. + + ) : ( + + {organized.map((cause) => )} + + )} + + + + + + Statements you’ve signed + + {statementsLoading && statements.length === 0 ? ( + + + Loading signed statements… + + ) : statementsError ? ( + {statementsError} + ) : statements.length === 0 ? ( + + No signed statements on this wallet yet. + + ) : ( + + {statements.map((statement) => ( + + ))} + + )} + + + + + + Alignment attestations + + + Vouches this wallet made that a project advances a statement. + + {alignmentsLoading && attestations.length === 0 ? ( + + + Loading attestations… + + ) : alignmentsError ? ( + + {alignmentsError} + + + ) : attestations.length === 0 ? ( + + No alignment attestations from this wallet yet. + + ) : ( + + {attestations.map((attestation) => { + const projectAddress = paddedAddressSubject(attestation.subjectId) + return ( + + + {projectAddress ? ( + + ) : ( + `Subject ${attestation.subjectId.slice(0, 12)}…` + )} + + + + ) + })} + + )} + + + ) +} diff --git a/ui/src/causestarter/shell/CauseShell.tsx b/ui/src/causestarter/shell/CauseShell.tsx index 34bf3c05..3600dbae 100644 --- a/ui/src/causestarter/shell/CauseShell.tsx +++ b/ui/src/causestarter/shell/CauseShell.tsx @@ -19,6 +19,7 @@ import VolunteerActivismOutlinedIcon from '@mui/icons-material/VolunteerActivism import SavingsOutlinedIcon from '@mui/icons-material/SavingsOutlined' import MenuBookOutlinedIcon from '@mui/icons-material/MenuBookOutlined' import GitHubIcon from '@mui/icons-material/GitHub' +import PersonOutlinedIcon from '@mui/icons-material/PersonOutlined' import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined' import { Link, useLocation, useNavigate } from 'react-router-dom' import { WalletButton } from '../../shared/components/WalletButton' @@ -155,6 +156,15 @@ export function CauseShell({ children }: CauseShellProps) { )} + + + import('../../causestarter/pages/HomePage'), 'HomePage')} /> import('../../causestarter/pages/PersonalDashboardPage'), 'PersonalDashboardPage')} /> + import('../../causestarter/pages/ProfilePage'), 'ProfilePage')} /> import('../../delegation/pages/MyNotesPage'), 'DonatePage')} /> } /> import('../../causestarter/pages/StartCauseRedirect'), 'StartCauseRedirect')} /> @@ -77,6 +78,7 @@ export const causestarterManifest: DomainManifest = { { label: 'Docs', path: '/docs' }, ], secondaryNavigation: [ + { label: 'Profile', path: '/profile' }, { label: 'Settings', path: '/settings' }, ], footerText: 'CauseStarter is a lens: it renders a cause you already have a link to. It does not rank or directory causes.', From 8eb96301b5ca9af65ccd74caca67e19ff1bffe6b Mon Sep 17 00:00:00 2001 From: Adam Spitz Date: Tue, 8 Sep 2026 11:08:05 -0400 Subject: [PATCH 3/4] Add focus.md for current work: testnet usability and CauseStarter UI. --- README.md | 1 + focus.md | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 focus.md diff --git a/README.md b/README.md index bf432b46..590b1cf6 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ Commonality is a system for decentralized crowdfunding of public goods: people can fund projects aligned with shared values without needing a central organization to coordinate them. See [What is Commonality?](./specs/README.md#what-is-commonality) for the product overview. + - [Current focus](./focus.md) — the one or two kinds of work we are concentrating on right now (not a to-do list) - [AI continuity notes](./CONTINUITY.md) - [Fake / seed data plan](./fake-data-generation/PLAN.md) — tiny local world vs real Conceptspace statements vs mass fake activity; **next step for a fresh LLM** - [To-do list](./TODO.md) — where LLMs file new one-shot tasks. Tag each with its [autonomy tier](./workflow/task-tiers.md) (Ask / Tell / Trust); untagged means Ask. diff --git a/focus.md b/focus.md new file mode 100644 index 00000000..4897c03a --- /dev/null +++ b/focus.md @@ -0,0 +1,17 @@ +# Current focus + +High-level work we are actually concentrating on right now. Not a to-do list — those live in [TODO.md](./TODO.md), [causestarter/TODO.md](./causestarter/TODO.md), and the [testnet working plan](./workflow/testnet-working-plan.md). At most three items. Keep this file current. + +## 1. Make testnet usable + +The MVP is implemented in code; the live testnet stack is not yet a credible shared lab. Goal: Adam and Sam can use the deployed testnet without fighting ops, config, or broken paths. + +Ordered work is in [`workflow/testnet-working-plan.md`](./workflow/testnet-working-plan.md). Do the next unchecked item there. Mass fake activity is **not** part of this focus. + +Related: [project status](./workflow/project-status.md), [MVP](./specs/product/mvp.md), verifier reports. + +## 2. Finish the CauseStarter UI + +CauseStarter is the founder-first surface and the intended primary entry. Goal: it is complete enough as a product UI that remaining items are known leftovers, not “the app isn’t there yet.” + +Backlog: [`causestarter/TODO.md`](./causestarter/TODO.md). Product/tech domain notes: [product UI domains](./specs/product/ui-domains.md), [technical UI domains](./specs/tech/ui-domains.md). From ba0cba248292954ca708202202a25fa98d99959e Mon Sep 17 00:00:00 2001 From: Adam Spitz Date: Tue, 8 Sep 2026 11:17:19 -0400 Subject: [PATCH 4/4] Fix Fund e2e after landing no longer hosts the dashboard teaser. The personal board lives on /dashboard with personal-dashboard-page, not home-dashboard-board. --- causestarter/README.md | 4 ++-- causestarter/e2e/connect-and-start.spec.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/causestarter/README.md b/causestarter/README.md index 89acc321..24eb1a21 100644 --- a/causestarter/README.md +++ b/causestarter/README.md @@ -305,8 +305,8 @@ Then **restart Grok** so MCP tools load. | `wallet-disconnect` | Disconnect | | `home-landing` | Root landing (first-visit pitch plus role cards) | | `home-dashboard` | Role-card grid on the home landing | -| `home-dashboard-board` | Occupied-home teaser of the personal fundable-projects board | -| `home-dashboard-see-all` | Occupied home → `/dashboard` (full personal list) | +| `home-dashboard-board` | Preview layout of the personal fundable-projects board (unused on home now) | +| `nav-profile` | Header icon → `/profile` | | `personal-dashboard-page` | Full personal fundable-projects board at `/dashboard` | | `nav-start` | Desktop/mobile nav “Start” → same (creates a new draft) | | `cause-detail-page` | Cause page root (where all editing happens; brand-new drafts show “Start a cause board” coach copy here) | diff --git a/causestarter/e2e/connect-and-start.spec.ts b/causestarter/e2e/connect-and-start.spec.ts index 1a0b169f..7b28c5b4 100644 --- a/causestarter/e2e/connect-and-start.spec.ts +++ b/causestarter/e2e/connect-and-start.spec.ts @@ -58,8 +58,8 @@ test.describe('CauseStarter agent smoke', () => { test('personal fundable-projects board lives under Fund after connect', async ({ page }) => { await connectHardhat0(page) await page.getByTestId('nav-fund').click() - await expect(page.getByTestId('home-dashboard-board')).toBeVisible({ timeout: 15_000 }) - await expect(page.getByRole('heading', { name: /your fundable projects/i })).toBeVisible() + await expect(page.getByTestId('personal-dashboard-page')).toBeVisible({ timeout: 15_000 }) + await expect(page.getByRole('heading', { name: /fundable projects/i })).toBeVisible() }) test('starts a cause and lands on its editable page', async ({ page }) => {