Skip to content

Enhance bounty features with new UI, data layers, and crowdfunding support#663

Merged
0xdevcollins merged 32 commits into
mainfrom
feat/t-replace
Jun 27, 2026
Merged

Enhance bounty features with new UI, data layers, and crowdfunding support#663
0xdevcollins merged 32 commits into
mainfrom
feat/t-replace

Conversation

@0xdevcollins

Copy link
Copy Markdown
Collaborator

No description provided.

0xdevcollins and others added 30 commits June 11, 2026 07:37
…eceipts/send

Winners: new task-first Winners section (pick winner per prize with engine default, inline stacking confirm, deliberate 'Don't award' + unawarded-funds acknowledgement); /rewards redirects to /winners; staged Pick -> Confirm -> Pay.

Judging: slimmed Results tab to read-only standings + 'Go to Winners'; criterionId name fix; AI scorecards, recommendation thresholds, CSV judge import, tracks + custom questions wizard sections; validation + empty-state polish.

Public: private + password access gate, community links, card private badge. Treasury: receipts + Send funds. Removed the stacking toggle from prize setup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…599) (#611)

The net-new bounty UI with no hackathon analog: the two-axis mode picker and
the mode-conditional submission settings that drive all six combinations.

- modeSchema.ts: plain taxonomy unions (entryType x claimType), the
  computeBountyModeLabel helper, and getModeFields — the mode -> field matrix
  derived functionally from the publish gate (validateTwoAxisMode) so it cannot
  drift from the backend.
- submissionModelSchema.ts: makeSubmissionModelSchema(mode), a mode-aware Zod
  factory whose required fields and the >=2 floors (maxApplicants for open
  competition, shortlistSize for application competition) mirror the server gate.
- ModeTab.tsx: entry x claim picker, a winners stepper (1-3) for competitions,
  and a live computed mode label.
- SubmissionModelTab.tsx: renders each field per the matrix (required / optional
  / hidden) and shows submissionVisibility read-only, forced by the mode
  (HIDDEN_UNTIL_DEADLINE for competitions).

Standalone components (props: mode, onSave, onContinue, initialData, isLoading);
they wire into the wizard shell + routes in #597 / #598.
…ow) (#596) (#612)

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): 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.
Completes the editable surface of the bounty Configure wizard, dropping the
three remaining tabs into the seams the shell (#598) left.

- tabs/schemas/scopeSchema.ts: title, description, optional GitHub issue
  url/number, optional project/window.
- tabs/schemas/rewardSchema.ts: makeRewardSchema(claimType) — exactly one tier
  for single claim, 1-3 for a competition; amounts > 0, unique positions
  including position 1 (mirrors the publish gate's deriveWinnerDistribution).
- lib/utils/bounty-escrow.ts: adapts the bounty reward shape to reuse the
  hackathon prize-pool math (getTotalPrizePoolForFunding, buildWinnerDistribution,
  2.5% PLATFORM_FEE) rather than duplicating it.
- tabs/ScopeTab.tsx: scope form.
- tabs/RewardTab.tsx: currency + mode-driven prize tiers (field array sized to
  the winner count) with a live prize-pool / fee / total preview.
- tabs/ReviewTab.tsx: per-section summary, validation summary, funding totals,
  and a publish CTA disabled until every section validates.
- NewBountyTab.tsx + constants.ts: wire the three tabs into the wizard; the form
  snapshot now uses the per-tab form schema types.

Reward currency is collected in the Reward step (the backend section that
persists it) rather than Scope, so it round-trips. The publish action is a
placeholder pending the escrow publish flow (#601); the disabled-until-valid
gate is wired here.
…601) (#615)

* feat(bounty): Scope + Reward + Review tabs and Zod schemas (#600)

Completes the editable surface of the bounty Configure wizard, dropping the
three remaining tabs into the seams the shell (#598) left.

- tabs/schemas/scopeSchema.ts: title, description, optional GitHub issue
  url/number, optional project/window.
- tabs/schemas/rewardSchema.ts: makeRewardSchema(claimType) — exactly one tier
  for single claim, 1-3 for a competition; amounts > 0, unique positions
  including position 1 (mirrors the publish gate's deriveWinnerDistribution).
- lib/utils/bounty-escrow.ts: adapts the bounty reward shape to reuse the
  hackathon prize-pool math (getTotalPrizePoolForFunding, buildWinnerDistribution,
  2.5% PLATFORM_FEE) rather than duplicating it.
- tabs/ScopeTab.tsx: scope form.
- tabs/RewardTab.tsx: currency + mode-driven prize tiers (field array sized to
  the winner count) with a live prize-pool / fee / total preview.
- tabs/ReviewTab.tsx: per-section summary, validation summary, funding totals,
  and a publish CTA disabled until every section validates.
- NewBountyTab.tsx + constants.ts: wire the three tabs into the wizard; the form
  snapshot now uses the per-tab form schema types.

Reward currency is collected in the Reward step (the backend section that
persists it) rather than Scope, so it round-trips. The publish action is a
placeholder pending the escrow publish flow (#601); the disabled-until-valid
gate is wired here.

* feat(bounty): use-bounty-publish (escrow publish via shared runner) (#601)

Wires the bounty publish action, mirroring use-hackathon-publish.

- hooks/use-bounty-publish.ts: builds PublishBountyEscrowDto (ownerAddress,
  tokenAddress via getTokenAddress('USDC'), budget = sum of tier amounts,
  submissionDeadline as unix from the draft deadline, applicationCreditCost,
  winnerDistribution via the shared buildBountyWinnerDistribution, fundingMode)
  and drives it with useEscrowOpRunner: MANAGED returns PENDING_CONFIRM then
  polls; EXTERNAL signs the returned XDR via signXdrWithKit then submits + polls.
  On COMPLETED the bounty has moved draft_awaiting_funding -> open. Pre-flight
  resumes an in-flight op, short-circuits an already-published bounty, and runs a
  MANAGED USDC balance check (pool + 2.5% fee).
- NewBountyTab.tsx: replace the Review placeholder with the real publish() and
  wire isPublishing into the CTA.

Treasury funding (sourceWalletId) is omitted until the backend treasury-parity
issue (boundless-nestjs #314) lands; MANAGED + EXTERNAL are supported. The
funding-source picker + progress modal remain a follow-up; the hook exposes the
escrow phase/error/txHash they need.
Adds the organizer bounty routes, mirroring the hackathon route tree, and the
published-list data needed by the list page.

- app/(landing)/organizations/[id]/bounties/page.tsx: list page with a Drafts
  section (resume links + status badges + mode label / section count) and a
  Published section (status badges + reward), plus a "Host a bounty" CTA.
- .../bounties/new/page.tsx: renders <NewBountyTab organizationId={id} />.
- .../bounties/drafts/[draftId]/page.tsx: renders
  <NewBountyTab organizationId={id} draftId={draftId} />.
- features/bounties/api/core.ts + use-bounties.ts: listOrganizationBounties +
  useOrganizationBounties for the root list (the backend root GET has no
  response DTO, so the row shape is projected to OrganizationBountyListItem).

The pages mount the existing wizard shell (#598); the list separates drafts
(draft / draft_awaiting_funding) from published bounties so they don't
double-list.
Adds a Bounties menu item (/organizations/{orgId}/bounties) and a Post Bounty
quick action (/organizations/{orgId}/bounties/new) to OrganizationSidebar, so
organizers can reach the bounty list + Configure wizard.

Codegen reconcile: re-ran npm run codegen against the v2 backend; the committed
lib/api/generated/schema.d.ts already matches (BountyDraftResponseDto +
UpdateBountyDraftDto present, no drift), so no schema change is needed.
…618)

* feat(bounty): wizard enhancements (resources step, currency, reputation, nav)

- Resources: new optional Resources step (links + PDF/DOC/PPT/MD uploads),
  mirroring the hackathon resources tab; never blocks publish. Reuses a now
  configurable ResourceFileUpload (folder/tags/accepted types props; unique
  input id per row).
- Reward currency: dropdown of USDC (default) / XLM (disabled) with real token
  logos, replacing the free-text field.
- Reputation: per-category minimum floor (development highest); the organizer
  can raise it but not drop below the category baseline.
- Navigation: Back button on every step after the first.
- Scope: category chips + searchable country dropdown with flags + markdown
  description; GitHub issue URL required only for development.
- Publish: redirect to the organizer's bounty list once publish finalizes.
- Dev-only "Fill with mock" button to populate every section at once.

* feat(bounty): hackathon-style org bounty list + delete-draft confirm

- Rebuild the organization bounty list page in the hackathon style: sticky
  header with stats, Published/Drafts tabs, search + sort, and a banner-less
  reward-focused card grid (draft cards show a progress bar + resume link).
- Deleting a draft now opens a confirmation modal (DeleteBountyDraftDialog)
  instead of deleting on the first click.
- Expose `_count.submissions` on the org bounty list item for the cards.
* 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

---------

Co-authored-by: Benjtalkshow <chinedubenj@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* 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

---------

Co-authored-by: Benjtalkshow <chinedubenj@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* 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

---------

Co-authored-by: Benjtalkshow <chinedubenj@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* 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>

---------

Co-authored-by: Benjtalkshow <chinedubenj@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* 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>
Add the builder/participant data layer to features/bounties, mirroring the
hackathon participant scope and reusing the shared escrow runner. The visible
builder UI (marketplace, detail, apply/submit/withdraw, my-bounties) consumes
this.

- participant-escrow-client: on-chain apply/submit/withdraw-application/
  withdraw-submission/contribute + participant op poll + submit-signed
  (the no-org-prefix /api/bounties/{id}/escrow/* routes)
- participant-client: public list/detail, v2 application records
  (apply/edit/withdraw/me), competition join/leave
- use-participant hooks: useBountiesList/useBounty/useMyBountyApplication,
  useApply/Edit/WithdrawApplication, useJoin/LeaveCompetition, and escrow-op
  hooks (useSubmitBounty/useWithdrawSubmission/useContributeToBounty/
  useApplyToBountyEscrow/useWithdrawApplicationEscrow) driven through the
  runner, default MANAGED
- use-escrow: EscrowOpScope is now organizer | participant; the runner routes
  op-poll/submit-signed to the participant URLs
- keys/types/index: participant query keys, type aliases, public exports

MyBountyApplication is hand-typed until boundless-nestjs #331 lands and codegen
runs; useMyBountyActivity is deferred until the participant dashboard (#332).

Closes #621
* feat(bounty): participant data layer (escrow client + hooks)

Add the builder/participant data layer to features/bounties, mirroring the
hackathon participant scope and reusing the shared escrow runner. The visible
builder UI (marketplace, detail, apply/submit/withdraw, my-bounties) consumes
this.

- participant-escrow-client: on-chain apply/submit/withdraw-application/
  withdraw-submission/contribute + participant op poll + submit-signed
  (the no-org-prefix /api/bounties/{id}/escrow/* routes)
- participant-client: public list/detail, v2 application records
  (apply/edit/withdraw/me), competition join/leave
- use-participant hooks: useBountiesList/useBounty/useMyBountyApplication,
  useApply/Edit/WithdrawApplication, useJoin/LeaveCompetition, and escrow-op
  hooks (useSubmitBounty/useWithdrawSubmission/useContributeToBounty/
  useApplyToBountyEscrow/useWithdrawApplicationEscrow) driven through the
  runner, default MANAGED
- use-escrow: EscrowOpScope is now organizer | participant; the runner routes
  op-poll/submit-signed to the participant URLs
- keys/types/index: participant query keys, type aliases, public exports

MyBountyApplication is hand-typed until boundless-nestjs #331 lands and codegen
runs; useMyBountyActivity is deferred until the participant dashboard (#332).

Closes #621

* feat(bounty): public bounty marketplace (discover/list)

Replace the /coming-soon placeholder at /bounties with a real public
marketplace, mirroring the hackathon browse and wired to the participant
data layer (#621).

- route app/(landing)/bounties/page.tsx + bounties metadata entry
- BountiesPage: search + status + mode filters, infinite scroll,
  loading/empty/error states
- BountyCard: mode badge (plain label), status, reward, organization,
  application-window date; links to /bounties/[id]
- BountiesFiltersHeader + colocated useInfiniteBounties (infinite query
  over listBounties)

Status + search filter server-side; mode narrows client-side (the public
list endpoint doesn't filter on mode). Category filter and a card submission
deadline are omitted because BountyPublicDto exposes neither — small backend
follow-ups.

Closes #622
* feat(bounty): participant data layer (escrow client + hooks)

Add the builder/participant data layer to features/bounties, mirroring the
hackathon participant scope and reusing the shared escrow runner. The visible
builder UI (marketplace, detail, apply/submit/withdraw, my-bounties) consumes
this.

- participant-escrow-client: on-chain apply/submit/withdraw-application/
  withdraw-submission/contribute + participant op poll + submit-signed
  (the no-org-prefix /api/bounties/{id}/escrow/* routes)
- participant-client: public list/detail, v2 application records
  (apply/edit/withdraw/me), competition join/leave
- use-participant hooks: useBountiesList/useBounty/useMyBountyApplication,
  useApply/Edit/WithdrawApplication, useJoin/LeaveCompetition, and escrow-op
  hooks (useSubmitBounty/useWithdrawSubmission/useContributeToBounty/
  useApplyToBountyEscrow/useWithdrawApplicationEscrow) driven through the
  runner, default MANAGED
- use-escrow: EscrowOpScope is now organizer | participant; the runner routes
  op-poll/submit-signed to the participant URLs
- keys/types/index: participant query keys, type aliases, public exports

MyBountyApplication is hand-typed until boundless-nestjs #331 lands and codegen
runs; useMyBountyActivity is deferred until the participant dashboard (#332).

Closes #621

* feat(bounty): public bounty marketplace (discover/list)

Replace the /coming-soon placeholder at /bounties with a real public
marketplace, mirroring the hackathon browse and wired to the participant
data layer (#621).

- route app/(landing)/bounties/page.tsx + bounties metadata entry
- BountiesPage: search + status + mode filters, infinite scroll,
  loading/empty/error states
- BountyCard: mode badge (plain label), status, reward, organization,
  application-window date; links to /bounties/[id]
- BountiesFiltersHeader + colocated useInfiniteBounties (infinite query
  over listBounties)

Status + search filter server-side; mode narrows client-side (the public
list endpoint doesn't filter on mode). Category filter and a card submission
deadline are omitted because BountyPublicDto exposes neither — small backend
follow-ups.

Closes #622

* feat(bounty): bounty detail page (/bounties/[id])

The builder hub: shows the bounty, the caller's status, and a mode-aware
entry CTA. Wired to the participant data layer (#621).

- route app/(landing)/bounties/[id]/page.tsx
- BountyDetail: markdown description, prize tiers, mode/status badges,
  organization, reward pool + eligibility sidebar (reputation minimum /
  application window / max applicants / shortlist); loading + not-found
- BountyEntryCta: mode-aware CTA — Claim (open+single), Join (open+
  competition), Apply (application). Gated/explained when not accepting,
  the application window closed, or already applied; shows the caller's
  application status via useMyBountyApplication

The actual entry execution (forms / on-chain op) ships in #624; the CTA is
gated + labeled and hands off to it. applicationCreditCost is not in
BountyPublicDto so it's omitted from the eligibility panel.

Closes #623
…647)

Fill the entry execution the detail-page CTA hands off to (#623), wired to
the participant data layer (#621). Covers all six modes.

- applicationSchema: mode-aware zod validation + body builders (light:
  proposalShort 100-300 words + estimatedDays + <=3 links; full: proposalFull
  500-2000 words + qualifications >=50 chars + <=6 links + optional video)
- BountyApplicationForm: conditional fields per entry type with live word
  count; reused for create + edit
- BountyEntryDialog: mode-aware modal — Apply (POST v2/applications) / Edit
  (PATCH); Join (POST v2/competition/join); Claim (on-chain apply escrow op,
  MANAGED, with progress + reputation pre-check). Wallet-gated.
- BountyEntryCta: opens the dialog and, for an active application, exposes
  Edit + Withdraw (application), Leave (competition), or on-chain Withdraw
  (single claim) behind a two-step confirm

Closes #624
After entry (claim/join/selection) the builder submits their deliverable,
anchored on-chain via the participant escrow runner (#621). Mirrors the
hackathon submission anchor.

- BountySubmitPanel (detail sidebar, shown when eligible: claimed/joined/
  SELECTED/SHORTLISTED): contentUri deliverable input -> submit escrow op
  (useSubmitBounty, MANAGED) with anchor progress (preparing -> anchoring ->
  confirmed) + tx-hash explorer link
- Withdraw submission via useWithdrawSubmission behind a two-step confirm
- Wallet-gated; guarded by entry eligibility

Persistent submission state across reloads is session-local until the #332
my-submission read lands in the schema; the submission-deadline gate is
enforced on-chain (not in BountyPublicDto).

Closes #625
The builder's /me surfaces for bounty applications, submissions, and reward
receipts, consuming the participant dashboard (#332) + earnings (#335).

- data layer: useMyBountyApplications / useMyBountySubmissions (the deferred
  useMyBountyActivity) via the legacy axios api (hand-typed rows, retry:false,
  degrading to empty until #332 lands in the schema)
- /me/bounties page: Applications + Submissions tabs with status/progress
  badges; won submissions show the reward amount + a payout tx-hash link;
  rows link to the detail page
- sidebar: "My Bounties" nav entry
- /me/earnings: the page already renders breakdown.bounties + activities
  generically (so #335 surfaces them); add a "View tx" link to reward
  activities (txHash cast until codegen)

Closes #626
GitHub's built-in Closes/Fixes/Resolves auto-close only fires for PRs
merged into the default branch. Feature PRs here merge into integration
branches (feat/t-replace, etc.), leaving linked issues open. This adds a
pull_request_target workflow that parses the merged PR body and closes
the referenced same-repo issues on any base branch.
…627) (#653)

The participant gap endpoints (applications/me, my-activity, results) have
landed in v2, so regenerate the OpenAPI schema and replace the two
hand-typed casts (MyBountyApplication, my-activity dashboard rows) with
their generated DTO aliases. Refresh openapi.snapshot.json so offline/CI
codegen matches. Nav entries (marketplace + /me sidebar) already shipped.
0xdevcollins and others added 2 commits June 25, 2026 11:05
…quirements (#662)

* feat(bounty): submission metadata types, category filter, and wizard requirement toggles

Refresh the generated OpenAPI schema for the new public-bounty fields
(claimedBy, category, submissionDeadline, submissionRequirements) and the
submit-op metadata. Add the category query param to the marketplace list
client, the per-bounty my-submission read + hook, and the organizer
requirement toggles in the Configure wizard's submission step. Flatten the
category reputation baseline to 0 for testing and let the wizard accept a
0 floor.

* feat(bounty): marketplace card redesign, category tabs, and detail display

Bounty cards now show the organizer logo, category, a live due-date
countdown, and the USDC token logo (title only, no markdown bleed). Add a
category tab bar to the marketplace and mirror the same details on the
detail page, plus a mode-explainer tooltip. Single-claim bounties render a
'Claimed by' card linking to the claimant profile, and the application
withdraw button is gated while a submission is anchored.

* feat(bounty): dedicated submit page with metadata and media upload

Replace the single-input submit panel with a /bounties/[id]/submit page:
primary submission link plus optional documentation, tweet, and demo video
links and a drag-and-drop media uploader. Required fields follow the
bounty's organizer-configured requirements; on success it returns to the
detail page. The sidebar panel now routes here (new tab) and reflects the
submitted/withdraw state.
@vercel

vercel Bot commented Jun 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
boundless Ready Ready Preview, Comment Jun 27, 2026 7:51pm
boundless-kd16 Ready Ready Preview, Comment Jun 27, 2026 7:51pm

@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 322 files, which is 172 over the limit of 150.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 98f4d66e-837d-4503-994c-1755bef71e23

📥 Commits

Reviewing files that changed from the base of the PR and between 2cb7127 and 1a8b26c.

⛔ Files ignored due to path filters (2)
  • lib/api/generated/schema.d.ts is excluded by !**/generated/**
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (322)
  • .claude/launch.json
  • .env.example
  • .github/workflows/close-linked-issues.yml
  • .husky/pre-push
  • app/(landing)/bounties/[id]/page.tsx
  • app/(landing)/bounties/[id]/submit/page.tsx
  • app/(landing)/bounties/page.tsx
  • app/(landing)/crowdfunding/[slug]/milestones/[id]/page.tsx
  • app/(landing)/crowdfunding/[slug]/milestones/page.tsx
  • app/(landing)/crowdfunding/[slug]/page.tsx
  • app/(landing)/crowdfunding/new/page.tsx
  • app/(landing)/crowdfunding/page.tsx
  • app/(landing)/hackathons/[slug]/components/AccessGate.tsx
  • app/(landing)/hackathons/[slug]/components/RegistrationQuestionsDialog.tsx
  • app/(landing)/hackathons/[slug]/components/SponsorsSection.tsx
  • app/(landing)/hackathons/[slug]/components/header/ActionButtons.tsx
  • app/(landing)/hackathons/[slug]/components/sidebar/CommunityLinks.tsx
  • app/(landing)/hackathons/[slug]/components/sidebar/MySubmissionPanel.tsx
  • app/(landing)/hackathons/[slug]/components/sidebar/PoolAndAction.tsx
  • app/(landing)/hackathons/[slug]/components/sidebar/index.tsx
  • app/(landing)/hackathons/[slug]/components/tabs/contents/Overview.tsx
  • app/(landing)/hackathons/[slug]/components/tabs/contents/index.tsx
  • app/(landing)/hackathons/[slug]/components/tabs/contents/submissions/SubmissionCard.tsx
  • app/(landing)/hackathons/[slug]/page.tsx
  • app/(landing)/hackathons/[slug]/submit/page.tsx
  • app/(landing)/hackathons/layout.tsx
  • app/(landing)/hackathons/preview/[orgId]/[draftId]/page.tsx
  • app/(landing)/organizations/[id]/bounties/drafts/[draftId]/page.tsx
  • app/(landing)/organizations/[id]/bounties/new/page.tsx
  • app/(landing)/organizations/[id]/bounties/page.tsx
  • app/(landing)/organizations/[id]/hackathons/[hackathonId]/judging/page.tsx
  • app/(landing)/organizations/[id]/hackathons/[hackathonId]/page.tsx
  • app/(landing)/organizations/[id]/hackathons/[hackathonId]/participants/page.tsx
  • app/(landing)/organizations/[id]/hackathons/[hackathonId]/rewards/page.tsx
  • app/(landing)/organizations/[id]/hackathons/[hackathonId]/settings/page.tsx
  • app/(landing)/organizations/[id]/hackathons/[hackathonId]/winners/page.tsx
  • app/(landing)/organizations/[id]/hackathons/drafts/[draftId]/page.tsx
  • app/(landing)/organizations/[id]/hackathons/new/page.tsx
  • app/(landing)/organizations/[id]/hackathons/page.tsx
  • app/(landing)/organizations/[id]/treasury/page.tsx
  • app/(landing)/organizations/[id]/treasury/receipts/[receiptId]/page.tsx
  • app/(landing)/organizations/layout.tsx
  • app/(landing)/projects/[slug]/page.tsx
  • app/judge/[hackathonId]/submissions/[submissionId]/page.tsx
  • app/layout.tsx
  • app/me/bounties/page.tsx
  • app/me/crowdfunding/[slug]/components/CampaignBanner.tsx
  • app/me/crowdfunding/[slug]/components/CampaignTabs.tsx
  • app/me/crowdfunding/[slug]/components/FundingProgress.tsx
  • app/me/crowdfunding/[slug]/components/ProjectDetails.tsx
  • app/me/crowdfunding/[slug]/components/index.ts
  • app/me/crowdfunding/[slug]/contributions/page.tsx
  • app/me/crowdfunding/[slug]/edit/components/BasicInfoSection.tsx
  • app/me/crowdfunding/[slug]/edit/components/ContactSocialSection.old.tsx
  • app/me/crowdfunding/[slug]/edit/components/ContactSocialSection.tsx
  • app/me/crowdfunding/[slug]/edit/components/DetailsFundingSection.tsx
  • app/me/crowdfunding/[slug]/edit/components/MilestonesSection.tsx
  • app/me/crowdfunding/[slug]/edit/components/ProjectLinksSection.tsx
  • app/me/crowdfunding/[slug]/edit/components/RepoLinksSection.tsx
  • app/me/crowdfunding/[slug]/edit/components/TeamSection.tsx
  • app/me/crowdfunding/[slug]/edit/components/index.ts
  • app/me/crowdfunding/[slug]/edit/components/md-editor-custom.css
  • app/me/crowdfunding/[slug]/edit/page.tsx
  • app/me/crowdfunding/[slug]/layout.tsx
  • app/me/crowdfunding/[slug]/milestones/[milestoneIndex]/page.tsx
  • app/me/crowdfunding/[slug]/milestones/page.tsx
  • app/me/crowdfunding/[slug]/page.tsx
  • app/me/crowdfunding/new/page.tsx
  • app/me/crowdfunding/page.tsx
  • app/me/earnings/page.tsx
  • app/me/layout.tsx
  • app/partners/contribute/[token]/page.tsx
  • app/providers.tsx
  • components/app-sidebar.tsx
  • components/auth/LoginWrapper.tsx
  • components/auth/OtpForm.tsx
  • components/bounties/DueCountdown.tsx
  • components/bounties/detail/BountyDetail.tsx
  • components/bounties/detail/BountyEntryCta.tsx
  • components/bounties/detail/entry/BountyApplicationForm.tsx
  • components/bounties/detail/entry/BountyEntryDialog.tsx
  • components/bounties/detail/entry/applicationSchema.ts
  • components/bounties/detail/submit/BountySubmitForm.tsx
  • components/bounties/detail/submit/BountySubmitGate.tsx
  • components/bounties/detail/submit/BountySubmitPanel.tsx
  • components/bounties/marketplace/BountiesFiltersHeader.tsx
  • components/bounties/marketplace/BountiesPage.tsx
  • components/bounties/marketplace/BountyCard.tsx
  • components/bounties/marketplace/CategoryTabs.tsx
  • components/bounties/marketplace/use-bounties-marketplace.ts
  • components/crowdfunding-table-columns.tsx
  • components/crowdfunding-table-toolbar.tsx
  • components/crowdfunding/CampaignStatusBanner.tsx
  • components/crowdfunding/ContributeSheet.tsx
  • components/crowdfunding/MilestoneSubmitForm.tsx
  • components/crowdfunding/VotePanel.tsx
  • components/crowdfunding/campaign-funding-tab.tsx
  • components/crowdfunding/campaign-milestones-tab.tsx
  • components/crowdfunding/campaign-stats.tsx
  • components/crowdfunding/milestone-card.tsx
  • components/crowdfunding/milestone-detail-header.tsx
  • components/crowdfunding/milestone-detail-info.tsx
  • components/crowdfunding/milestones-metrics.tsx
  • components/crowdfunding/new/NewCampaignSidebar.tsx
  • components/crowdfunding/new/NewCampaignWizard.tsx
  • components/crowdfunding/new/WizardHelpButton.tsx
  • components/crowdfunding/new/constants.ts
  • components/crowdfunding/new/fee.ts
  • components/crowdfunding/new/milestone-templates.ts
  • components/crowdfunding/new/steps/BasicsStep.tsx
  • components/crowdfunding/new/steps/FundingStep.tsx
  • components/crowdfunding/new/steps/LinksStep.tsx
  • components/crowdfunding/new/steps/MilestonesStep.tsx
  • components/crowdfunding/new/steps/ReviewStep.tsx
  • components/crowdfunding/new/steps/StoryStep.tsx
  • components/crowdfunding/new/steps/TeamStep.tsx
  • components/crowdfunding/quick-links.tsx
  • components/crowdfunding/submit-evidence-modal.tsx
  • components/hackathons/HackathonsPage.tsx
  • components/hackathons/overview/RegisterHackathonModal.tsx
  • components/hackathons/submissions/SubmissionAnchorProgress.tsx
  • components/hackathons/submissions/SubmissionForm.tsx
  • components/judge/JudgeAiAssist.tsx
  • components/landing-page/hackathon/HackathonCard.tsx
  • components/landing-page/navbar.tsx
  • components/me-dashboard.tsx
  • components/me/bounties/MyBountiesPage.tsx
  • components/organization/OrganizationSettings.tsx
  • components/organization/OrganizationSidebar.tsx
  • components/organization/bounties/DeleteBountyDraftDialog.tsx
  • components/organization/bounties/new/NewBountyTab.tsx
  • components/organization/bounties/new/constants.ts
  • components/organization/bounties/new/mock-data.ts
  • components/organization/bounties/new/tabs/ModeTab.tsx
  • components/organization/bounties/new/tabs/ResourcesTab.tsx
  • components/organization/bounties/new/tabs/ReviewTab.tsx
  • components/organization/bounties/new/tabs/RewardTab.tsx
  • components/organization/bounties/new/tabs/ScopeTab.tsx
  • components/organization/bounties/new/tabs/SubmissionModelTab.tsx
  • components/organization/bounties/new/tabs/schemas/modeSchema.ts
  • components/organization/bounties/new/tabs/schemas/resourcesSchema.ts
  • components/organization/bounties/new/tabs/schemas/rewardSchema.ts
  • components/organization/bounties/new/tabs/schemas/scopeSchema.ts
  • components/organization/bounties/new/tabs/schemas/submissionModelSchema.ts
  • components/organization/cards/ReviewSubmissionModal/SubmissionModalHeader.tsx
  • components/organization/hackathons/HackathonPublishStatusBanner.tsx
  • components/organization/hackathons/ParticipantsGrid.tsx
  • components/organization/hackathons/ParticipantsTable.tsx
  • components/organization/hackathons/details/HackathonSidebar.tsx
  • components/organization/hackathons/judging/AiScorecardsPanel.tsx
  • components/organization/hackathons/judging/AllocationPreviewCard.tsx
  • components/organization/hackathons/judging/ImportJudgesCsvDialog.tsx
  • components/organization/hackathons/judging/JudgingResultsTable.tsx
  • components/organization/hackathons/judging/OrganizerJudgesPanel.tsx
  • components/organization/hackathons/judging/RecommendationThresholdsCard.tsx
  • components/organization/hackathons/new/FundingConfirmationModal.tsx
  • components/organization/hackathons/new/FundingProgressModal.tsx
  • components/organization/hackathons/new/GenerateWithAiDialog.tsx
  • components/organization/hackathons/new/HackathonTabsNavigation.tsx
  • components/organization/hackathons/new/NewHackathonTab.tsx
  • components/organization/hackathons/new/PrePublishAnnounceDialog.tsx
  • components/organization/hackathons/new/RegenerateSectionButton.tsx
  • components/organization/hackathons/new/constants.ts
  • components/organization/hackathons/new/tabs/CustomQuestionsTab.tsx
  • components/organization/hackathons/new/tabs/InfoTab.tsx
  • components/organization/hackathons/new/tabs/JudgingTab.tsx
  • components/organization/hackathons/new/tabs/ReviewTab.tsx
  • components/organization/hackathons/new/tabs/RewardsTab.tsx
  • components/organization/hackathons/new/tabs/TimelineTab.tsx
  • components/organization/hackathons/new/tabs/TracksTab.tsx
  • components/organization/hackathons/new/tabs/components/CategorySelection.tsx
  • components/organization/hackathons/new/tabs/components/ResourceFileUpload.tsx
  • components/organization/hackathons/new/tabs/components/review/EscrowSummary.tsx
  • components/organization/hackathons/new/tabs/components/review/HackathonPublishedModal.tsx
  • components/organization/hackathons/new/tabs/components/review/PublishSection.tsx
  • components/organization/hackathons/new/tabs/components/review/WalletConnectionWarning.tsx
  • components/organization/hackathons/new/tabs/schemas/rewardsSchema.ts
  • components/organization/hackathons/rewards/CreateMilestonesDialog.tsx
  • components/organization/hackathons/rewards/EscrowStatusCard.tsx
  • components/organization/hackathons/rewards/PublishWinnersWizard.tsx
  • components/organization/hackathons/rewards/RewardDistributionStatusBanner.tsx
  • components/organization/hackathons/rewards/RewardPayoutProgressModal.tsx
  • components/organization/hackathons/rewards/RewardsPageContent.tsx
  • components/organization/hackathons/rewards/RewardsPageHeader.tsx
  • components/organization/hackathons/rewards/SubmissionListItem.tsx
  • components/organization/hackathons/rewards/SubmissionsList.tsx
  • components/organization/hackathons/rewards/WinnerFormItem.tsx
  • components/organization/hackathons/rewards/WinnersBoard.tsx
  • components/organization/hackathons/settings/AdvancedSettingsTab.tsx
  • components/organization/hackathons/settings/AllocateContributionModal.tsx
  • components/organization/hackathons/settings/PartnersSettingsTab.tsx
  • components/organization/hackathons/settings/RewardsSettingsTab.tsx
  • components/organization/hackathons/settings/TracksSettingsTab.tsx
  • components/organization/tabs/MembersTab.tsx
  • components/organization/tabs/MembersTab/MemberCard.tsx
  • components/organization/tabs/MembersTab/PermissionsTable.tsx
  • components/organization/treasury/AuditLog.tsx
  • components/organization/treasury/Receipts.tsx
  • components/organization/treasury/SendFunds.tsx
  • components/organization/treasury/WalletsSection.tsx
  • components/project-details/project-sidebar/ProjectSidebarLinks.tsx
  • components/providers/auth-provider.tsx
  • features/bounties/api/core.ts
  • features/bounties/api/draft-client.ts
  • features/bounties/api/escrow-client.ts
  • features/bounties/api/keys.ts
  • features/bounties/api/participant-client.ts
  • features/bounties/api/participant-dashboard-client.ts
  • features/bounties/api/participant-escrow-client.ts
  • features/bounties/api/use-bounties.ts
  • features/bounties/api/use-draft.ts
  • features/bounties/api/use-escrow.ts
  • features/bounties/api/use-participant-dashboard.ts
  • features/bounties/api/use-participant.ts
  • features/bounties/index.ts
  • features/bounties/types.ts
  • features/crowdfunding/api/campaign-client.ts
  • features/crowdfunding/api/keys.ts
  • features/crowdfunding/api/milestone-client.ts
  • features/crowdfunding/api/use-campaign.ts
  • features/crowdfunding/api/use-milestone.ts
  • features/crowdfunding/components/CrowdfundingExplore.tsx
  • features/crowdfunding/components/CrowdfundingPageHero.tsx
  • features/crowdfunding/index.ts
  • features/crowdfunding/types.ts
  • features/hackathons/api/draft-client.ts
  • features/hackathons/api/escrow-client.ts
  • features/hackathons/api/keys.ts
  • features/hackathons/api/use-brief-templates.ts
  • features/hackathons/api/use-draft.ts
  • features/hackathons/api/use-escrow.ts
  • features/hackathons/api/use-generate-from-brief.ts
  • features/hackathons/api/use-regenerate-section.ts
  • features/hackathons/api/use-submission-anchor.ts
  • features/hackathons/api/use-submission.ts
  • features/hackathons/index.ts
  • features/hackathons/types.ts
  • features/projects/api/index.ts
  • features/projects/components/ProjectCard.tsx
  • features/projects/types/index.ts
  • features/treasury/api.ts
  • features/treasury/index.ts
  • features/treasury/keys.ts
  • features/treasury/types.ts
  • features/treasury/use-treasury-audit.ts
  • features/treasury/use-treasury-policy.ts
  • features/treasury/use-treasury-receipts.ts
  • features/treasury/use-treasury-spend.ts
  • features/treasury/use-treasury-wallets.ts
  • hooks/hackathon/use-cancel-hackathon.ts
  • hooks/hackathon/use-delete-hackathon.ts
  • hooks/hackathon/use-hackathon-queries.ts
  • hooks/hackathon/use-hackathons-list.ts
  • hooks/hackathon/use-register-hackathon.ts
  • hooks/hackathon/use-team-posts.ts
  • hooks/use-auth.ts
  • hooks/use-bounty-draft.ts
  • hooks/use-bounty-publish.ts
  • hooks/use-bounty-steps.ts
  • hooks/use-follow.ts
  • hooks/use-hackathon-draft.ts
  • hooks/use-hackathon-publish.ts
  • hooks/use-hackathon-rewards.ts
  • hooks/use-hackathon-step-save.ts
  • hooks/use-hackathon-steps.ts
  • hooks/use-hackathons.ts
  • hooks/use-publish-winners.ts
  • hooks/use-rank-assignment.ts
  • hooks/use-reward-distribution-status.ts
  • hooks/use-winners-board.ts
  • hooks/use-wizard-steps.ts
  • instrumentation-client.ts
  • instrumentation.ts
  • lib/api/api.ts
  • lib/api/auth.ts
  • lib/api/client.ts
  • lib/api/hackathon.ts
  • lib/api/hackathons.ts
  • lib/api/hackathons/custom-questions.ts
  • lib/api/hackathons/draft.ts
  • lib/api/hackathons/index.ts
  • lib/api/hackathons/judging.ts
  • lib/api/hackathons/participants.ts
  • lib/api/hackathons/partners.ts
  • lib/api/hackathons/rewards.ts
  • lib/api/hackathons/tracks.ts
  • lib/api/hackathons/winners.ts
  • lib/api/index.ts
  • lib/api/judge.ts
  • lib/api/openapi.ts
  • lib/api/organization.ts
  • lib/api/types.ts
  • lib/api/user/earnings.ts
  • lib/api/wallet.ts
  • lib/auth/logger.ts
  • lib/config/tokens.ts
  • lib/config/wallet-kit.ts
  • lib/crowdfunding/status.ts
  • lib/error-reporting.ts
  • lib/metadata.ts
  • lib/providers/OrganizationProvider.tsx
  • lib/providers/hackathonProvider.tsx
  • lib/stores/auth-store.ts
  • lib/utils/bounty-escrow.ts
  • lib/utils/effective-prize-tiers.ts
  • lib/utils/hackathon-escrow.ts
  • lib/utils/hackathon-form-transforms.ts
  • lib/utils/hackathon-step-validation.ts
  • lib/utils/hackathon-winner-distribution.ts
  • lib/utils/prize-tier-matcher.ts
  • lib/utils/publish-op-storage.ts
  • lib/wallet/wallet-kit.ts
  • next.config.ts
  • openapi.snapshot.json
  • package.json
  • sentry.edge.config.ts
  • sentry.server.config.ts
  • types/earnings.ts
  • types/hackathon/core.ts
  • types/hackathon/draft.ts
  • types/hackathon/index.ts
  • types/hackathon/rewards.ts

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/t-replace

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@0xdevcollins 0xdevcollins merged commit ef423a2 into main Jun 27, 2026
6 of 10 checks passed
0xdevcollins added a commit that referenced this pull request Jun 27, 2026
* fix(hackathons): single-column teams tab and primary-colored pager (#562)

* fix(hackathons): single-column teams tab and primary-colored pager

Revert the teams tab grid to a single column and rework the shared
Pagination component to match the icon-chevron layout used by the
organizer submissions and participants pages, styled with the primary
color.

* feat(submissions): link submission card avatars to profile pages

Wrap the individual avatar on SubmissionCard in a profile link and
forward team-member usernames to GroupAvatar so each clustered avatar
opens that user's profile in a new tab.

* feat(hackathons): track-based prize structure + submission polish + tracks UI (#564)

* feat(hackathons): add "hidden until results" submission visibility mode

Surfaces the new HIDDEN_UNTIL_RESULTS option (added in the nestjs PR) in
the organizer settings tab. Reorders the three visibility options so the
recommended "Shortlisted only" leads, the new "Hidden until results are
announced" sits in the middle, and "All submissions" comes last. Rewrites
the copy on the "All submissions" choice that incorrectly claimed
disqualified projects would be shown -- they never were on the backend,
and Phase 2 makes that an explicit guarantee. Aligns the form's default
and API-fallback value with the backend default (ACCEPTED_SHORTLISTED,
not ALL) so organizers don't see a misleading initial selection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(hackathons): track-based prize structure, submission polish, tracks UI

Wires the frontend for the new track-based prize flow:

- New TracksSettingsTab with full CRUD: name/slug/description/eligibility/
  prompt/customQuestions/requiredArtifacts; per-row bulk-opt-in action with
  confirmation dialog for retrofitting existing submissions
- RewardsTab gains a 3-card prize structure picker, per-tier kind toggle and
  track dropdown, amber "tracks unbound" banner, and an inline Manage Tracks
  dialog embedding the settings table
- SubmissionForm: track picker + per-track answers (prompt / custom
  questions / required artifacts), tagline, builtWith chips, screenshots,
  license, code attestation, with soft compliance gate for already-submitted
  submissions. trackIds hydrate from trackEntries on edit so bulk-opted-in
  submitters don't strip themselves out
- SubmissionDetailModal renders tagline, screenshots, built-with, license
  badge, and per-track answers
- Public hackathon page: Overview splits prizes into Overall/Track sections;
  sidebar tier list shows TRACK prefix and looks up track names; Winners tab
  gets a Track Winners section with per-track cards
- API client: lib/api/hackathons/tracks.ts with listTracks /
  listOrganizerTracks / createTrack / updateTrack / deleteTrack /
  bulkOptInAllSubmissions, plus types for HackathonTrack,
  TrackCustomQuestion, TrackRequiredArtifact, TrackAnswer,
  SubmissionTrackEntry, BulkOptInResult
- Hackathon provider/hooks expose trackWinners and per-track entries

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Feat/submission visibility hidden until results (#566)

* feat(hackathons): add "hidden until results" submission visibility mode

Surfaces the new HIDDEN_UNTIL_RESULTS option (added in the nestjs PR) in
the organizer settings tab. Reorders the three visibility options so the
recommended "Shortlisted only" leads, the new "Hidden until results are
announced" sits in the middle, and "All submissions" comes last. Rewrites
the copy on the "All submissions" choice that incorrectly claimed
disqualified projects would be shown -- they never were on the backend,
and Phase 2 makes that an explicit guarantee. Aligns the form's default
and API-fallback value with the backend default (ACCEPTED_SHORTLISTED,
not ALL) so organizers don't see a misleading initial selection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(hackathons): track-based prize structure, submission polish, tracks UI

Wires the frontend for the new track-based prize flow:

- New TracksSettingsTab with full CRUD: name/slug/description/eligibility/
  prompt/customQuestions/requiredArtifacts; per-row bulk-opt-in action with
  confirmation dialog for retrofitting existing submissions
- RewardsTab gains a 3-card prize structure picker, per-tier kind toggle and
  track dropdown, amber "tracks unbound" banner, and an inline Manage Tracks
  dialog embedding the settings table
- SubmissionForm: track picker + per-track answers (prompt / custom
  questions / required artifacts), tagline, builtWith chips, screenshots,
  license, code attestation, with soft compliance gate for already-submitted
  submissions. trackIds hydrate from trackEntries on edit so bulk-opted-in
  submitters don't strip themselves out
- SubmissionDetailModal renders tagline, screenshots, built-with, license
  badge, and per-track answers
- Public hackathon page: Overview splits prizes into Overall/Track sections;
  sidebar tier list shows TRACK prefix and looks up track names; Winners tab
  gets a Track Winners section with per-track cards
- API client: lib/api/hackathons/tracks.ts with listTracks /
  listOrganizerTracks / createTrack / updateTrack / deleteTrack /
  bulkOptInAllSubmissions, plus types for HackathonTrack,
  TrackCustomQuestion, TrackRequiredArtifact, TrackAnswer,
  SubmissionTrackEntry, BulkOptInResult
- Hackathon provider/hooks expose trackWinners and per-track entries

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(submissions): tighten Zod schema + surface backend debug info

- Add max constraints that previously only existed on the backend DTO so
  validation fires inline (projectName 100, description 5000, URL 500).
- ApiErrorField gains an optional `debug` field that the backend Prisma
  filter populates outside production.
- useSubmission's error formatter prefers `debug` over the generic field
  message when present, so toasts show the real Prisma reason behind
  "Data validation failed" instead of a blank "validation: …" line.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(submit-page): hydrate Phase A fields + stop wiping user input on re-render

The submit page mapped `initialData` inline on every render, which (a)
recreated the object reference on every render so the form's reset
effect fired continuously and wiped values the user was typing, and (b)
dropped the Phase A fields entirely (tagline, builtWith, screenshots,
license, codeAttestedAt) plus trackEntries. The combined effect was the
documented symptom — only logo and videoUrl survived the save because
those round-tripped through the type-narrowed object literal, while
tagline / builtWith / license kept appearing to "switch to empty".

- Memoize `initialData` against `mySubmission` so the reference only
  changes when the underlying submission actually changes.
- Pass through Phase A fields and trackEntries so the form can hydrate
  the saved values, and so a follow-up save doesn't write back empties.
- Widen SubmissionFormContent's `initialData` prop to accept the raw
  server-side extras (trackEntries, codeAttestedAt) that the form
  already consumes via cast — keeps the parent's hydration explicit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(hackathons): always open submissions in a new tab (#568)

* fix(hackathons): single-column teams tab and primary-colored pager

Revert the teams tab grid to a single column and rework the shared
Pagination component to match the icon-chevron layout used by the
organizer submissions and participants pages, styled with the primary
color.

* feat(submissions): link submission card avatars to profile pages

Wrap the individual avatar on SubmissionCard in a profile link and
forward team-member usernames to GroupAvatar so each clustered avatar
opens that user's profile in a new tab.

* fix(hackathons): always open submissions in a new tab

Across the hackathon, organizer, judge, and profile surfaces, clicking
a submission now opens the project page in a new tab so reviewers and
participants do not lose their list/queue context. Switched the
remaining anchor tags to next/link Link components.

* feat(judging): organizer dashboard — coverage, preview, per-track results (#570) (#572)

Pairs with boundless-nestjs feat(judging) organizer dashboard upgrades.
Adds three new components to the organizer judging page; no changes to
existing components, judges, or scoring flows.

- `CoverageMatrix` — heatmap on the Overview tab. Rows are
  submissions, columns are judges. Surfaces idle judges (mostly-empty
  columns) and orphan submissions (rows with 0-1 scores) at a glance —
  both block a defensible publish.

- `AllocationPreviewCard` — sits above the Publish button on the
  Results tab. Read-only allocator dry-run showing overall placements
  + per-track winners exactly as publish-results would commit. Calls
  out EXCLUSIVE stacking effects (track leader losing to overall),
  plus surfaces publish gates (deadline, completeness, partner
  allocation) so the organizer sees blockers without attempting to
  publish.

- `TrackResultsSection` — per-track collapsible standings on the
  Results tab. Each section is scoped to a track's opt-ins, sorted by
  averageScore, with the bound prize tier shown as a chip. The leader
  is highlighted as the current pick — soft preview only; the
  Allocation Preview above shows the authoritative EXCLUSIVE-stacked
  outcome.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(judging): unallocated partner funds become a warning, not a blocker (#574)

Mirrors the backend relaxation. The allocator preview's
"Ready to publish" badge previously turned amber whenever any partner
contribution had unallocated balance — but the backend gate that
backed that signal has been relaxed (the funds stay in escrow
post-publish; they're not lost).

Split the existing publish-readiness messaging into two lists:

- Blockers — the hard gates (deadline, completeness, no reviews).
  Same red treatment, same ready-to-publish badge logic.
- Warnings — informational, never blocks. Renders in blue, calls out
  the unallocated amount with a note that it remains in escrow.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(rewards): show track winners on the organizer rewards page (#576)

The rewards page was filtering winners by `submission.rank` only, so
on the track-based prize structure it silently dropped every track
winner. For the Boundless × Trustless Work hackathon (3 overall + 5
track tiers), the page rendered 3 of 8 winners and the publish wizard
preview showed 3 of 8 prize tiers paired.

Same root cause as boundlessfi/boundless-nestjs#132: track winners
live on `SubmissionTrackEntry.wonRank`, not on `submission.rank`, and
the rewards UI was rank-keyed end to end.

Changes:

- Extend `Submission` with optional track-winner fields (`isTrackWinner`,
  `trackId`, `trackName`, `trackPrize`, `trackWonRank`).
- `useHackathonRewards` now fetches `getHackathonWinners` in a
  dedicated effect that runs once results are published. The
  `trackWinners` payload is stamped onto matching submissions AND
  returned raw so the page can render a per-track section.
- `useHackathonRewards` also preserves `tier.kind` and `tier.trackId`
  on the mapped `prizeTiers` and re-sorts so track tiers no longer
  collapse to the 999 fallback rank.
- `rewards/page.tsx` winners filter now ORs `s.isTrackWinner` with the
  rank-based check, and `maxRank` counts only OVERALL tiers so the
  rank-keyed lookups don't get polluted with synthetic track ranks.
- New `TrackWinnersSection` component renders below the existing
  rank-based `PodiumSection`. Mirrors the public WinnersTab pattern:
  one card per track with prize chip, project name, team, avatars.
- `PublishWinnersWizard` includes track winners in its preview list
  and threads `kind`/`trackId` through to `WinnersGrid`.
- `WinnersGrid` now renders overall + track tiers as separate
  sections. Overall keeps the 2-1-3 podium re-order; track tiers
  render in display order. Each tier is matched to its winner via
  rank OR trackId.

Pairs with boundless-nestjs#132 (BE trigger endpoint already
respects track winners after that merged).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(rewards): polish publish-wizard preview to industry-standard layout (#580)

The wizard preview's "3/8 Winners Assigned" line plus the existing
WinnerCard layout was confusing — "$300 USDC" double-signed the
currency, "0 Comments" was meaningless in a payout preview, and track
winners got a "4th Position" ribbon they didn't earn.

Redesigned to match the patterns Devpost / Devfolio / ETHGlobal use
in their publish flows:

WinnersGrid header:
- Replace the cryptic "X/Y Winners Assigned" with a callout chip:
  green check + "All N winners assigned" when complete, amber
  warning + "X of Y assigned (Z unassigned)" when not.
- Show the total prize pool ("1,500 USDC pool") as a sibling chip so
  the organizer sees the dollar figure they're committing.
- Render "Overall Placements" as a sub-header only when track
  winners also exist (avoids redundant heading on OVERALL_ONLY).

WinnerCard:
- Drop the dollar sign for non-dollar currencies — `"$300 USDC"` is
  now `"300 USDC"`, a cleaner industry-standard read.
- Track winners get a track-name chip ("Best UI/UX") instead of a
  synthetic-rank ribbon ("4th Position").
- Drop the "0 Comments" noise; not a meaningful signal at payout time.
- Drop the placeholder bitmed.png; surface project name + participant
  name + category as the primary content.
- Cards now have consistent dimensions (no podium scaling for tracks),
  uniform border + hover treatment, prize chip aligned to the right.

Only the publish-wizard preview is touched; the public Winners tab
and the rewards podium are unchanged.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(hackathons): add judging dataset to export dropdown (#577)

Surface a new "Judging" entry alongside the existing export options
(Winners, Submissions, etc.) so organizers can download judging
criteria, judges, aggregated results, per-judge scores, and judge
comments. Requires the matching backend dataset to be deployed.

* fix(rewards): match track winners by submissionId, not participantId (#582)

The publish-wizard preview was still showing "3 of 8 winners assigned"
even after the track-winner enrichment landed in #576. The
enrichment effect was looking up trackWinners by `sub.id`, but the
rewards data mapper sets `Submission.id` to the participant ID, not
the submission row ID. The Map lookup keyed by
`HackathonTrackWinner.submissionId` never matched, so no submissions
got `isTrackWinner = true` stamped.

For the Boundless × Trustless Work hackathon (3 overall + 5 track
winners), the wizard saw only the 3 overall winners — the 5 track
winners never made it into the `winners` array.

Fix:

- Add `submissionId?: string` to the `Submission` type.
- Mapper populates it from `submissionData.id || sub.id ||
  sub.submissionId`. The mapper's `id` field stays on the participant
  ID for compatibility with the existing rank-assignment code that
  already keys off it.
- Track-winner enrichment looks up `byId.get(sub.submissionId)` first,
  falls back to `byId.get(sub.id)` for older rows where the mapper
  output predated the new field.

After this, the wizard will show "All 8 winners assigned • 1,500 USDC
pool" with the three overall placements and five track winners.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(auth): send absolute callbackURL on Google sign-up (#584)

The Google sign-up flow passed `callbackURL: process.env.NEXT_PUBLIC_APP_URL || '/'`
to Better Auth. Better Auth treats a relative path as relative to the
API host that processed the OAuth callback, not the frontend host.
When `NEXT_PUBLIC_APP_URL` wasn't set (or was missing in the runtime
env even though available at build time on Next.js), the fallback `'/'`
sent users to the API host's root after OAuth completed. The session
cookie WAS set on the shared `.boundlessfi.xyz` domain during the
callback, but the user landed on a blank API page and thought sign-up
had failed. Clearing browser cache (cookies survive — different
section in Chrome) didn't drop the cookie, so the next visit to the
frontend silently restored their session and they appeared
"automatically logged in."

Fix: always build an absolute `callbackURL` pointing at the frontend
host. Same pattern LoginWrapper already uses — falls back to
window.location.origin at runtime, then to the env var at build/SSR,
then to the production canonical URL. All three are in the BE's
`trustedOrigins` list so Better Auth won't reject the URL.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(submissions): unbreak submission detail page + publish hackathon recap (#586)

* fix(submissions): wire submission entity type through votes, comments, and rank chip

The submission detail page (/projects/[slug]?type=submission) was
treating submissions as crowdfunding projects, which broke every
interactive surface for hackathon entries:

- Sidebar getVoteCounts and the voters tab's getProjectVotes were
  hardcoded to CROWDFUNDING_CAMPAIGN, so the lookup failed with
  "Failed to load voting data" and disabled the buttons.
- createVote on the voters tab was also hardcoded, so even a click
  through hit Prisma's "Invalid reference to related record" (P2025)
  for non-existent campaign rows.
- ProjectComments used CommentEntityType.PROJECT, so comments and
  replies posted against a non-existent project record.
- The Follow button hardcoded entityType='PROJECT' with the
  submission's id, leading to "Failed to update follow status". The
  follow EntityType enum has no HACKATHON_SUBMISSION value, so the
  button is hidden on submission pages.
- The status badge displayed raw enum values like "SHORTLISTED" and
  there was no signal that a submission was a winner.

All vote/comment surfaces now derive the entity type from the URL,
matching the backend's HACKATHON_SUBMISSION enum on both votes and
comments. The page mapper surfaces submissionRank and a friendly
submissionStatus on the project, and the sidebar header renders a
"Winner" / "Rank #N" chip next to the existing status badge.

* fix(blog): use object-cover so card and detail covers fill the frame

The earlier switch back to object-contain left letterbox bands around
banners that aren't an exact 2:1 ratio on both the BlogCard grid and
the post-details hero. With properly-sized cover banners, object-cover
fills the frame without visible cropping. Authors should size cover
images for a 2:1 ratio on the card and the responsive heights on the
details page.

* feat(blog): publish Boundless x Trustless Work hackathon winners recap

Adds the post-hackathon recap announcing the three main winners
(Conductor, Crypt, GoPadi), the five honorable mentions, and the
Showcase recognitions from the May 16 finale. Featured on the blog
index.

* chore(deps): npm audit fix to clear js-cookie high-severity advisory

GHSA-qjx8-664m-686j (js-cookie per-instance prototype hijack in
assign()) was newly flagged as high severity. The pre-push hook runs
`npm audit --audit-level=high` and refused to push any branch until
this was patched. Lockfile-only update, no package.json changes,
semver-compatible. Bundled here because the gate was blocking the
parent PR; reviewers can treat this as an unrelated chore commit.

* fix(blog): drop duplicate h1 from hackathon winners post (#588)

BlogPostDetails already renders post.title from frontmatter as the
page heading above the cover banner, so the matching `#` heading at
the top of the MDX body rendered a second time directly under the
banner. Removing the body-level h1 keeps the page to a single title.

* feat(didit): drive verification UI from /didit/status, fix In Review button (#590)

* feat(didit): drive verification UI from /didit/status, fix In Review button

Reported: when a user's identity verification is "In Review", the
verify button still shows; after the Didit hosted flow there is no
clear "what happens next, how long" copy.

The new backend endpoint (GET /api/didit/status, boundlessfi/
boundless-nestjs#192) returns a normalized state the frontend can
switch on. This PR consumes it everywhere identity status is shown.

- lib/api/didit.ts: new getDiditStatus() + VerificationStatus type.
- IdentityVerificationSection: source of truth is now the status
  endpoint, not user.identityVerificationStatus. Verify button only
  renders when canStartNew === true — covers in_review and approved
  by construction. Each state gets its own badge, copy, and
  conditional review-window/decline-reason block. Light 10s poll
  while state is in_progress so the UI flips automatically once
  the webhook lands.
- DiditVerifyButton: optional `label` prop so the section can show
  "Try verification again" / "Start new verification" where it makes
  sense.
- VerificationSubmittedModal: state-driven copy + icon + halo.
  in_review uses "1-3 business days, expected by <date>". approved
  / declined / in_progress / abandoned / expired each get their own
  message. Accepts the full VerificationStatus so the modal can show
  the exact estimated-completion date from the backend.
- app/api/didit/callback/route.ts: forwards the new `state` query
  param (still tolerates legacy ?verification=complete). Includes
  session_id when present, sets tab=identity so users land on the
  right tab.
- SettingsContent: fetches getDiditStatus when ?verification=... is
  present, passes the status into the modal so the copy includes the
  precise SLA. Tab picker also honors ?tab=identity.

`npx tsc --noEmit` clean, `npm run lint` clean. No tests in this
repo, so verified by walking each state in the type system.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(deps): bump js-cookie 3.0.5 -> 3.0.7 to clear high-severity audit

GHSA: cookie-attribute injection via prototype hijack in assign().
Patch bump; no API changes for our usage.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Feat/didit verification status frontend (#592)

* feat(didit): drive verification UI from /didit/status, fix In Review button

Reported: when a user's identity verification is "In Review", the
verify button still shows; after the Didit hosted flow there is no
clear "what happens next, how long" copy.

The new backend endpoint (GET /api/didit/status, boundlessfi/
boundless-nestjs#192) returns a normalized state the frontend can
switch on. This PR consumes it everywhere identity status is shown.

- lib/api/didit.ts: new getDiditStatus() + VerificationStatus type.
- IdentityVerificationSection: source of truth is now the status
  endpoint, not user.identityVerificationStatus. Verify button only
  renders when canStartNew === true — covers in_review and approved
  by construction. Each state gets its own badge, copy, and
  conditional review-window/decline-reason block. Light 10s poll
  while state is in_progress so the UI flips automatically once
  the webhook lands.
- DiditVerifyButton: optional `label` prop so the section can show
  "Try verification again" / "Start new verification" where it makes
  sense.
- VerificationSubmittedModal: state-driven copy + icon + halo.
  in_review uses "1-3 business days, expected by <date>". approved
  / declined / in_progress / abandoned / expired each get their own
  message. Accepts the full VerificationStatus so the modal can show
  the exact estimated-completion date from the backend.
- app/api/didit/callback/route.ts: forwards the new `state` query
  param (still tolerates legacy ?verification=complete). Includes
  session_id when present, sets tab=identity so users land on the
  right tab.
- SettingsContent: fetches getDiditStatus when ?verification=... is
  present, passes the status into the modal so the copy includes the
  precise SLA. Tab picker also honors ?tab=identity.

`npx tsc --noEmit` clean, `npm run lint` clean. No tests in this
repo, so verified by walking each state in the type system.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(deps): bump js-cookie 3.0.5 -> 3.0.7 to clear high-severity audit

GHSA: cookie-attribute injection via prototype hijack in assign().
Patch bump; no API changes for our usage.

* feat(verify): add /verify page so backend's post-KYC redirect resolves

The backend's GET /api/didit/callback (boundlessfi/boundless-nestjs#192)
defaults its redirect target to ${FRONTEND_URL}/verify?state=...&session_id=...
but the route did not exist — every successful KYC completion landed
on a 404.

Adds a polished result page that:

- Reads ?state= from the URL for the initial render (so the page
  shows the right outcome even before the status fetch completes).
- Fetches /api/didit/status for authoritative state + the precise
  review window / decline reason; URL state is just a hint.
- Renders per-state copy + icon + halo matching the
  VerificationSubmittedModal:
    approved  -> "Continue to dashboard" CTA.
    in_review -> "1-3 business days, expected by <date>".
    in_progress -> polls every 10s so the page flips when the
                   webhook lands.
    declined  -> shows the reason + retry button.
    abandoned/expired/not_started -> restart button + Settings CTA.
- Falls back gracefully when the status fetch fails (e.g.
  unauthenticated): retries on demand, or sends the user home if
  there's no URL state either.

Builds and registers as a static route (✓ Compiled successfully).

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: align request payloads with backend DTOs ahead of forbidNonWhitelisted (#594)

* fix: align request payloads with backend DTOs ahead of forbidNonWhitelisted

The boundless-nestjs backend (PR #187) is flipping its global
ValidationPipe from forbidNonWhitelisted: false to true. Today any
unknown body field is silently stripped; after the flip, sending such
a field produces a 400. This commit fixes every front-end -> back-end
payload the audit flagged.

1. Team posts (CreateTeamPostModal, ContactTeamModal,
   team-detail page).
   - lib/api/hackathons/teams.ts: replaced `contactInfo: string` and
     `contactMethod` with `contactInfo?: TeamContactInfo` matching the
     backend shape `{ telegram?, discord?, email? }`. Added a
     readTeamContact() helper that flattens the sparse object back to
     `{ method, value }` for UI display.
   - CreateTeamPostModal: form schema keeps the `{ method, value }`
     UX, but onSubmit projects to `{ contactInfo: { [method]: value } }`
     before calling the api. github and other were removed from the
     method enum because the backend cannot store them. Edit-mode
     initial-data hydrates from readTeamContact().
   - ContactTeamModal: replaced direct string access with
     readTeamContact() and used contact.method/value throughout. The
     github icon case was dropped since github is no longer a valid
     method.
   - team-detail page: same readTeamContact() flattening for the
     Contact card.

2. Judging score (lib/api/hackathons/judging.ts, useScoreForm).
   The submitJudgingScore request used `comment` for the global
   per-judge note, but the backend ScoreSubmissionDto names the field
   `notes`. Renamed the interface field and the call site. Per-criterion
   `comment` stays because CriterionScoreDto does accept it.

3. Submission update (hooks/hackathon/use-submission.ts).
   Added a pickUpdateSubmissionFields() whitelist mapping to the
   backend UpdateSubmissionDto. The update path now strips:
     - participationType, teamId, teamName (Create-only)
     - organizationId, hackathonId, participantId (server-derived)
     - per-entry email on teamMembers (TeamMemberDto has no email)
   ...before PATCH /hackathons/submissions/:id. The form continues to
   send the full shape; the hook filters at the API boundary so future
   callers are also protected.

4. User profile (lib/api/auth.ts).
   Removed the `preferences` block from UpdateUserProfileRequest. The
   backend UpdateProfileDto has no such field; users use the dedicated
   /users/settings/* endpoints for appearance / language / timezone /
   notification toggles. The wide type was the same kind of unknown-
   field door the audit is closing.

5. Hackathon update (lib/api/hackathons.ts).
   UpdateHackathonRequest was `Partial<Hackathon> & { rewards? }`,
   which let any Hackathon-shape field (id, organizationId, status,
   creatorId, ...) leak into PUT /hackathons/:id. Narrowed to just
   `{ rewards?: HackathonRewards }` since that is the only field
   actually sent today (JudgingResultsTable's rank-override save).

Out of scope (separate pre-existing bugs to file):
- PUT /hackathons/:id has no matching backend route; the rank-
  override save in JudgingResultsTable is broken regardless of #187.
- POST /users/earnings/claim does not match backend /users/earnings/
  withdraw.
- POST /organizations/:id/invite does not match backend /invitations.

Backend #188 (global ThrottlerGuard + 429 handling): no frontend
changes needed. lib/api/api.ts already honours Retry-After and
exponential backoff (1s, 2s, 4s) for three retries before surfacing
a RATE_LIMIT_EXCEEDED error code.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(submissions): project teamMembers entries to backend TeamMemberDto shape

Found during code review of the previous commit on this branch.

The strip on teamMembers only removed `email`, but the frontend
SubmissionFormData also lets each entry carry `avatar` — and the
backend TeamMemberDto only has { userId, name, username?, role }.
Sending `avatar` would 400 once forbidNonWhitelisted lands.

Switched from a deny-list strip (`{ email: _email, ...rest }`) to an
explicit project-down to the four backend fields. Same defense-in-
depth pattern as the outer pickUpdateSubmissionFields whitelist.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Feat/crowdfunding (#640)

* feat: integrate treasury management features and refactor hackathon orchestration and API client architecture.

* chore: update package-lock.json dependencies

* chore: update pre-push husky hook to perform full type-checks and production builds

* refactor: remove DevelopmentStatusModal from global layout

* feat: task-first Winners page, judging UX, private access, treasury receipts/send

Winners: new task-first Winners section (pick winner per prize with engine default, inline stacking confirm, deliberate 'Don't award' + unawarded-funds acknowledgement); /rewards redirects to /winners; staged Pick -> Confirm -> Pay.

Judging: slimmed Results tab to read-only standings + 'Go to Winners'; criterionId name fix; AI scorecards, recommendation thresholds, CSV judge import, tracks + custom questions wizard sections; validation + empty-state polish.

Public: private + password access gate, community links, card private badge. Treasury: receipts + Send funds. Removed the stacking toggle from prize setup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(bounty): ModeTab + SubmissionModelTab for the Configure wizard (#599) (#611)

The net-new bounty UI with no hackathon analog: the two-axis mode picker and
the mode-conditional submission settings that drive all six combinations.

- modeSchema.ts: plain taxonomy unions (entryType x claimType), the
  computeBountyModeLabel helper, and getModeFields — the mode -> field matrix
  derived functionally from the publish gate (validateTwoAxisMode) so it cannot
  drift from the backend.
- submissionModelSchema.ts: makeSubmissionModelSchema(mode), a mode-aware Zod
  factory whose required fields and the >=2 floors (maxApplicants for open
  competition, shortlistSize for application competition) mirror the server gate.
- ModeTab.tsx: entry x claim picker, a winners stepper (1-3) for competitions,
  and a live computed mode label.
- SubmissionModelTab.tsx: renders each field per the matrix (required / optional
  / hidden) and shows submissionVisibility read-only, forced by the mode
  (HIDDEN_UNTIL_DEADLINE for competitions).

Standalone components (props: mode, onSave, onContinue, initialData, isLoading);
they wire into the wizard shell + routes in #597 / #598.

* 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): features/bounties data layer (types, keys, draft + escrow) (#596) (#612)

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(bounty): wizard shell + step/draft machinery (#598) (#613)

* 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(bounty): Scope + Reward + Review tabs and Zod schemas (#600) (#614)

Completes the editable surface of the bounty Configure wizard, dropping the
three remaining tabs into the seams the shell (#598) left.

- tabs/schemas/scopeSchema.ts: title, description, optional GitHub issue
  url/number, optional project/window.
- tabs/schemas/rewardSchema.ts: makeRewardSchema(claimType) — exactly one tier
  for single claim, 1-3 for a competition; amounts > 0, unique positions
  including position 1 (mirrors the publish gate's deriveWinnerDistribution).
- lib/utils/bounty-escrow.ts: adapts the bounty reward shape to reuse the
  hackathon prize-pool math (getTotalPrizePoolForFunding, buildWinnerDistribution,
  2.5% PLATFORM_FEE) rather than duplicating it.
- tabs/ScopeTab.tsx: scope form.
- tabs/RewardTab.tsx: currency + mode-driven prize tiers (field array sized to
  the winner count) with a live prize-pool / fee / total preview.
- tabs/ReviewTab.tsx: per-section summary, validation summary, funding totals,
  and a publish CTA disabled until every section validates.
- NewBountyTab.tsx + constants.ts: wire the three tabs into the wizard; the form
  snapshot now uses the per-tab form schema types.

Reward currency is collected in the Reward step (the backend section that
persists it) rather than Scope, so it round-trips. The publish action is a
placeholder pending the escrow publish flow (#601); the disabled-until-valid
gate is wired here.

* feat(bounty): use-bounty-publish (escrow publish via shared runner) (#601) (#615)

* feat(bounty): Scope + Reward + Review tabs and Zod schemas (#600)

Completes the editable surface of the bounty Configure wizard, dropping the
three remaining tabs into the seams the shell (#598) left.

- tabs/schemas/scopeSchema.ts: title, description, optional GitHub issue
  url/number, optional project/window.
- tabs/schemas/rewardSchema.ts: makeRewardSchema(claimType) — exactly one tier
  for single claim, 1-3 for a competition; amounts > 0, unique positions
  including position 1 (mirrors the publish gate's deriveWinnerDistribution).
- lib/utils/bounty-escrow.ts: adapts the bounty reward shape to reuse the
  hackathon prize-pool math (getTotalPrizePoolForFunding, buildWinnerDistribution,
  2.5% PLATFORM_FEE) rather than duplicating it.
- tabs/ScopeTab.tsx: scope form.
- tabs/RewardTab.tsx: currency + mode-driven prize tiers (field array sized to
  the winner count) with a live prize-pool / fee / total preview.
- tabs/ReviewTab.tsx: per-section summary, validation summary, funding totals,
  and a publish CTA disabled until every section validates.
- NewBountyTab.tsx + constants.ts: wire the three tabs into the wizard; the form
  snapshot now uses the per-tab form schema types.

Reward currency is collected in the Reward step (the backend section that
persists it) rather than Scope, so it round-trips. The publish action is a
placeholder pending the escrow publish flow (#601); the disabled-until-valid
gate is wired here.

* feat(bounty): use-bounty-publish (escrow publish via shared runner) (#601)

Wires the bounty publish action, mirroring use-hackathon-publish.

- hooks/use-bounty-publish.ts: builds PublishBountyEscrowDto (ownerAddress,
  tokenAddress via getTokenAddress('USDC'), budget = sum of tier amounts,
  submissionDeadline as unix from the draft deadline, applicationCreditCost,
  winnerDistribution via the shared buildBountyWinnerDistribution, fundingMode)
  and drives it with useEscrowOpRunner: MANAGED returns PENDING_CONFIRM then
  polls; EXTERNAL signs the returned XDR via signXdrWithKit then submits + polls.
  On COMPLETED the bounty has moved draft_awaiting_funding -> open. Pre-flight
  resumes an in-flight op, short-circuits an already-published bounty, and runs a
  MANAGED USDC balance check (pool + 2.5% fee).
- NewBountyTab.tsx: replace the Review placeholder with the real publish() and
  wire isPublishing into the CTA.

Treasury funding (sourceWalletId) is omitted until the backend treasury-parity
issue (boundless-nestjs #314) lands; MANAGED + EXTERNAL are supported. The
funding-source picker + progress modal remain a follow-up; the hook exposes the
escrow phase/error/txHash they need.

* feat(bounty): routes + organization bounty list page (#597) (#616)

Adds the organizer bounty routes, mirroring the hackathon route tree, and the
published-list data needed by the list page.

- app/(landing)/organizations/[id]/bounties/page.tsx: list page with a Drafts
  section (resume links + status badges + mode label / section count) and a
  Published section (status badges + reward), plus a "Host a bounty" CTA.
- .../bounties/new/page.tsx: renders <NewBountyTab organizationId={id} />.
- .../bounties/drafts/[draftId]/page.tsx: renders
  <NewBountyTab organizationId={id} draftId={draftId} />.
- features/bounties/api/core.ts + use-bounties.ts: listOrganizationBounties +
  useOrganizationBounties for the root list (the backend root GET has no
  response DTO, so the row shape is projected to OrganizationBountyListItem).

The pages mount the existing wizard shell (#598); the list separates drafts
(draft / draft_awaiting_funding) from published bounties so they don't
double-list.

* feat(bounty): sidebar nav entry + Post Bounty quick action (#602) (#617)

Adds a Bounties menu item (/organizations/{orgId}/bounties) and a Post Bounty
quick action (/organizations/{orgId}/bounties/new) to OrganizationSidebar, so
organizers can reach the bounty list + Configure wizard.

Codegen reconcile: re-ran npm run codegen against the v2 backend; the committed
lib/api/generated/schema.d.ts already matches (BountyDraftResponseDto +
UpdateBountyDraftDto present, no drift), so no schema change is needed.

* 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

* feat(bounty): wizard enhancements + hackathon-style org bounty list (#618)

* feat(bounty): wizard enhancements (resources step, currency, reputation, nav)

- Resources: new optional Resources step (links + PDF/DOC/PPT/MD uploads),
  mirroring the hackathon resources tab; never blocks publish. Reuses a now
  configurable ResourceFileUpload (folder/tags/accepted types props; unique
  input id per row).
- Reward currency: dropdown of USDC (default) / XLM (disabled) with real token
  logos, replacing the free-text field.
- Reputation: per-category minimum floor (development highest); the organizer
  can raise it but not drop below the category baseline.
- Navigation: Back button on every step after the first.
- Scope: category chips + searchable country dropdown with flags + markdown
  description; GitHub issue URL required only for development.
- Publish: redirect to the organizer's bounty list once publish finalizes.
- Dev-only "Fill with mock" button to populate every section at once.

* feat(bounty): hackathon-style org bounty list + delete-draft confirm

- Rebuild the organization bounty list page in the hackathon style: sticky
  header with stats, Published/Drafts tabs, search + sort, and a banner-less
  reward-focused card grid (draft cards show a progress bar + resume link).
- Deleting a draft now opens a confirmation modal (DeleteBountyDraftDialog)
  instead of deleting on the first click.
- Expose `_count.submissions` on the org bounty list item for the cards.

* 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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Nnaji Benjamin <60315147+Benjtalkshow@users.noreply.github.com>
Co-authored-by: Benjtalkshow <chinedubenj@gmail.com>

* Revert "Feat/crowdfunding (#640)" (#641)

This reverts commit 52be58c7540a7dbc0207095b995232522221ad67.

* Enhance bounty features with new UI, data layers, and crowdfunding support (#663)

* feat: integrate treasury management features and refactor hackathon orchestration and API client architecture.

* chore: update package-lock.json dependencies

* chore: update pre-push husky hook to perform full type-checks and production builds

* refactor: remove DevelopmentStatusModal from global layout

* feat: task-first Winners page, judging UX, private access, treasury receipts/send

Winners: new task-first Winners section (pick winner per prize with engine default, inline stacking confirm, deliberate 'Don't award' + unawarded-funds acknowledgement); /rewards redirects to /winners; staged Pick -> Confirm -> Pay.

Judging: slimmed Results tab to read-only standings + 'Go to Winners'; criterionId name fix; AI scorecards, recommendation thresholds, CSV judge import, tracks + custom questions wizard sections; validation + empty-state polish.

Public: private + password access gate, community links, card private badge. Treasury: receipts + Send funds. Removed the stacking toggle from prize setup.


* feat(bounty): ModeTab + SubmissionModelTab for the Configure wizard (#599) (#611)

The net-new bounty UI with no hackathon analog: the two-axis mode picker and
the mode-conditional submission settings that drive all six combinations.

- modeSchema.ts: plain taxonomy unions (entryType x claimType), the
  computeBountyModeLabel helper, and getModeFields — the mode -> field matrix
  derived functionally from the publish gate (validateTwoAxisMode) so it cannot
  drift from the backend.
- submissionModelSchema.ts: makeSubmissionModelSchema(mode), a mode-aware Zod
  factory whose required fields and the >=2 floors (maxApplicants for open
  competition, shortlistSize for application competition) mirror the server gate.
- ModeTab.tsx: entry x claim picker, a winners stepper (1-3) for competitions,
  and a live computed mode label.
- SubmissionModelTab.tsx: renders each field per the matrix (required / optional
  / hidden) and shows submissionVisibility read-only, forced by the mode
  (HIDDEN_UNTIL_DEADLINE for competitions).

Standalone components (props: mode, onSave, onContinue, initialData, isLoading);
they wire into the wizard shell + routes in #597 / #598.

* feat(bounty): features/bounties data layer (types, keys, draft + escrow) (#596) (#612)

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) (#613)

* 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(bounty): Scope + Reward + Review tabs and Zod schemas (#600) (#614)

Completes the editable surface of the bounty Configure wizard, dropping the
three remaining tabs into the seams the shell (#598) left.

- tabs/schemas/scopeSchema.ts: title, description, optional GitHub issue
  url/number, optional project/window.
- tabs/schemas/rewardSchema.ts: makeRewardSchema(claimType) — exactly one tier
  for single claim, 1-3 for a competition; amounts > 0, unique positions
  including position 1 (mirrors the publish gate's deriveWinnerDistribution).
- lib/utils/bounty-escrow.ts: adapts the bounty reward shape to reuse the
  hackathon prize-pool math (getTotalPrizePoolForFunding, buildWinnerDistribution,
  2.5% PLATFORM_FEE) rather than duplicating it.
- tabs/ScopeTab.tsx: scope form.
- tabs/RewardTab.tsx: currency + mode-driven prize tiers (field array sized to
  the winner count) with a live prize-pool / fee / total preview.
- tabs/ReviewTab.tsx: per-section summary, validation summary, funding totals,
  and a publish CTA disabled until every section validates.
- NewBountyTab.tsx + constants.ts: wire the three tabs into the wizard; the form
  snapshot now uses the per-tab form schema types.

Reward currency is collected in the Reward step (the backend section that
persists it) rather than Scope, so it round-trips. The publish action is a
placeholder pending the escrow publish flow (#601); the disabled-until-valid
gate is wired here.

* feat(bounty): use-bounty-publish (escrow publish via shared runner) (#601) (#615)

* feat(bounty): Scope + Reward + Review tabs and Zod schemas (#600)

Completes the editable surface of the bounty Configure wizard, dropping the
three remaining tabs into the seams the shell (#598) left.

- tabs/schemas/scopeSchema.ts: title, description, optional GitHub issue
  url/number, optional project/window.
- tabs/schemas/rewardSchema.ts: makeRewardSchema(claimType) — exactly one tier
  for single claim, 1-3 for a competition; amounts > 0, unique positions
  including position 1 (mirrors the publish gate's deriveWinnerDistribution).
- lib/utils/bounty-escrow.ts: adapts the bounty reward shape to reuse the
  hackathon prize-pool math (getTotalPrizePoolForFunding, buildWinnerDistribution,
  2.5% PLATFORM_FEE) rather than duplicating it.
- tabs/ScopeTab.tsx: scope form.
- tabs/RewardTab.tsx: currency + mode-driven prize tiers (field array sized to
  the winner count) with a live prize-pool / fee / total preview.
- tabs/ReviewTab.tsx: per-section summary, validation summary, funding totals,
  and a publish CTA disabled until every section validates.
- NewBountyTab.tsx + constants.ts: wire the three tabs into the wizard; the form
  snapshot now uses the per-tab form schema types.

Reward currency is collected in the Reward step (the backend section that
persists it) rather than Scope, so it round-trips. The publish action is a
placeholder pending the escrow publish flow (#601); the disabled-until-valid
gate is wired here.

* feat(bounty): use-bounty-publish (escrow publish via shared runner) (#601)

Wires the bounty publish action, mirroring use-hackathon-publish.

- hooks/use-bounty-publish.ts: builds PublishBountyEscrowDto (ownerAddress,
  tokenAddress via getTokenAddress('USDC'), budget = sum of tier amounts,
  submissionDeadline as unix from the draft deadline, applicationCreditCost,
  winnerDistribution via the shared buildBountyWinnerDistribution, fundingMode)
  and drives it with useEscrowOpRunner: MANAGED returns PENDING_CONFIRM then
  polls; EXTERNAL signs the returned XDR via signXdrWithKit then submits + polls.
  On COMPLETED the bounty has moved draft_awaiting_funding -> open. Pre-flight
  resumes an in-flight op, short-circuits an already-published bounty, and runs a
  MANAGED USDC balance check (pool + 2.5% fee).
- NewBountyTab.tsx: replace the Review placeholder with the real publish() and
  wire isPublishing into the CTA.

Treasury funding (sourceWalletId) is omitted until the backend treasury-parity
issue (boundless-nestjs #314) lands; MANAGED + EXTERNAL are supported. The
funding-source picker + progress modal remain a follow-up; the hook exposes the
escrow phase/error/txHash they need.

* feat(bounty): routes + organization bounty list page (#597) (#616)

Adds the organizer bounty routes, mirroring the hackathon route tree, and the
published-list data needed by the list page.

- app/(landing)/organizations/[id]/bounties/page.tsx: list page with a Drafts
  section (resume links + status badges + mode label / section count) and a
  Published section (status badges + reward), plus a "Host a bounty" CTA.
- .../bounties/new/page.tsx: renders <NewBountyTab organizationId={id} />.
- .../bounties/drafts/[draftId]/page.tsx: renders
  <NewBountyTab organizationId={id} draftId={draftId} />.
- features/bounties/api/core.ts + use-bounties.ts: listOrganizationBounties +
  useOrganizationBounties for the root list (the backend root GET has no
  response DTO, so the row shape is projected to OrganizationBountyListItem).

The pages mount the existing wizard shell (#598); the list separates drafts
(draft / draft_awaiting_funding) from published bounties so they don't
double-list.

* feat(bounty): sidebar nav entry + Post Bounty quick action (#602) (#617)

Adds a Bounties menu item (/organizations/{orgId}/bounties) and a Post Bounty
quick action (/organizations/{orgId}/bounties/new) to OrganizationSidebar, so
organizers can reach the bounty list + Configure wizard.

Codegen reconcile: re-ran npm run codegen against the v2 backend; the committed
lib/api/generated/schema.d.ts already matches (BountyDraftResponseDto +
UpdateBountyDraftDto present, no drift), so no schema change is needed.

* feat(bounty): wizard enhancements + hackathon-style org bounty list (#618)

* feat(bounty): wizard enhancements (resources step, currency, reputation, nav)

- Resources: new optional Resources step (links + PDF/DOC/PPT/MD uploads),
  mirroring the hackathon resources tab; never blocks publish. Reuses a now
  configurable ResourceFileUpload (folder/tags/accepted types props; unique
  input id per row).
- Reward currency: dropdown of USDC (default) / XLM (disabled) with real token
  logos, replacing the free-text field.
- Reputation: per-category minimum floor (development highest); the organizer
  can raise it but not drop below the category baseline.
- Navigation: Back button on every step after the first.
- Scope: category chips + searchable country dropdown with flags + markdown
  description; GitHub issue URL required only for development.
- Publish: redirect to the organizer's bounty list once publish finalizes.
- Dev-only "Fill with mock" button to populate every section at once.

* feat(bounty): hackathon-style org bounty list + delete-draft confirm

- Rebuild the organization bounty list page in the hackathon style: sticky
  header with stats, Published/Drafts tabs, search + sort, and a banner-less
  reward-focused card grid (draft cards show a progress bar + resume link).
- Deleting a draft now opens a confirmation modal (DeleteBountyDraftDialog)
  instead of deleting on the first click.
- Expose `_count.submissions` on the org bounty list item for the cards.

* Feat/crowdfunding (#620)

* 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

---------

Co-authored-by: Benjtalkshow <chinedubenj@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* Feat/crowdfunding (#642)

* 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-fe…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants