Fix build errors and UI#195
Conversation
- 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
|
@Danitello123 is attempting to deploy a commit to the Threadflow Team on Vercel. A member of the Team first needs to authorize it. |
📝 WalkthroughWalkthroughThis pull request implements the bounty creation UI flow by introducing a new Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (4)
app/bounty/create/page.tsxcomponents/bounty/bounty-create-form.tsxcomponents/global-navbar.tsxhooks/use-create-bounty.ts
| const user = await getCurrentUser(); | ||
|
|
||
| if (!user) { | ||
| redirect("/auth"); | ||
| } |
There was a problem hiding this comment.
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.
| 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; | ||
| } | ||
| } |
There was a problem hiding this comment.
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(""); |
There was a problem hiding this comment.
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.
| 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."; | ||
| } |
There was a problem hiding this comment.
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.
| <Button | ||
| variant="ghost" | ||
| size="icon" | ||
| type="button" | ||
| onClick={() => | ||
| setMilestones((prev) => prev.filter((_, idx) => idx !== index)) | ||
| } | ||
| > | ||
| × | ||
| </Button> |
There was a problem hiding this comment.
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.
| <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.
| {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> | ||
| )} |
There was a problem hiding this comment.
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.
Benjtalkshow
left a comment
There was a problem hiding this comment.
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.
|
Hello @Danitello123 |
Closes #181
Summary by CodeRabbit