Skip to content

Feature/sponsor bounty creation - #290

Closed
Danitello123 wants to merge 3 commits into
boundlessfi:mainfrom
Danitello123:feature/sponsor-bounty-creation
Closed

Feature/sponsor bounty creation#290
Danitello123 wants to merge 3 commits into
boundlessfi:mainfrom
Danitello123:feature/sponsor-bounty-creation

Conversation

@Danitello123

@Danitello123 Danitello123 commented Jun 26, 2026

Copy link
Copy Markdown

closes #181

Summary by CodeRabbit

  • New Features
    • Added a step-by-step bounty creation workflow (Basic Info → Rewards & Deadlines → Review) with validation and a publish confirmation summary.
    • Introduced “Bounty Creation Portal” UI and connected the form to bounty creation with success/error feedback.
    • Expanded supported payout currency options to include EURC.
  • Bug Fixes
    • Updated access control so bounty creation is available to sponsors and organization members.
    • Improved post-submit behavior with automatic redirect after a successful bounty creation and clear error toasts.

User added 2 commits June 24, 2026 02:34
- Remove unused useUserRole import and userRole variable from
  �pp/bounty/create/page.tsx (no-unused-vars)
- Remove unused useUserRole import and userRole variable from
  components/global-navbar.tsx (no-unused-vars)
- Add missing 
ole field to ExtendedUser interface in both files
  so user.role access is type-safe without runtime cast
- Replace error: any in hooks/use-create-bounty.ts onError handler
  with unknown + instanceof Error guard (@typescript-eslint/no-explicit-any)
- Replace four orm as any casts in �ounty-create-form.tsx with
  typed UseFormReturn<FieldValues> casts (@typescript-eslint/no-explicit-any)
- Update currency picker from AQUA → EURC in �udget-input.tsx and
  schemas.ts to match issue spec (XLM, USDC, EURC)
- Add unit tests for use-create-bounty hook covering success/error
  paths, cache invalidation, redirect, and toast notifications
@vercel

vercel Bot commented Jun 26, 2026

Copy link
Copy Markdown

@superman32432432 is attempting to deploy a commit to the Threadflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 25211c53-6bb9-466f-8aeb-d29cc7377889

📥 Commits

Reviewing files that changed from the base of the PR and between 690dc75 and a4d6db6.

📒 Files selected for processing (1)
  • components/bounty/bounty-create-form.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/bounty/bounty-create-form.tsx

📝 Walkthrough

Walkthrough

Adds a bounty creation page and multi-step form, broadens create access to sponsors or organization members, wires a creation mutation hook with cache invalidation and redirects, and updates the allowed reward asset from AQUA to EURC.

Changes

Bounty Creation Flow

Layer / File(s) Summary
Route access
app/bounty/create/page.tsx, components/global-navbar.tsx
Session-derived sponsor or organization membership now gates the create route and navbar CTA, and the page renders BountyCreateForm instead of the placeholder card.
Mutation wrapper
hooks/use-create-bounty.ts, hooks/__tests__/use-create-bounty.test.tsx
useCreateBounty wraps the GraphQL mutation with query invalidation, toast messages, and redirect handling; tests cover the success and error paths.
Form contract
components/bounty/bounty-create-form.tsx, components/bounty/forms/schemas.ts, components/bounty/forms/budget-input.tsx
The bounty form adds conditional validation, session-driven defaults, asset options, and submit payload mapping, including the reward asset change from AQUA to EURC.
Step 1 and Step 2 inputs
components/bounty/bounty-create-form.tsx
The first two steps render bounty details, reward and deadline controls, lightning-round selection, and milestone inputs.
Review and submit controls
components/bounty/bounty-create-form.tsx
The review step summarizes the collected values and the action bar renders error, back, next, and submit controls with loading state.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CreateBountyPage
  participant BountyCreateForm
  participant useCreateBounty
  participant useCreateBountyMutation
  participant QueryClient
  participant Router
  participant toast

  User->>CreateBountyPage: Open /bounty/create
  CreateBountyPage->>BountyCreateForm: Render authorized form
  User->>BountyCreateForm: Submit bounty details
  BountyCreateForm->>useCreateBounty: createBounty(input)
  useCreateBounty->>useCreateBountyMutation: mutate({ input })

  alt success
    useCreateBountyMutation-->>useCreateBounty: createBounty.id
    useCreateBounty->>QueryClient: invalidate bountyKeys.lists()
    useCreateBounty->>toast: success message
    useCreateBounty->>Router: push("/bounty/" + id) or "/bounty"
  else error
    useCreateBounty->>toast: error message
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • Benjtalkshow

Poem

A bunny found a bounty gate,
with EURC coins and a hopeful date.
He hopped through steps, then clicked “Submit,”
and cheerful toasts said, “You did it!”
🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the bounty creation feature added in this PR.
Linked Issues check ✅ Passed The PR covers the creation route, form, mutation hook, validation, redirects, and navbar gating required by #181.
Out of Scope Changes check ✅ Passed No clear out-of-scope changes stand out; the asset and test updates support the bounty creation flow.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 OpenGrep (1.23.0)
components/bounty/bounty-create-form.tsx

┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

[00.13][ERROR]: unable to find a config; path .coderabbit-opengrep-fallback.yml does not exist


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
app/bounty/create/page.tsx (1)

8-15: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated ExtendedUser interface across three files.

The same ExtendedUser shape is declared here, in components/global-navbar.tsx (Lines 28-35), and in components/bounty/bounty-create-form.tsx (Lines 8-15). The session-user casting (session?.user as ExtendedUser) and the isSponsorOrOrgMember derivation are also duplicated. Extract the type and a small isSponsorOrOrgMember(user) helper into a shared module so the gating contract stays consistent if role/organizations semantics change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/bounty/create/page.tsx` around lines 8 - 15, The `ExtendedUser` type and
sponsor/org-member gating logic are duplicated across multiple components, so
extract them into one shared module and reuse them from
`app/bounty/create/page.tsx`, `components/global-navbar.tsx`, and
`components/bounty/bounty-create-form.tsx`. Move the session-user cast
(`session?.user as ExtendedUser`) and the `isSponsorOrOrgMember(user)`
derivation into that shared helper so the role/organizations contract stays
consistent in one place. Update the affected components to import the shared
type and helper instead of redefining the interface locally.
components/bounty/bounty-create-form.tsx (1)

256-263: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Specify a radix in parseInt. parseInt(match[1]) should pass radix 10 to avoid surprising parsing behavior and satisfy lint rules. The captured group is \d+ so it's safe, but being explicit is the idiomatic choice.

🔧 Proposed fix
-      return match ? parseInt(match[1]) : undefined;
+      return match ? parseInt(match[1], 10) : undefined;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/bounty/bounty-create-form.tsx` around lines 256 - 263, The Github
issue URL parsing helper parseGithubIssueNumber currently calls parseInt without
an explicit radix, which can trip lint rules and lead to ambiguous parsing
behavior. Update the parseGithubIssueNumber function to pass radix 10 when
converting match[1], keeping the existing regex and return behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@components/bounty/bounty-create-form.tsx`:
- Around line 224-226: The default reward amount in the bounty form currently
uses an explicit any cast, which trips linting. Update the initial state in
bounty-create-form.tsx where the reward object is built so amount is typed
without any, either by modeling it as number | undefined or by casting through
unknown in the same spot. Keep the empty default behavior intact while removing
the no-explicit-any violation in the form state setup.
- Line 422: The SelectItem usage with an empty string value will crash at
runtime because the Radix-based Select primitive does not allow empty-string
item values. Update the Select setup in bounty-create-form so the “None” and
“Standard Bounty (No Window)” options use a non-empty sentinel value, and
normalize that sentinel back to unset/empty in the form wiring. Adjust the
relevant Select value/defaultValues handling and the onSubmit path so the
sentinel maps to undefined or "" consistently without ever passing "" into
SelectItem.
- Around line 68-70: The bounty form schema is using Zod 3-style
`required_error` options that are ignored in Zod 4, so update the validation
messages in `bounty-create-form.tsx` to use the `error` callback instead. In the
schema definition for `type`, `amount`, and `percentage`, follow the same
`error` pattern used in `schemas.ts` (for example, `budgetSchema`) and branch on
`issue.input` to return the required-vs-invalid message for each field. Use the
existing symbols `BountyType`, `z.nativeEnum`, and the form schema object to
locate and update the affected validators.

In `@hooks/__tests__/use-create-bounty.test.tsx`:
- Around line 26-41: The `@/lib/graphql/generated` mock is replacing the whole
module, so `BountyType` is missing and `MOCK_INPUT` crashes when
`BountyType.FixedPrice` is evaluated. Update the `jest.mock` factory in
`use-create-bounty.test.tsx` to preserve the module’s वास्तविक exports while
only overriding `useCreateBountyMutation`. Keep `BountyType` available from the
original generated module so the test setup can resolve it at import time.

---

Nitpick comments:
In `@app/bounty/create/page.tsx`:
- Around line 8-15: The `ExtendedUser` type and sponsor/org-member gating logic
are duplicated across multiple components, so extract them into one shared
module and reuse them from `app/bounty/create/page.tsx`,
`components/global-navbar.tsx`, and `components/bounty/bounty-create-form.tsx`.
Move the session-user cast (`session?.user as ExtendedUser`) and the
`isSponsorOrOrgMember(user)` derivation into that shared helper so the
role/organizations contract stays consistent in one place. Update the affected
components to import the shared type and helper instead of redefining the
interface locally.

In `@components/bounty/bounty-create-form.tsx`:
- Around line 256-263: The Github issue URL parsing helper
parseGithubIssueNumber currently calls parseInt without an explicit radix, which
can trip lint rules and lead to ambiguous parsing behavior. Update the
parseGithubIssueNumber function to pass radix 10 when converting match[1],
keeping the existing regex and return behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f152c07f-c9ba-4fcc-bc8b-a7c20df8c0b0

📥 Commits

Reviewing files that changed from the base of the PR and between 9e5dc91 and 690dc75.

📒 Files selected for processing (7)
  • app/bounty/create/page.tsx
  • components/bounty/bounty-create-form.tsx
  • components/bounty/forms/budget-input.tsx
  • components/bounty/forms/schemas.ts
  • components/global-navbar.tsx
  • hooks/__tests__/use-create-bounty.test.tsx
  • hooks/use-create-bounty.ts

Comment on lines +68 to +70
type: z.nativeEnum(BountyType, {
required_error: "Please select a bounty type"
}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

In Zod 4, does z.object field schema still honor required_error, or must it use the unified error callback? Is z.ZodIssueCode.custom still valid in Zod 4?

💡 Result:

In Zod 4, the required_error and invalid_type_error parameters have been dropped [1]. Zod 4 standardizes error customization under a single, unified error parameter [2][1], which is the recommended way to handle custom error messaging within schema definitions. Regarding z.ZodIssueCode.custom: Yes, it remains a valid issue code in Zod 4 and continues to be used for custom validation issues, such as those generated by refinements [3][4]. While Zod 4 allows for more flexibility with issue codes, z.ZodIssueCode.custom is explicitly defined in the core types and remains supported for custom logic and error handling [3]. Summary of changes: - Required_error/invalid_type_error: Dropped in Zod 4 in favor of the unified error parameter [1]. - Unified error parameter: This now serves as the standard for schema-level error customization [2][1]. - z.ZodIssueCode.custom: Remains valid and active for custom refinements and issue generation [3].

Citations:


🏁 Script executed:

cat package.json | grep -A 2 -B 2 zod

Repository: boundlessfi/bounties

Length of output: 260


🏁 Script executed:

find . -name "schemas.ts" -o -name "schema.ts" -o -name "validation.ts" | head -5

Repository: boundlessfi/bounties

Length of output: 484


🏁 Script executed:

cat components/bounty/bounty-create-form.tsx | sed -n '65,100p'

Repository: boundlessfi/bounties

Length of output: 1390


🏁 Script executed:

cat components/bounty/forms/schemas.ts | head -100

Repository: boundlessfi/bounties

Length of output: 2231


Zod 4 API mismatch: required_error is unsupported; switch to the error callback.

The project uses zod@4.3.5, where required_error and invalid_type_error were removed in favor of the unified error callback. The current schema definitions silently ignore these options, resulting in generic Zod error messages instead of the intended custom ones.

Align with the existing schemas.ts pattern (e.g., budgetSchema) by replacing required_error with an error function:

  • type field (Line 68): Replace z.nativeEnum(BountyType, { required_error: ... }) with z.nativeEnum(BountyType, { error: (issue) => issue.input === undefined ? "Please select a bounty type" : "Invalid type" }).
  • amount field (Line 80): Replace .number({ required_error: ... }) with .number({ error: (issue) => issue.input === undefined ? "Amount is required" : "Amount must be a number" }).
  • percentage field (Line 95): Replace .number({ required_error: ... }) with .number({ error: (issue) => issue.input === undefined ? "% is required" : "Percentage must be a number" }).

Note: While z.nativeEnum remains available for TypeScript enums in Zod 4, the error configuration syntax has changed to the error callback as shown above.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/bounty/bounty-create-form.tsx` around lines 68 - 70, The bounty
form schema is using Zod 3-style `required_error` options that are ignored in
Zod 4, so update the validation messages in `bounty-create-form.tsx` to use the
`error` callback instead. In the schema definition for `type`, `amount`, and
`percentage`, follow the same `error` pattern used in `schemas.ts` (for example,
`budgetSchema`) and branch on `issue.input` to return the required-vs-invalid
message for each field. Use the existing symbols `BountyType`, `z.nativeEnum`,
and the form schema object to locate and update the affected validators.

Comment on lines +224 to +226
reward: {
amount: undefined as any,
asset: "USDC",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Avoid as any for the reward amount default (ESLint failing). Static analysis flags @typescript-eslint/no-explicit-any at Line 225. Cast through unknown (or model the field as number | undefined) to keep the empty initial state without any.

🔧 Proposed fix
       reward: {
-        amount: undefined as any,
+        amount: undefined as unknown as number,
         asset: "USDC",
       },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
reward: {
amount: undefined as any,
asset: "USDC",
reward: {
amount: undefined as unknown as number,
asset: "USDC",
🧰 Tools
🪛 ESLint

[error] 225-225: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/bounty/bounty-create-form.tsx` around lines 224 - 226, The default
reward amount in the bounty form currently uses an explicit any cast, which
trips linting. Update the initial state in bounty-create-form.tsx where the
reward object is built so amount is typed without any, either by modeling it as
number | undefined or by casting through unknown in the same spot. Keep the
empty default behavior intact while removing the no-explicit-any violation in
the form state setup.

Source: Linters/SAST tools

</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="">None</SelectItem>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

<SelectItem value=""> will crash at runtime (Radix constraint). Radix UI Select.Item forbids an empty-string value ("A <Select.Item /> must have a value prop that is not an empty string"). Both the projectId "None" option (Line 422) and the "Standard Bounty (No Window)" option (Line 490) use value="" and will throw when the dropdown renders. Use a sentinel value and map it back to undefined/"" in onSubmit.

🐛 Proposed direction
-                          <SelectItem value="">None</SelectItem>
+                          <SelectItem value="none">None</SelectItem>
-                            <SelectItem value="">Standard Bounty (No Window)</SelectItem>
+                            <SelectItem value="none">Standard Bounty (No Window)</SelectItem>

Then normalize in onSubmit (treat "none" as unset) and in the defaultValues/Select value wiring.

Confirm the Select primitive is Radix-based (shadcn/ui) where the empty-value constraint applies:

#!/bin/bash
fd -t f 'select.tsx' components/ui --exec sed -n '1,40p' {}

Also applies to: 490-490

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/bounty/bounty-create-form.tsx` at line 422, The SelectItem usage
with an empty string value will crash at runtime because the Radix-based Select
primitive does not allow empty-string item values. Update the Select setup in
bounty-create-form so the “None” and “Standard Bounty (No Window)” options use a
non-empty sentinel value, and normalize that sentinel back to unset/empty in the
form wiring. Adjust the relevant Select value/defaultValues handling and the
onSubmit path so the sentinel maps to undefined or "" consistently without ever
passing "" into SelectItem.

Comment on lines +26 to +41
jest.mock("@/lib/graphql/generated", () => ({
useCreateBountyMutation: (options: {
onSuccess: (data: unknown) => void;
onError: (error: unknown) => void;
}) => {
capturedOnSuccess = options.onSuccess;
capturedOnError = options.onError;
return {
mutate: mockMutate,
mutateAsync: mockMutateAsync,
isPending: false,
isError: false,
isSuccess: false,
};
},
}));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP 'export (enum|const) BountyType|BountyType' lib/graphql/generated.ts | head
fd -t f 'use-create-bounty.test.tsx' --exec sed -n '26,82p' {}

Repository: boundlessfi/bounties

Length of output: 2122


Mock omits BountyType, causing suite crash. The factory for @/lib/graphql/generated defines only useCreateBountyMutation but the test imports BountyType from that same module. Since BountyType is dereferenced at module evaluation time in MOCK_INPUT (BountyType.FixedPrice), the missing export resolves to undefined, throwing a TypeError that fails all tests.

🐛 Proposed fix: preserve real exports while overriding the mutation hook
 jest.mock("`@/lib/graphql/generated`", () => ({
+  ...jest.requireActual("`@/lib/graphql/generated`"),
   useCreateBountyMutation: (options: {
     onSuccess: (data: unknown) => void;
     onError: (error: unknown) => void;
   }) => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
jest.mock("@/lib/graphql/generated", () => ({
useCreateBountyMutation: (options: {
onSuccess: (data: unknown) => void;
onError: (error: unknown) => void;
}) => {
capturedOnSuccess = options.onSuccess;
capturedOnError = options.onError;
return {
mutate: mockMutate,
mutateAsync: mockMutateAsync,
isPending: false,
isError: false,
isSuccess: false,
};
},
}));
jest.mock("`@/lib/graphql/generated`", () => ({
...jest.requireActual("`@/lib/graphql/generated`"),
useCreateBountyMutation: (options: {
onSuccess: (data: unknown) => void;
onError: (error: unknown) => void;
}) => {
capturedOnSuccess = options.onSuccess;
capturedOnError = options.onError;
return {
mutate: mockMutate,
mutateAsync: mockMutateAsync,
isPending: false,
isError: false,
isSuccess: false,
};
},
}));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hooks/__tests__/use-create-bounty.test.tsx` around lines 26 - 41, The
`@/lib/graphql/generated` mock is replacing the whole module, so `BountyType` is
missing and `MOCK_INPUT` crashes when `BountyType.FixedPrice` is evaluated.
Update the `jest.mock` factory in `use-create-bounty.test.tsx` to preserve the
module’s वास्तविक exports while only overriding `useCreateBountyMutation`. Keep
`BountyType` available from the original generated module so the test setup can
resolve it at import time.

@Benjtalkshow Benjtalkshow left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the work on this, but there are a few blockers before merge:
pnpm tsc --noEmit and pnpm lint both fail (Zod v4 uses error: not
required_error:, the as UseFormReturn<FieldValues> casts are rejected, and
form.watch("type") needs useWatch per the lint rule). On the design side
the form drops deadline, startDate, endDate, and milestones at submit
since CreateBountyInput doesn't accept them, useUserRole is bypassed in
the page and navbar (please extend the hook to expose org membership instead),
and mock organizations and mockProjects are showing up in the production
flow. Please also fix the empty-string <SelectItem value=""> (Radix throws
on this), keep AQUA alongside EURC, and split the 805-line component into
per-step files under components/bounty/create-form/. Once those are
addressed, please attach screenshots of step 1, step 2, and the review screen
for each bounty type so I can verify the UI before re-reviewing.

@Danitello123
Danitello123 deleted the feature/sponsor-bounty-creation branch June 28, 2026 21:51
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.

Feature: Sponsor Bounty Creation Flow

2 participants