Skip to content

Fix build errors and UI#195

Open
Danitello123 wants to merge 3 commits into
boundlessfi:mainfrom
Danitello123:fix-build-errors-and-ui
Open

Fix build errors and UI#195
Danitello123 wants to merge 3 commits into
boundlessfi:mainfrom
Danitello123:fix-build-errors-and-ui

Conversation

@Danitello123
Copy link
Copy Markdown

@Danitello123 Danitello123 commented Apr 27, 2026

Closes #181

Summary by CodeRabbit

  • New Features
    • Added bounty creation page with multi-step form for inputting title, type, description, organization, project, issue details, reward configuration, and milestone setup.
    • Updated navigation to display a "Create" link for authenticated users.

- Add missing imports for Wallet, LogIn, Fingerprint in global-navbar.tsx
- Fix Card closing tag in bounty-create-form.tsx
- Add Alert component imports
- Convert remaining UI elements to Card components for consistency
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 27, 2026

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

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 27, 2026

📝 Walkthrough

Walkthrough

This pull request implements the bounty creation UI flow by introducing a new /bounty/create route with a multi-step form component, adding server-side authentication gating, integrating a custom hook that wraps the create mutation with cache invalidation and success/error handling, and conditionally displaying a "Create" link in the navbar for authenticated users.

Changes

Cohort / File(s) Summary
Bounty Creation UI
app/bounty/create/page.tsx, components/bounty/bounty-create-form.tsx
New async page component with server-side auth redirect; large multi-step form component with 3-step wizard (basic info, rewards/deadline, review), client-side validation (required fields, URLs, date constraints, milestone percentage rules), milestone payout computation, and GitHub issue number extraction.
Bounty Creation Hook
hooks/use-create-bounty.ts
New React hook wrapping the create mutation with onSuccess callback that invalidates bounty list queries, displays success toast, and conditionally redirects to created bounty detail page; includes error toast on failure and convenience wrapper functions.
Navigation Integration
components/global-navbar.tsx
Updated to read authentication state via authClient.useSession(), conditionally render a "Create" navbar item linking to /bounty/create when user is authenticated.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • Benjtalkshow
  • 0xdevcollins

Poem

🐰 A bounty form springs forth with joy!
Three steps to create, without ploy—
Validate and compute with care,
Cache invalidate, redirect fair,
And navbar now shows "Create" CTA bright! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

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.
Title check ❓ Inconclusive The title 'Fix build errors and UI' is vague and generic; it doesn't clearly describe the main change (bounty creation flow implementation) but instead references only build fixes. Use a more specific title that captures the primary objective, such as 'Add sponsor bounty creation flow with multi-step form' or 'Implement bounty creation UI with form validation and mutation integration.'
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The PR implements all core requirements: multi-step bounty creation form with validation, creation hook with success/error handling and redirect, authentication gating in navbar, and support for multiple bounty types.
Out of Scope Changes check ✅ Passed All changes directly support the bounty creation flow; the modifications to global-navbar.tsx, creation of bounty-create-form.tsx, use-create-bounty hook, and create page are all within scope of issue #181.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/bounty/create/page.tsx`:
- Around line 6-10: The page currently only checks authentication via
getCurrentUser() and redirect(), but must enforce authorization so only sponsors
or organization members can access /bounty/create; update the server-side logic
in app/bounty/create/page.tsx (where getCurrentUser() is called) to additionally
verify the user's permission flags/roles (e.g., isSponsor, isOrgMember,
organizationMemberships or role property on the returned user object) and if the
check fails call redirect("/unauthorized") or redirect back to a safe page;
ensure the same permission logic used elsewhere (navbar or ACL helper) is reused
or centralized (extract to a helper like requireSponsorOrOrgMember(user) if
present) so the authorization cannot be bypassed by a signed-in user.

In `@components/bounty/bounty-create-form.tsx`:
- Around line 180-185: The validation currently compares new Date(deadline) <
today which allows selecting today's date; update the comparison in the
validation block (the code that sets nextErrors.deadline) to reject deadlines
that are not strictly after today by using <= instead of < (i.e., treat new
Date(deadline) <= today as invalid) so that selecting today's date will produce
the "Deadline must be in the future." error.
- Around line 44-62: The current URL validation accepts any syntactically valid
URL so non-GitHub links pass; update validation to require an actual GitHub
issue URL: change parseGithubIssueNumber to only return a number when the URL
hostname is github.com (or www.github.com) and the pathname matches the full
pattern /<owner>/<repo>/issues/<number>; add or replace isValidUrl with
isValidGithubIssueUrl (used in the fixed-price bounty flow referenced around
parseGithubIssueNumber and lines 156-162) to return true only when
parseGithubIssueNumber yields a number and the hostname/path check passes;
ensure callers use this stricter validator for GitHub-issue-required fields.
- Line 68: The form never collects or validates the category state—ensure the UI
includes a controlled input/select bound to category and setCategory (add it
into the step-one JSX where other fields live), update validateStepOne to
require category.trim() !== "" and return an error when empty, and confirm the
submission path (e.g., createBounty/handleSubmit) and the description synthesis
function that appends category actually read from this state so the created
bounty always includes the chosen category.
- Around line 505-514: The icon-only remove button in the BountyCreateForm (the
Button component that calls setMilestones((prev) => prev.filter((_, idx) => idx
!== index))) needs an accessible name so screen readers announce its purpose;
add an aria-label that includes the milestone position (for example "Remove
milestone {index + 1}" or use the milestone's identifier if available) to the
Button props so the action is described to assistive technology.

In `@components/global-navbar.tsx`:
- Around line 129-136: The Create CTA is shown to any authenticated user via the
isAuthenticated check; change the condition to only render the Link when the
current user has sponsor or organization-member privileges (e.g., replace
isAuthenticated with a role check like user?.isSponsor || user?.isOrgMember or a
helper such as canCreateBounty(session.user)). Update the conditional around the
Link (the element linking to "/bounty/create") to use that privilege check, and
if needed derive those flags from the session/user object or an authorization
utility so regular contributors no longer see the Create button.
🪄 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: e66b91b0-f5fe-421e-8af4-bfca22752778

📥 Commits

Reviewing files that changed from the base of the PR and between f73835a and b454eaa.

📒 Files selected for processing (4)
  • app/bounty/create/page.tsx
  • components/bounty/bounty-create-form.tsx
  • components/global-navbar.tsx
  • hooks/use-create-bounty.ts

Comment on lines +6 to +10
const user = await getCurrentUser();

if (!user) {
redirect("/auth");
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add server-side authorization, not just authentication.

This route only rejects anonymous users. Any signed-in user can still hit /bounty/create directly, which bypasses the sponsor/org-member restriction in the feature requirements. The permission check needs to live here too, not only in the navbar.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/bounty/create/page.tsx` around lines 6 - 10, The page currently only
checks authentication via getCurrentUser() and redirect(), but must enforce
authorization so only sponsors or organization members can access
/bounty/create; update the server-side logic in app/bounty/create/page.tsx
(where getCurrentUser() is called) to additionally verify the user's permission
flags/roles (e.g., isSponsor, isOrgMember, organizationMemberships or role
property on the returned user object) and if the check fails call
redirect("/unauthorized") or redirect back to a safe page; ensure the same
permission logic used elsewhere (navbar or ACL helper) is reused or centralized
(extract to a helper like requireSponsorOrOrgMember(user) if present) so the
authorization cannot be bypassed by a signed-in user.

Comment on lines +44 to +62
function parseGithubIssueNumber(url: string) {
try {
const parsed = new URL(url);
const match = parsed.pathname.match(/issues\/(\d+)/);
return match ? Number(match[1]) : undefined;
} catch {
return undefined;
}
}

function isValidUrl(url: string) {
if (!url) return false;
try {
new URL(url);
return true;
} catch {
return 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.

⚠️ Potential issue | 🟠 Major

Validate an actual GitHub issue URL here, not just any URL.

For fixed-price bounties, https://example.com/foo currently passes because isValidUrl() only checks URL syntax. That means githubIssueNumber can be undefined while the client still considers the form valid.

Possible fix
 function isValidUrl(url: string) {
-  if (!url) return false;
   try {
-    new URL(url);
-    return true;
+    const parsed = new URL(url);
+    return (
+      parsed.hostname === "github.com" &&
+      /^\/[^/]+\/[^/]+\/issues\/\d+$/.test(parsed.pathname)
+    );
   } catch {
     return false;
   }
 }

Also applies to: 156-162

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/bounty/bounty-create-form.tsx` around lines 44 - 62, The current
URL validation accepts any syntactically valid URL so non-GitHub links pass;
update validation to require an actual GitHub issue URL: change
parseGithubIssueNumber to only return a number when the URL hostname is
github.com (or www.github.com) and the pathname matches the full pattern
/<owner>/<repo>/issues/<number>; add or replace isValidUrl with
isValidGithubIssueUrl (used in the fixed-price bounty flow referenced around
parseGithubIssueNumber and lines 156-162) to return true only when
parseGithubIssueNumber yields a number and the hostname/path check passes;
ensure callers use this stricter validator for GitHub-issue-required fields.

const [step, setStep] = useState(1);
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [category, setCategory] = useState("");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

The required category field is still missing from the flow.

category is kept in state and even appended into the synthesized description, but the form never collects it and validateStepOne() never enforces it. That misses the stated client-side validation requirement, so every created bounty goes out without a real category value.

Also applies to: 104-106, 139-154, 310-337

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/bounty/bounty-create-form.tsx` at line 68, The form never collects
or validates the category state—ensure the UI includes a controlled input/select
bound to category and setCategory (add it into the step-one JSX where other
fields live), update validateStepOne to require category.trim() !== "" and
return an error when empty, and confirm the submission path (e.g.,
createBounty/handleSubmit) and the description synthesis function that appends
category actually read from this state so the created bounty always includes the
chosen category.

Comment on lines +180 to +185
if (deadline) {
const today = new Date();
today.setHours(0, 0, 0, 0);
if (new Date(deadline) < today) {
nextErrors.deadline = "Deadline must be in the future.";
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Treat “today” as invalid for future deadlines.

This check only rejects dates before today, so selecting today's date still passes. The requirement says the deadline must be in the future, so this should be <= today.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/bounty/bounty-create-form.tsx` around lines 180 - 185, The
validation currently compares new Date(deadline) < today which allows selecting
today's date; update the comparison in the validation block (the code that sets
nextErrors.deadline) to reject deadlines that are not strictly after today by
using <= instead of < (i.e., treat new Date(deadline) <= today as invalid) so
that selecting today's date will produce the "Deadline must be in the future."
error.

Comment on lines +505 to +514
<Button
variant="ghost"
size="icon"
type="button"
onClick={() =>
setMilestones((prev) => prev.filter((_, idx) => idx !== index))
}
>
×
</Button>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Give the remove-milestone button an accessible name.

This is an icon-only action with no label, so assistive tech won't announce what it does. Add an aria-label such as Remove milestone 2.

Possible fix
                   <Button
                     variant="ghost"
                     size="icon"
                     type="button"
+                    aria-label={`Remove milestone ${index + 1}`}
                     onClick={() =>
                       setMilestones((prev) => prev.filter((_, idx) => idx !== index))
                     }
                   >
📝 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
<Button
variant="ghost"
size="icon"
type="button"
onClick={() =>
setMilestones((prev) => prev.filter((_, idx) => idx !== index))
}
>
×
</Button>
<Button
variant="ghost"
size="icon"
type="button"
aria-label={`Remove milestone ${index + 1}`}
onClick={() =>
setMilestones((prev) => prev.filter((_, idx) => idx !== index))
}
>
×
</Button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/bounty/bounty-create-form.tsx` around lines 505 - 514, The
icon-only remove button in the BountyCreateForm (the Button component that calls
setMilestones((prev) => prev.filter((_, idx) => idx !== index))) needs an
accessible name so screen readers announce its purpose; add an aria-label that
includes the milestone position (for example "Remove milestone {index + 1}" or
use the milestone's identifier if available) to the Button props so the action
is described to assistive technology.

Comment on lines +129 to +136
{isAuthenticated && (
<Link
href="/bounty/create"
className="rounded-full border border-primary/30 bg-primary/5 px-3 py-1 text-sm font-semibold text-primary transition-colors hover:bg-primary/10"
>
Create
</Link>
)}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Restrict the CTA to sponsors/org members, not any session.

This now shows Create for every authenticated user, but the feature requirements call for sponsor-or-org-member gating. As written, regular contributors get a misleading entry point into a flow they should not have access to.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/global-navbar.tsx` around lines 129 - 136, The Create CTA is shown
to any authenticated user via the isAuthenticated check; change the condition to
only render the Link when the current user has sponsor or organization-member
privileges (e.g., replace isAuthenticated with a role check like user?.isSponsor
|| user?.isOrgMember or a helper such as canCreateBounty(session.user)). Update
the conditional around the Link (the element linking to "/bounty/create") to use
that privilege check, and if needed derive those flags from the session/user
object or an authorization utility so regular contributors no longer see the
Create button.

Copy link
Copy Markdown
Contributor

@Benjtalkshow Benjtalkshow left a comment

Choose a reason for hiding this comment

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

Thanks for closing the duplicate. The Card conversion progressed and rounded-3xl is gone. But the build is still failing on CI, and pnpm build produces two TS errors:

bounty-create-form.tsx:246 passes githubIssueUrl: ... || undefined, but the codegen type CreateBountyInput.githubIssueUrl is string (non-nullable). Either pass "" and rely on form validation to make the field required, or update the GraphQL schema to allow null and regenerate the types.

use-create-bounty.ts:35 reads .message on a value typed as unknown. Narrow it with error instanceof Error ? error.message : "Unable to create bounty...".

Please run pnpm build locally before pushing — both errors would surface immediately.

Also still open from the UI feedback: the 8 review tiles at lines 555-614 are still <div className="rounded-xl border ..."> — please convert them to <Card> for consistency, switch the form fields from <Field> to FormFieldWrapper (see application-dialog.tsx), and use the numbered step UI pattern from forms/milestone-builder.tsx for the wizard progress.

Once the build passes, this is close to ready.

@Benjtalkshow
Copy link
Copy Markdown
Contributor

Hello @Danitello123
Whats the update on this PR?

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