Inngest ,AI SDK and Pinecone setup complete#13
Conversation
- Implemented a checks workflow to run linting, type-checking, tests, and build on pull requests and main branch pushes. - Created a deploy workflow to automate deployment to Vercel on pushes to the main branch. - Added VSCode settings for GitHub Actions schema validation. - Introduced a CONTRIBUTING.md file with guidelines for contributing to the project. - Consolidated project documentation into a single README.md for easier access. - Added new logo assets for branding.
…turtle into working-branch
… and functionality
…turtle into working-branch
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR refactors the dashboard to use server-side async components with Suspense instead of client-side React Query, introduces form management utilities with validation, adds authentication middleware, creates reusable form UI components, enhances type safety across the codebase, and updates documentation and build configuration. Changes
Sequence Diagram(s)sequenceDiagram
participant Browser
participant Middleware
participant Server
participant Suspense
participant Component
participant Database
Browser->>Middleware: Request /dashboard
Middleware->>Server: Session validation via getSession
alt Session exists
Server-->>Middleware: Session confirmed
Middleware-->>Browser: Allow request
Browser->>Server: Render Page component
Server->>Suspense: Render DashboardStats (pending)
Suspense->>Browser: Show DashboardStatsSkeleton
Component->>Database: Fetch user stats (Octokit)
Database-->>Component: Stats data
Component-->>Suspense: Resolve with StatCards
Suspense->>Browser: Replace skeleton with stats
else No session
Server-->>Middleware: No session
Middleware-->>Browser: Redirect to /login
end
sequenceDiagram
participant User
participant ProfileForm
participant useProfile Hook
participant useForm Hook
participant API
participant Toast
User->>ProfileForm: Render profile settings
ProfileForm->>useProfile: Initialize hook
useProfile->>API: Fetch user profile (TanStack Query)
API-->>useProfile: Profile data
useProfile->>useForm: Initialize form with profile data
User->>ProfileForm: Edit name field
ProfileForm->>useForm: setValue('name', newValue)
useForm-->>useForm: Clear error for name field
User->>ProfileForm: Submit form
ProfileForm->>useForm: handleSubmit(e)
useForm->>useForm: Validate data via validation schema
alt Validation passes
useForm->>API: Call updateUserProfile
API-->>useForm: Success
useForm->>Toast: Show success message
else Validation fails
useForm-->>ProfileForm: Display field errors
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~55 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
README.md (1)
119-128: Version inconsistency: README states Next.js 15, but project uses Next.js 16.The Tech Stack section mentions "Next.js 15 with App Router" but the project appears to be using Next.js 16.0.10 based on the library context. Update to reflect the actual version.
📝 Proposed fix
## Tech Stack -- **Framework:** Next.js 15 with App Router +- **Framework:** Next.js 16 with App Router - **Language:** TypeScriptsrc/components/github/repository-list.tsx (1)
43-55: Toast message should reflect "Disconnect All" action.Line 49 shows "Repository disconnected successfully" but this is the
disconnectAllMutationhandler. The message should indicate multiple repositories were disconnected.📝 Proposed fix
onSuccess: (result) => { if(result?.success){ queryClient.invalidateQueries({ queryKey: ['connected-repositories'] }); queryClient.invalidateQueries({ queryKey: ['repository-stats'] }); - toast.success("Repository disconnected successfully"); + toast.success("All repositories disconnected successfully"); setDisconnectAllOpen(false); } else { - toast.error("Failed to disconnect repository"); + toast.error("Failed to disconnect repositories"); } }
🤖 Fix all issues with AI agents
In `@CONTRIBUTING.md`:
- Around line 1-53: The CONTRIBUTING.md references missing files
CODE_OF_CONDUCT.md and .env.example (also mentioned via README.md); fix by
either adding those files to the repo or updating CONTRIBUTING.md/README.md to
remove or replace those references with existing resources (e.g., link to actual
code of conduct file or a different env example), ensuring the text "Read and
follow `CODE_OF_CONDUCT.md`" and "Follow `.env.example` instructions in
`README.md`" are corrected to point to real files or guidance.
In `@docs/README.md`:
- Around line 42-51: The README's Project Structure incorrectly lists "api/" as
a top-level sibling to "app/"; update the section so API routes are shown under
the App Router (e.g., reflect "src/app/api/" rather than "src/api/") and ensure
the example tree matches actual routes used in the repo (for example, the
existing route file src/app/api/user/github-account/route.ts); modify the bullet
for api to indicate it's within app (or remove the standalone api/ entry) and
keep other folder names unchanged.
In `@src/app/`(dashboard)/dashboard/page.tsx:
- Around line 95-112: The ContributionGraphServer component currently only
renders summary text and a legend but never uses contributionData.contributions;
update ContributionGraphServer to iterate over contributionData.contributions
and render the actual contribution grid (e.g., map rows/weeks and cells/days),
using unique keys and appropriate classes/aria attributes so each cell reflects
its contribution count and intensity (class or style) based on
contribution.count; ensure you reference contributionData.contributions, produce
a consistent grid structure (rows/columns) and include tooltip/aria-label
showing the date and count for each cell.
In `@src/app/api/user/github-account/route.ts`:
- Around line 24-25: The catch block currently swallows all exceptions which can
hide real failures; change the anonymous catch to catch (err) in the route
handler and conditionally suppress only expected PPR-related errors (e.g., by
implementing or using a helper like isPprExpectedError(err) or checking known
error codes/messages), otherwise call your logger (e.g., console.error or
requestLogger.error) with the full error and context; update the catch in
src/app/api/user/github-account/route.ts around the GitHub account handling to
perform this selective logging.
In `@src/hooks/useForm.ts`:
- Around line 12-53: The hook freezes initialFormData at mount which breaks
isDirty and reset after async hydration; change initialFormData from a const to
a React state pair (e.g., [initialFormData, setInitialFormData]) and update the
UseFormReturn type so reset accepts an optional newBaseline (reset:
(newBaseline?: Partial<T>) => void). Implement reset to, when newBaseline is
provided, call setInitialFormData(newBaseline as T) and setFormData(newBaseline
as T), otherwise revert to the current initialFormData; ensure isDirty continues
to compare formData to the state-backed initialFormData and update any
dependencies (e.g., setValue/setData callbacks) to reference the state variable
where needed.
In `@src/lib/validation.ts`:
- Around line 32-34: The custom validator is being called with the wrong second
argument ({ fieldName }) in validateField; change validateField to accept the
full data object (e.g., add a parameter data: Record<string, unknown> |
undefined) and call rules.custom(value, data) so rules.custom receives the
complete form data; also update validateData to forward the data when it calls
validateField so cross-field validators can inspect other fields via the second
argument.
In `@src/module/dashboard/actions/index.ts`:
- Around line 107-108: Replace the hardcoded const totalRepos = 30 with real
retrieval or an explicit placeholder: call your repository/count API or model
(e.g., RepositoryModel.countDocuments or RepoService.getConnectedRepoCount) and
assign its awaited result to totalRepos, or set totalRepos = null and document
that the UI should handle a null placeholder; update any surrounding function
(the dashboard action in this file where totalRepos is defined) to await the
count and handle errors (fallback to null and log) so the value is accurate
instead of hardcoded.
🧹 Nitpick comments (12)
src/middleware.ts (2)
21-23: Prefix matching withstartsWithmay be overly broad.Routes like
/dashboard-adminor/pricing-tablewould incorrectly match/dashboardand/pricing. Consider using exact matching or more precise route patterns.♻️ Suggested fix with stricter matching
- const isProtectedRoute = protectedRoutes.some(route => - pathname.startsWith(route) - ) + const isProtectedRoute = protectedRoutes.some(route => + pathname === route || pathname.startsWith(`${route}/`) + )
34-36: Consider avoiding logging potentially sensitive error details.Logging the full error object may expose sensitive information in server logs. Consider logging only the error message or a sanitized version.
♻️ Suggested fix
} catch (error) { - console.error('Auth middleware error:', error) + console.error('Auth middleware error:', error instanceof Error ? error.message : 'Unknown error') return NextResponse.redirect(new URL('/login', request.url)) }src/module/dashboard/actions/index.ts (3)
71-77: Silent error swallowing hides genuine failures.Catching all errors and returning defaults makes debugging difficult. Consider logging the error in development or distinguishing between expected (prerender) and unexpected errors.
♻️ Suggested improvement
- } catch { + } catch (error) { // During prerendering, return default values (expected PPR behavior) + if (process.env.NODE_ENV === 'development') { + console.warn('getContributionGraph error:', error); + } return { contributions: [], totalContributions: 0, };
150-158: Inconsistent error handling compared to other functions.
getContributionGraphandgetDashboardStatsreturn early with defaults when session is missing, butgetMonthlyActivitythrows and relies on the outer catch. For consistency and clarity, consider using the same pattern.♻️ Suggested fix for consistency
try { const session = await auth.api.getSession({ headers: await headers() }); if (!session) { - throw new Error("Unauthorized"); + // During prerendering, return default values + return getDefaultMonthlyData(); } const token = await getGithubToken(); if (!token) { - throw new Error("No GitHub token found"); + return getDefaultMonthlyData(); }Then extract the default data generation to a helper function to avoid duplication:
function getDefaultMonthlyData() { const monthsNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; const now = new Date(); const defaultData = []; for (let i = 5; i >= 0; i--) { const date = new Date(now.getFullYear(), now.getMonth() - i, 1); defaultData.push({ name: `${monthsNames[date.getMonth()]} ${date.getFullYear()}`, commits: 0, prs: 0, reviews: 0, }); } return defaultData; }
145-148: Outdated JSDoc - function no longer throws or returns null.The docstring states
@throws {Error}andReturns null if an error occurs, but the function now returns default data on error and doesn't propagate exceptions.📝 Suggested doc update
* `@returns` A promise that resolves to an array of objects, each containing the month name - * (e.g., "Jan 2023"), and the counts for commits, PRs, and reviews. Returns null if an error occurs. - * `@throws` {Error} If the user is unauthorized or no GitHub token is found. + * (e.g., "Jan 2023"), and the counts for commits, PRs, and reviews. Returns default zero-value data + * during prerendering or if an error occurs. */src/app/(dashboard)/settings/page.tsx (1)
64-81: Suspense fallback duplicates Card structure - consider extracting a skeleton component.The fallback Card mirrors the GithubStatus Card structure. Extracting a
GithubStatusSkeletoncomponent would improve maintainability and ensure consistency when the Card design changes.♻️ Suggested refactor
function GithubStatusSkeleton() { return ( <Card> <CardHeader> <CardTitle className="flex items-center gap-2"> <Github className="h-5 w-5" /> GitHub Integration </CardTitle> <CardDescription> Connect your GitHub account to view contribution data and statistics. </CardDescription> </CardHeader> <CardContent> <div className="animate-pulse">Loading...</div> </CardContent> </Card> ); } // Usage <Suspense fallback={<GithubStatusSkeleton />}> <GithubStatus /> </Suspense>src/app/(dashboard)/dashboard/page.tsx (2)
88-89: TheisLoading={false}prop is redundant in server component context.Since Suspense handles the loading state, passing
isLoading={false}is unnecessary. Consider removing this prop or updatingMonthlyActivityChartto not require it when used in server components.♻️ Suggested fix
- <MonthlyActivityChart data={monthlyActivity} isLoading={false} /> + <MonthlyActivityChart data={monthlyActivity} />
147-185: Consider adding an error boundary for resilience.If any async component fails after hydration, the error could propagate and break the entire page. Wrapping sections in error boundaries would improve resilience.
♻️ Example error boundary usage
import { ErrorBoundary } from 'react-error-boundary' function ErrorFallback({ error, resetErrorBoundary }) { return ( <Card> <CardContent className="py-8 text-center"> <p className="text-muted-foreground">Failed to load data</p> <Button variant="outline" onClick={resetErrorBoundary}>Retry</Button> </CardContent> </Card> ) } // Usage <ErrorBoundary FallbackComponent={ErrorFallback}> <Suspense fallback={<DashboardStatsSkeleton />}> <DashboardStats /> </Suspense> </ErrorBoundary>src/app/api/webhooks/github/route.ts (2)
48-56: Inconsistent query: missingselectclause on second lookup.Line 49-52 uses
select: { hookSecret: true }for efficiency, but line 55 fetches the entire repository record. For consistency and to minimize data transfer, add the sameselectclause.♻️ Proposed fix
if (!repoRecord && parsedBody?.repository && typeof parsedBody.repository === 'object' && 'full_name' in parsedBody.repository) { - repoRecord = await prisma.repository.findFirst({ where: { fullName: (parsedBody.repository as { full_name: string }).full_name } }); + repoRecord = await prisma.repository.findFirst({ + where: { fullName: (parsedBody.repository as { full_name: string }).full_name }, + select: { hookSecret: true } + }); }
84-94: TODO: PR processing is not yet implemented.The
pull_requestevent handler logs the event but doesn't process it further. Consider implementing the enqueue/processing logic or tracking this as a follow-up task.Would you like me to help design the PR processing flow or open an issue to track this implementation?
src/module/github/github.ts (1)
140-148: Consider extracting duplicated 404 error handling.The same error handling pattern for 404 status appears in both
listWebhooksandcreateWebhookcatch blocks. A helper function would reduce duplication.♻️ Proposed helper
function throwIfPermissionError(error: unknown): never | void { if (error && typeof error === 'object' && 'status' in error) { const err = error as { status: number }; if (err.status === 404) { throw new Error("You don't have permission to create webhooks on this repository. Make sure you have admin access to the repository."); } } }Then use in catch blocks:
} catch (error: unknown) { - if (error && typeof error === 'object' && 'status' in error) { - const err = error as { status: number }; - if (err.status === 404) { - throw new Error("You don't have permission to create webhooks on this repository. Make sure you have admin access to the repository."); - } - } + throwIfPermissionError(error); throw error; }Also applies to: 168-176
src/app/(dashboard)/repositories/page.tsx (1)
24-39: Centralize repository types to avoid drift.These types are now defined here and in
src/module/repository/actions/index.ts, which risks divergence. Consider extracting them into a sharedrepository-types.ts(or similar) and importing viaimport typein both server and client modules.
| # Contributing to CodeTurtle | ||
|
|
||
| Thank you for considering contributing — we welcome and appreciate your help! | ||
|
|
||
| Please read these guidelines before opening issues or PRs to make the process fast and friendly for everyone. | ||
|
|
||
| ## Code of Conduct | ||
| Read and follow `CODE_OF_CONDUCT.md`. Be respectful and constructive. | ||
|
|
||
| ## How to Contribute | ||
| - For bugs: open an issue with a reproducible example, environment, and expected vs actual behavior. | ||
| - For feature ideas: open an issue describing the problem, the proposed solution, and any alternatives. | ||
| - For quick fixes and docs: open a PR directly against `main` from a feature branch. | ||
|
|
||
| ## Development Workflow | ||
| 1. Fork the repo and create a branch: `feature/<short-desc>` or `fix/<short-desc>`. | ||
| 2. Keep changes small and focused. | ||
| 3. Add tests when fixing bugs or adding features. | ||
| 4. Run linting and tests locally before opening a PR: | ||
|
|
||
| ```bash | ||
| bun install | ||
| bun run lint --if-present | ||
| bun run test --if-present | ||
| ``` | ||
|
|
||
| 5. Open a PR and include: | ||
| - Summary of changes | ||
| - Testing steps | ||
| - Related issues | ||
|
|
||
| ## Pull Request Checklist | ||
| - [ ] Branch off `main` and keep PR small | ||
| - [ ] Updated/added tests where applicable | ||
| - [ ] Linting passes locally | ||
| - [ ] Descriptive PR title and body | ||
| - [ ] Linked to an issue when appropriate | ||
|
|
||
| ## Commit Messages | ||
| Use short, present-tense messages. Prefer conventional commits (e.g., `feat:`, `fix:`, `chore:`). | ||
|
|
||
| ## Review & Merging | ||
| - At least one approving review required for changes to `main`. | ||
| - Maintainers may squash or rebase when merging. | ||
|
|
||
| ## Local Environment & Secrets | ||
| Follow `.env.example` instructions in `README.md`. Do not commit credentials or tokens. | ||
|
|
||
| ## Reporting Security Issues | ||
| If you find a security vulnerability, please report it privately via GitHub Security Advisories or by contacting the maintainer directly. Please do not open a public issue. | ||
|
|
||
| --- | ||
| Thanks again — we look forward to your contribution! No newline at end of file |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check for all referenced files
echo "=== Checking for CODE_OF_CONDUCT.md ==="
fd -t f "CODE_OF_CONDUCT.md" --max-depth 1
echo "=== Checking for README.md ==="
fd -t f "README.md" --max-depth 1
echo "=== Checking for .env.example ==="
fd -t f ".env.example" --max-depth 1
echo "=== List root directory files ==="
ls -la | head -20Repository: Ansh-dhanani/codeturtle
Length of output: 1448
Add missing referenced files or update references.
The CONTRIBUTING.md references two files that do not exist in the repository:
CODE_OF_CONDUCT.md(referenced in "Read and followCODE_OF_CONDUCT.md").env.example(referenced in "Follow.env.exampleinstructions inREADME.md")
Either create these files or update the documentation to remove the references to avoid confusing contributors.
🤖 Prompt for AI Agents
In `@CONTRIBUTING.md` around lines 1 - 53, The CONTRIBUTING.md references missing
files CODE_OF_CONDUCT.md and .env.example (also mentioned via README.md); fix by
either adding those files to the repo or updating CONTRIBUTING.md/README.md to
remove or replace those references with existing resources (e.g., link to actual
code of conduct file or a different env example), ensuring the text "Read and
follow `CODE_OF_CONDUCT.md`" and "Follow `.env.example` instructions in
`README.md`" are corrected to point to real files or guidance.
| ## Project Structure | ||
| ```text | ||
| src/ | ||
| ├── app/ # Next.js App Router | ||
| ├── api/ # API routes (auth, webhooks) | ||
| ├── lib/ # Auth, prisma client, utilities | ||
| ├── components/ # Shared UI components | ||
| ├── prisma/ # Prisma schema and migrations | ||
| └── styles/ # Global styles | ||
| ``` |
There was a problem hiding this comment.
Project structure appears inconsistent with Next.js App Router conventions.
The structure shows api/ as a sibling directory to app/, but based on Next.js App Router conventions and the files in this PR (e.g., src/app/api/user/github-account/route.ts), API routes are located inside src/app/api/.
Suggested fix
src/
├── app/ # Next.js App Router
-├── api/ # API routes (auth, webhooks)
+│ └── api/ # API routes (auth, webhooks)
├── lib/ # Auth, prisma client, utilities
├── components/ # Shared UI components
-├── prisma/ # Prisma schema and migrations
└── styles/ # Global styles
+prisma/ # Prisma schema and migrations (at root)📝 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.
| ## Project Structure | |
| ```text | |
| src/ | |
| ├── app/ # Next.js App Router | |
| ├── api/ # API routes (auth, webhooks) | |
| ├── lib/ # Auth, prisma client, utilities | |
| ├── components/ # Shared UI components | |
| ├── prisma/ # Prisma schema and migrations | |
| └── styles/ # Global styles | |
| ``` | |
| src/ | |
| ├── app/ # Next.js App Router | |
| │ └── api/ # API routes (auth, webhooks) | |
| ├── lib/ # Auth, prisma client, utilities | |
| ├── components/ # Shared UI components | |
| └── styles/ # Global styles | |
| prisma/ # Prisma schema and migrations (at root) |
🤖 Prompt for AI Agents
In `@docs/README.md` around lines 42 - 51, The README's Project Structure
incorrectly lists "api/" as a top-level sibling to "app/"; update the section so
API routes are shown under the App Router (e.g., reflect "src/app/api/" rather
than "src/api/") and ensure the example tree matches actual routes used in the
repo (for example, the existing route file
src/app/api/user/github-account/route.ts); modify the bullet for api to indicate
it's within app (or remove the standalone api/ entry) and keep other folder
names unchanged.
| async function ContributionGraphServer() { | ||
| const contributionData = await getContributionGraph() | ||
|
|
||
| const { | ||
| data: stats, | ||
| isLoading: isStatsLoading, | ||
| } = useQuery({ | ||
| queryKey: ['dashboard-stats'], | ||
| queryFn: async () => await getDashboardStats(), | ||
| refetchOnWindowFocus: false, | ||
| }) | ||
| return ( | ||
| <div className="w-full"> | ||
| <div className="text-sm text-muted-foreground mb-2"> | ||
| {contributionData.totalContributions} contributions in the last year | ||
| </div> | ||
| <div className="text-xs text-muted-foreground"> | ||
| Less <span className="inline-block w-2 h-2 bg-muted rounded-sm mx-1"></span> | ||
| <span className="inline-block w-2 h-2 bg-blue-200 rounded-sm mx-1"></span> | ||
| <span className="inline-block w-2 h-2 bg-blue-300 rounded-sm mx-1"></span> | ||
| <span className="inline-block w-2 h-2 bg-blue-400 rounded-sm mx-1"></span> | ||
| <span className="inline-block w-2 h-2 bg-blue-500 rounded-sm mx-1"></span> More | ||
| </div> | ||
| </div> | ||
| ) | ||
| } |
There was a problem hiding this comment.
ContributionGraphServer only renders summary text, not the actual contribution graph.
The component shows total contributions and a legend but doesn't render the contributions array data. The actual contribution grid visualization appears to be missing.
Do you want me to help implement the contribution graph grid visualization using the contributionData.contributions array?
🤖 Prompt for AI Agents
In `@src/app/`(dashboard)/dashboard/page.tsx around lines 95 - 112, The
ContributionGraphServer component currently only renders summary text and a
legend but never uses contributionData.contributions; update
ContributionGraphServer to iterate over contributionData.contributions and
render the actual contribution grid (e.g., map rows/weeks and cells/days), using
unique keys and appropriate classes/aria attributes so each cell reflects its
contribution count and intensity (class or style) based on contribution.count;
ensure you reference contributionData.contributions, produce a consistent grid
structure (rows/columns) and include tooltip/aria-label showing the date and
count for each cell.
| } catch { | ||
| // Suppress logging for expected PPR behavior |
There was a problem hiding this comment.
Silencing all errors may mask real issues.
While suppressing logs for expected PPR behavior is reasonable, this catch block now silences all errors indiscriminately, including database failures, network issues, or auth errors. Consider logging unexpected errors while filtering out known PPR-related ones.
Suggested approach
- } catch {
- // Suppress logging for expected PPR behavior
+ } catch (error) {
+ // Only suppress logging for expected PPR/prerender behavior
+ if (process.env.NODE_ENV === 'development') {
+ console.error("Error checking GitHub account:", error);
+ }Alternatively, if specific error types are expected during PPR, check for those specifically before suppressing.
📝 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.
| } catch { | |
| // Suppress logging for expected PPR behavior | |
| } catch (error) { | |
| // Only suppress logging for expected PPR/prerender behavior | |
| if (process.env.NODE_ENV === 'development') { | |
| console.error("Error checking GitHub account:", error); | |
| } | |
| } |
🤖 Prompt for AI Agents
In `@src/app/api/user/github-account/route.ts` around lines 24 - 25, The catch
block currently swallows all exceptions which can hide real failures; change the
anonymous catch to catch (err) in the route handler and conditionally suppress
only expected PPR-related errors (e.g., by implementing or using a helper like
isPprExpectedError(err) or checking known error codes/messages), otherwise call
your logger (e.g., console.error or requestLogger.error) with the full error and
context; update the catch in src/app/api/user/github-account/route.ts around the
GitHub account handling to perform this selective logging.
| interface UseFormReturn<T extends Record<string, unknown>> { | ||
| data: T | ||
| errors: Record<string, string> | ||
| isSubmitting: boolean | ||
| isDirty: boolean | ||
| setValue: <K extends keyof T>(key: K, value: T[K]) => void | ||
| setData: (data: Partial<T>) => void | ||
| handleSubmit: (e: React.FormEvent) => void | ||
| reset: () => void | ||
| } | ||
|
|
||
| export function useForm<T extends Record<string, unknown>>({ | ||
| initialData = {} as Partial<T>, | ||
| validate, | ||
| onSubmit, | ||
| onSuccess, | ||
| onError, | ||
| }: UseFormOptions<T>): UseFormReturn<T> { | ||
| const [formData, setFormData] = React.useState<T>(initialData as T) | ||
| const [initialFormData] = React.useState<T>(initialData as T) | ||
| const [errors, setErrors] = React.useState<Record<string, string>>({}) | ||
|
|
||
| const isDirty = useMemo(() => { | ||
| return JSON.stringify(formData) !== JSON.stringify(initialFormData) | ||
| }, [formData, initialFormData]) | ||
|
|
||
| const setValue = useCallback(<K extends keyof T>(key: K, value: T[K]) => { | ||
| setFormData(prev => ({ ...prev, [key]: value })) | ||
| // Clear error when user starts typing | ||
| if (errors[key as string]) { | ||
| setErrors(prev => ({ ...prev, [key as string]: '' })) | ||
| } | ||
| }, [errors]) | ||
|
|
||
| const setData = useCallback((data: Partial<T>) => { | ||
| setFormData(prev => ({ ...prev, ...data })) | ||
| }, []) | ||
|
|
||
| const reset = useCallback(() => { | ||
| setFormData(initialFormData) | ||
| setErrors({}) | ||
| }, [initialFormData]) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "useForm.ts" -o -name "useForm.js"Repository: Ansh-dhanani/codeturtle
Length of output: 89
🏁 Script executed:
cat -n ./src/hooks/useForm.tsRepository: Ansh-dhanani/codeturtle
Length of output: 3141
🏁 Script executed:
find . -type f \( -name "*.test.ts*" -o -name "*.spec.ts*" \) | grep -i formRepository: Ansh-dhanani/codeturtle
Length of output: 49
🏁 Script executed:
find . -type f \( -name "*.test.ts*" -o -name "*.spec.ts*" \) | head -10Repository: Ansh-dhanani/codeturtle
Length of output: 49
🏁 Script executed:
rg "useForm" --type ts --type tsx -lRepository: Ansh-dhanani/codeturtle
Length of output: 94
🏁 Script executed:
rg "useForm" --type ts -B 2 -A 5Repository: Ansh-dhanani/codeturtle
Length of output: 4746
🏁 Script executed:
cat -n ./src/hooks/useProfile.tsRepository: Ansh-dhanani/codeturtle
Length of output: 2193
Update baseline after async data loads to keep isDirty accurate.
initialFormData is frozen at mount (line 31), so when callers hydrate data asynchronously (e.g., useProfile fetches user data), isDirty flips true and reset() returns to the old empty defaults instead of the loaded baseline. Allow reset to accept an optional new baseline and update initialFormData accordingly.
🛠️ Suggested fix
interface UseFormReturn<T extends Record<string, unknown>> {
data: T
errors: Record<string, string>
isSubmitting: boolean
isDirty: boolean
setValue: <K extends keyof T>(key: K, value: T[K]) => void
setData: (data: Partial<T>) => void
handleSubmit: (e: React.FormEvent) => void
- reset: () => void
+ reset: (nextInitialData?: T) => void
}
export function useForm<T extends Record<string, unknown>>({
initialData = {} as Partial<T>,
validate,
onSubmit,
onSuccess,
onError,
}: UseFormOptions<T>): UseFormReturn<T> {
const [formData, setFormData] = React.useState<T>(initialData as T)
- const [initialFormData] = React.useState<T>(initialData as T)
+ const [initialFormData, setInitialFormData] = React.useState<T>(initialData as T)
const [errors, setErrors] = React.useState<Record<string, string>>({})
const reset = useCallback((nextInitialData?: T) => {
+ const next = (nextInitialData ?? initialFormData) as T
+ setInitialFormData(next)
- setFormData(initialFormData)
+ setFormData(next)
setErrors({})
- }, [initialFormData])
+ }, [initialFormData])Callers can then do form.reset(loadedData) after async loads to keep isDirty accurate and ensure resets work correctly.
📝 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.
| interface UseFormReturn<T extends Record<string, unknown>> { | |
| data: T | |
| errors: Record<string, string> | |
| isSubmitting: boolean | |
| isDirty: boolean | |
| setValue: <K extends keyof T>(key: K, value: T[K]) => void | |
| setData: (data: Partial<T>) => void | |
| handleSubmit: (e: React.FormEvent) => void | |
| reset: () => void | |
| } | |
| export function useForm<T extends Record<string, unknown>>({ | |
| initialData = {} as Partial<T>, | |
| validate, | |
| onSubmit, | |
| onSuccess, | |
| onError, | |
| }: UseFormOptions<T>): UseFormReturn<T> { | |
| const [formData, setFormData] = React.useState<T>(initialData as T) | |
| const [initialFormData] = React.useState<T>(initialData as T) | |
| const [errors, setErrors] = React.useState<Record<string, string>>({}) | |
| const isDirty = useMemo(() => { | |
| return JSON.stringify(formData) !== JSON.stringify(initialFormData) | |
| }, [formData, initialFormData]) | |
| const setValue = useCallback(<K extends keyof T>(key: K, value: T[K]) => { | |
| setFormData(prev => ({ ...prev, [key]: value })) | |
| // Clear error when user starts typing | |
| if (errors[key as string]) { | |
| setErrors(prev => ({ ...prev, [key as string]: '' })) | |
| } | |
| }, [errors]) | |
| const setData = useCallback((data: Partial<T>) => { | |
| setFormData(prev => ({ ...prev, ...data })) | |
| }, []) | |
| const reset = useCallback(() => { | |
| setFormData(initialFormData) | |
| setErrors({}) | |
| }, [initialFormData]) | |
| interface UseFormReturn<T extends Record<string, unknown>> { | |
| data: T | |
| errors: Record<string, string> | |
| isSubmitting: boolean | |
| isDirty: boolean | |
| setValue: <K extends keyof T>(key: K, value: T[K]) => void | |
| setData: (data: Partial<T>) => void | |
| handleSubmit: (e: React.FormEvent) => void | |
| reset: (nextInitialData?: T) => void | |
| } | |
| export function useForm<T extends Record<string, unknown>>({ | |
| initialData = {} as Partial<T>, | |
| validate, | |
| onSubmit, | |
| onSuccess, | |
| onError, | |
| }: UseFormOptions<T>): UseFormReturn<T> { | |
| const [formData, setFormData] = React.useState<T>(initialData as T) | |
| const [initialFormData, setInitialFormData] = React.useState<T>(initialData as T) | |
| const [errors, setErrors] = React.useState<Record<string, string>>({}) | |
| const isDirty = useMemo(() => { | |
| return JSON.stringify(formData) !== JSON.stringify(initialFormData) | |
| }, [formData, initialFormData]) | |
| const setValue = useCallback(<K extends keyof T>(key: K, value: T[K]) => { | |
| setFormData(prev => ({ ...prev, [key]: value })) | |
| // Clear error when user starts typing | |
| if (errors[key as string]) { | |
| setErrors(prev => ({ ...prev, [key as string]: '' })) | |
| } | |
| }, [errors]) | |
| const setData = useCallback((data: Partial<T>) => { | |
| setFormData(prev => ({ ...prev, ...data })) | |
| }, []) | |
| const reset = useCallback((nextInitialData?: T) => { | |
| const next = (nextInitialData ?? initialFormData) as T | |
| setInitialFormData(next) | |
| setFormData(next) | |
| setErrors({}) | |
| }, [initialFormData]) |
🤖 Prompt for AI Agents
In `@src/hooks/useForm.ts` around lines 12 - 53, The hook freezes initialFormData
at mount which breaks isDirty and reset after async hydration; change
initialFormData from a const to a React state pair (e.g., [initialFormData,
setInitialFormData]) and update the UseFormReturn type so reset accepts an
optional newBaseline (reset: (newBaseline?: Partial<T>) => void). Implement
reset to, when newBaseline is provided, call setInitialFormData(newBaseline as
T) and setFormData(newBaseline as T), otherwise revert to the current
initialFormData; ensure isDirty continues to compare formData to the
state-backed initialFormData and update any dependencies (e.g., setValue/setData
callbacks) to reference the state variable where needed.
| if (rules.custom) { | ||
| return rules.custom(value, { fieldName }) | ||
| } |
There was a problem hiding this comment.
Bug: custom validator receives wrong second argument.
The custom validator signature expects data?: Record<string, unknown> (the form data), but line 33 passes { fieldName } instead. This breaks cross-field validation use cases.
🐛 Proposed fix
Update validateField to accept the full data object:
-export function validateField(value: unknown, rules: ValidationRule, fieldName: string): string | null {
+export function validateField(value: unknown, rules: ValidationRule, fieldName: string, data?: Record<string, unknown>): string | null {
if (rules.required && (!value || (typeof value === 'string' && value.trim() === ''))) {
return `${fieldName} is required`
}
// ... existing code ...
if (rules.custom) {
- return rules.custom(value, { fieldName })
+ return rules.custom(value, data)
}
return null
}And update validateData to pass the data:
for (const [field, rules] of Object.entries(schema)) {
- const error = validateField(data[field], rules, field.charAt(0).toUpperCase() + field.slice(1))
+ const error = validateField(data[field], rules, field.charAt(0).toUpperCase() + field.slice(1), data)
if (error) {🤖 Prompt for AI Agents
In `@src/lib/validation.ts` around lines 32 - 34, The custom validator is being
called with the wrong second argument ({ fieldName }) in validateField; change
validateField to accept the full data object (e.g., add a parameter data:
Record<string, unknown> | undefined) and call rules.custom(value, data) so
rules.custom receives the complete form data; also update validateData to
forward the data when it calls validateField so cross-field validators can
inspect other fields via the second argument.
| //todo to fetch total connected repos | ||
| const totalRepos = 30; |
There was a problem hiding this comment.
Hardcoded totalRepos = 30 should be addressed.
This placeholder value could mislead users. Consider fetching actual connected repository count from the database or returning a clearly marked placeholder (e.g., null with UI handling).
Do you want me to help implement fetching the actual connected repository count?
🤖 Prompt for AI Agents
In `@src/module/dashboard/actions/index.ts` around lines 107 - 108, Replace the
hardcoded const totalRepos = 30 with real retrieval or an explicit placeholder:
call your repository/count API or model (e.g., RepositoryModel.countDocuments or
RepoService.getConnectedRepoCount) and assign its awaited result to totalRepos,
or set totalRepos = null and document that the UI should handle a null
placeholder; update any surrounding function (the dashboard action in this file
where totalRepos is defined) to await the count and handle errors (fallback to
null and log) so the value is accurate instead of hardcoded.
Summary by CodeRabbit
Release Notes
Documentation
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.