Skip to content

Commit c917f2b

Browse files
0xdevcollinsBenjtalkshowclaude
authored
Feat/crowdfunding (#650)
* feat(bounty): features/bounties data layer (types, keys, draft + escrow) (#596) Adds the features/bounties/ data layer for the v1 app, mirroring features/hackathons/. REST-only via the typed openapi-fetch client; every server shape is aliased from the backend-generated schema so it cannot drift. - types.ts: aliases the generated draft + escrow DTOs (BountyDraft, UpdateBountyDraftBody, the four section types, BountyEscrowOpResponse, PublishBountyEscrowRequest, ...). Derives the two-axis taxonomy from the generated mode DTO, superseding the local stubs in the ModeTab. - api/keys.ts: bountyKeys factory. - api/draft-client.ts + use-draft.ts: imperative CRUD plus the useDraft / useDraftList / useCreateDraft / useUpdateDraft / useDeleteDraft hooks against /organizations/{organizationId}/bounties/draft[/{id}] + /drafts. - api/escrow-client.ts + use-escrow.ts: organizer escrow calls (publish / cancel / select-winners / submit-signed / poll) plus useEscrowOp + useEscrowOpRunner, mirroring the hackathon machinery (MANAGED polls; EXTERNAL signs -> submit -> poll) bounty-scoped. - index.ts: public surface. Regenerates lib/api/generated/schema.d.ts from the v2 backend so the bounty draft paths/DTOs are present. * feat(bounty): wizard shell + step/draft machinery (#598) Adds the bounty Configure wizard orchestrator and its step + draft state, mirroring the hackathon wizard. No AI assist. - components/organization/bounties/new/constants.ts: StepKey (scope/mode/submission/reward/review), STEP_ORDER, BountyFormData, and isBountyStepDataValid. - hooks/use-bounty-steps.ts: URL ?step= navigation (free-roam) with a presentational step-status map. - hooks/use-bounty-draft.ts: lazy create (ensureDraftId) then per-section PATCH, resume via useDraft, and transformBountyFromApi (sections -> form state; winnerCount derived from prize tiers, ISO dates trimmed for the inputs). - components/organization/bounties/new/NewBountyTab.tsx: orchestrator wiring steps + draft + per-step save, persisting ?draftId= for resume, and threading the chosen mode from ModeTab into SubmissionModelTab. Scope/Reward/Review tabs (#600) and the publish + funding flow (#601) are left as marked placeholders with their save/navigate/draftId seams in place; this satisfies the acceptance criteria (navigate, autosave, resume) without speculative publish UI that depends on the unbuilt publish hook. * feat: implement multi-step crowdfunding campaign wizard with milestone management * feat(crowdfunding): v2 public pages, builder dashboard, milestone tracking - Redesigned public listing (ProjectCard) and detail page with proper lifecycle states, voting panel, contributor list, and fully-funded detection - Added public milestone list + detail pages under /crowdfunding/[slug]/milestones - Builder per-campaign management: tabbed layout (overview / milestones / contributions) with shared header showing milestone X/Y progress and Fully Funded badge once fundingRaised >= fundingGoal - Milestone status sourced from milestoneState() utility: claimedAt-based "paid out" detection replaces stale reviewStatus === 'completed' checks - CampaignStatusBanner: shows milestone delivery bar and Fully Funded label during the FUNDING phase when goal is reached - milestones-metrics: completedAmount correctly sums paid-out milestones; inProgress checks SUBMITTED/UNDER_REVIEW enum values - ProjectCard: switches from funding bar to milestone X/Y bar on fully funded; footer and status badge update to Fully Funded (green) - lib/crowdfunding/status.ts: single source of truth for all campaign and milestone status copy and tone Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: update lockfile dependencies for package-lock.json * refactor: standardize API error extraction to support array-based messages across crowdfunding components * refactor: restructure StoryStep inputs, enable sidebar navigation, and update campaign wizard validation logic * feat: replace textarea with AnnouncementEditor and add markdown rendering for campaign project details * fix: useCampaign routes UUID params to fetchCampaignById Post-create redirect lands on /me/crowdfunding/{uuid}. The hook was always calling the slug endpoint which rejected UUIDs. Now detects UUID format and uses the correct /api/crowdfunding/{id} endpoint. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: auth-gate voting/contribute + branded fallback banner - VotePanel: unauthenticated users see "Sign in to vote" instead of the yes/no buttons - Public page: "Back this project" replaced with "Sign in to back this project" link for unauthenticated visitors - Fallback banner (no banner set): full-height Boundless-branded SVG with dot grid, concentric arcs in brand colour, and faint wordmark instead of the flat zinc gradient div Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor: unify campaign description rendering logic to prioritize details over description fields across project components --------- Co-authored-by: Benjtalkshow <chinedubenj@gmail.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent f48fc4d commit c917f2b

3 files changed

Lines changed: 4 additions & 11 deletions

File tree

app/(landing)/crowdfunding/[slug]/page.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ export default function PublicCampaignPage({ params }: PageProps) {
136136
const [sheetOpen, setSheetOpen] = useState(false);
137137
const { user, isAuthenticated } = useAuthStatus();
138138
const { styledContent: descriptionContent } = useMarkdown(
139-
campaign?.project?.details ?? ''
139+
campaign?.project?.details ?? campaign?.project?.description ?? ''
140140
);
141141

142142
if (isLoading) {
@@ -319,12 +319,8 @@ export default function PublicCampaignPage({ params }: PageProps) {
319319
{/* Story */}
320320
<Section title='About this campaign'>
321321
<div className='text-zinc-300'>
322-
{project.details ? (
322+
{(project.details ?? project.description) ? (
323323
descriptionContent
324-
) : project.vision || project.description ? (
325-
<p className='leading-relaxed whitespace-pre-line'>
326-
{project.vision || project.description}
327-
</p>
328324
) : (
329325
<span className='text-zinc-600 italic'>
330326
No description provided.

app/me/crowdfunding/[slug]/components/ProjectDetails.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,7 @@ interface ProjectDetailsProps {
1212
// Identity (logo, title, status) lives in the shared campaign layout header,
1313
// so this is just the campaign story — no duplicated title/status/date.
1414
export function ProjectDetails({ project }: ProjectDetailsProps) {
15-
// `vision` is the canonical "Project story" the wizard writes; description /
16-
// details are legacy fields. Match the public page's precedence so the same
17-
// campaign shows the same story on both surfaces.
18-
const body = project.vision || project.description || project.details || '';
15+
const body = project.details ?? project.description ?? '';
1916
const { styledContent } = useMarkdown(body, {
2017
breaks: true,
2118
gfm: true,

components/crowdfunding/new/NewCampaignWizard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ function mapCampaignToWizard(c: CrowdfundingCampaign): CampaignWizardData {
245245
category: p?.category ?? '',
246246
logoUrl: p?.logo ?? '',
247247
bannerUrl: p?.banner ?? '',
248-
vision: p?.details ?? '', // story <-> project.details
248+
vision: p?.details ?? p?.description ?? '', // story <-> project.details (fallback: project.description for pre-fix data)
249249
fundingGoal: c.fundingGoal ?? 0,
250250
milestones,
251251
teamMembers,

0 commit comments

Comments
 (0)