Skip to content

Conversation

@DeboraSerra
Copy link

@DeboraSerra DeboraSerra commented Sep 16, 2025

solves issue #5
create backend to api/setting/environment
render onboard on dashboard based on the number of keys setup in the db.

Summary by CodeRabbit

  • New Features

    • Guided onboarding to connect an AI provider and add your API key.
    • Automatic provider detection, validation, and sample code snippets (Node.js, cURL, Python).
    • Dashboard now prompts onboarding if no AI keys exist.
    • Global toast notifications and improved loading states.
  • API

    • Endpoints to list AI providers and manage your AI keys (create, validate, delete).
  • Database

    • Support for multiple AI providers and user/team AI keys.
  • Chores

    • Added/updated UI, form, and theme dependencies; TypeScript upgraded.
    • Seed script now initializes AI providers.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 16, 2025

Walkthrough

Adds AI provider/key management: Prisma models (AiProviders, AiKeys), seed script to populate providers, server services and API routes for providers and environment (CRUD/validate keys), a client onboarding flow in the dashboard to capture/test keys and show code snippets, global toasts, UI tweaks, and dependency/script updates.

Changes

Cohort / File(s) Summary
Tooling & Scripts
package.json
Routes dev:boot through npm run dev; adds Prisma seed command; updates/expands dependencies (Radix UI, react-hook-form, sanitize-html, shiki, sonner, next-themes); bumps TS and types; updates zod.
Database Schema & Seed
prisma/schema.prisma, prisma/seed.ts
Adds models AiProviders and AiKeys; relations to User and Team; composite index on [teamId, provider]. Seed script now creates AI providers (createMany, skipDuplicates).
Server APIs
src/app/(server)/api/settings/aiProviders/route.ts, src/app/(server)/api/settings/environment/route.ts
New GET aiProviders route. New environment route with GET (list keys), POST (create), DELETE (remove), PATCH (validate); auth-gated; schema validation; unified error handling calls.
Server Services
src/app/(server)/services/aiProvider/service.ts, src/app/(server)/services/environment/service.ts
Services for fetching providers and managing AI keys (create, list, validate, delete) via Prisma; exported interfaces and singletons.
Client Onboarding & Dashboard
src/app/(client)/(core)/dashboard/components/Onboarding.tsx, src/app/(client)/(core)/dashboard/page.tsx, src/app/(client)/layout.tsx
New onboarding component (3-step flow) fetching providers, creating/testing environment, showing code and final key; dashboard conditionally shows onboarding; layout adds global Toaster.
Utilities & Validation
src/lib/utils/onboardingUtils.ts, src/lib/validation/aiKeys.schemas.ts
Helpers to build Node/curl/Python example commands; Zod schemas/types for creating AI keys and validating key IDs.
UI Components
src/shadcn-ui/button.tsx, src/shadcn-ui/sonner.tsx
Button gains isActive prop and outline active styles. Adds themed Sonner Toaster wrapper with customized classNames.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User
  participant C as Onboarding (Client)
  participant A as API /settings/*
  participant S as Services
  participant DB as Prisma DB

  rect rgba(200,230,255,0.3)
  note over C: Step 1 - Load Providers
  U->>C: Open Dashboard (no keys)
  C->>A: GET /api/settings/aiProviders
  A->>S: aiProviderService.getAiProviders()
  S->>DB: findMany AiProviders
  DB-->>S: providers
  S-->>A: providers
  A-->>C: 200 { providers }
  end

  rect rgba(220,255,220,0.3)
  note over C: Step 1 - Save AI Key
  U->>C: Submit provider + apiKey
  C->>A: POST /api/settings/environment { provider, apiKey, aiKey }
  A->>S: environmentService.createAiKey(...)
  S->>DB: validate provider (findUnique)
  S->>DB: create AiKeys
  DB-->>S: aiKey
  S-->>A: aiKey
  A-->>C: 201 { aiKey }
  end

  rect rgba(255,245,200,0.3)
  note over C: Step 2 - Validate
  C->>A: PATCH /api/settings/environment { id }
  A->>S: environmentService.validateAiKey(id, user)
  S->>DB: update AiKeys.validated = true
  DB-->>S: validatedKey
  S-->>A: validatedKey
  A-->>C: 200 { validatedKey }
  end

  rect rgba(255,220,220,0.3)
  note over C: Step 3 - Complete
  C-->>U: Show final LangRoute API key
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested labels

🛠 Backend, 🎨 Frontend, ✨ New Feature, 🖌️ UX/UI Change

Poem

A rabbit tapped keys by moonlit light,
Seeded providers, set schemas right.
New routes hop data, swift and keen,
Toasts pop up with a subtle sheen.
Onboarding steps—one, two, three—
Now every key can burrow free. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title mentions the environment setup wizard feature added by this PR but includes an unnecessary leading “5” that references the issue number and adds noise. It partially summarizes the main change by highlighting the setup wizard but could be clearer. Despite the minor formatting issue, the title reasonably reflects the implemented onboarding flow and environment endpoint. Therefore it passes as a partially related summary.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 5-develop-environment-setup-wizard

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.

@DeboraSerra DeboraSerra changed the base branch from master to dev September 16, 2025 22:56
Copy link
Contributor

@SepidehShahbazi SepidehShahbazi left a comment

Choose a reason for hiding this comment

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

Nice work! I just have a few small suggestions:

  • It might be better to split Onboarding.tsx into separate modal components to keep each file focused and easier to maintain.
  • Once the auth-integration PRs are merged, we’ll have a reusable FormInput (wrapping FormField) to reduce redundant form code, plus a LoadingSpinner for loading states, worth refactoring to use those.
  • It may be cleaner to use TanStack Query for form data fetching. Centralizing requests and caching there can reduce per-page code and make data access more consistent across the app.

@coderabbitai coderabbitai bot added ✨ New Feature Proposal or work to add a new feature. 🎨 Frontend Frontend related issue. 🖌️ UX/UI Change User experience or interface design change. 🛠 Backend Backend related issue. labels Sep 25, 2025
Copy link
Contributor

@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: 24

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/shadcn-ui/sonner.tsx (1)

1-30: Move this file out of src/shadcn-ui/

Per guidelines, do not add/modify files under src/shadcn-ui/. Place this Sonner wrapper under src/app/(client)/components/ and import from there.

Example move:

  • New path: src/app/(client)/components/ui/Toaster.tsx
  • Replace imports accordingly in layout/providers.
prisma/schema.prisma (2)

1-4: Invalid generator property: seed is not supported in prisma schema.

This will cause schema parsing errors. Remove the seed line. If you intended to use the new “prisma-client” generator, set provider accordingly; otherwise keep prisma-client-js.

 generator client {
   provider = "prisma-client-js"
-  seed     = "prisma-client"
 }

129-154: Consider uniqueness/ownership constraints for AiKeys.

Depending on requirements, you may want to prevent duplicate keys per owner/provider pair. If you want uniqueness per user+provider or team+provider, add a unique index. If duplicates are allowed, ignore.

Example (user-scoped uniqueness):

@@unique([createdBy, provider], map: "unique_user_provider")
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7660b72 and f106f7b.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json and included by none
  • package.json is excluded by none and included by none
📒 Files selected for processing (13)
  • prisma/schema.prisma (3 hunks)
  • prisma/seed.ts (2 hunks)
  • src/app/(client)/(core)/dashboard/components/Onboarding.tsx (1 hunks)
  • src/app/(client)/(core)/dashboard/page.tsx (1 hunks)
  • src/app/(client)/layout.tsx (2 hunks)
  • src/app/(server)/api/settings/aiProviders/route.ts (1 hunks)
  • src/app/(server)/api/settings/environment/route.ts (1 hunks)
  • src/app/(server)/services/aiProvider/service.ts (1 hunks)
  • src/app/(server)/services/environment/service.ts (1 hunks)
  • src/lib/utils/onboardingUtils.ts (1 hunks)
  • src/lib/validation/aiKeys.schemas.ts (1 hunks)
  • src/shadcn-ui/button.tsx (2 hunks)
  • src/shadcn-ui/sonner.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
src/app/[(]server[)]/api/**/route.ts

⚙️ CodeRabbit configuration file

src/app/[(]server[)]/api/**/route.ts: Keep route handlers thin: validate with Zod (shared schemas), call a Service, then return a typed response. Do NOT import prisma/redis or perform business logic here. Example: parse with a schema from src/lib/validation/** and call a function in src/app/(server)/services/** that returns typed data.

Files:

  • src/app/(server)/api/settings/environment/route.ts
  • src/app/(server)/api/settings/aiProviders/route.ts
src/app/[(]client[)]/**

⚙️ CodeRabbit configuration file

src/app/[(]client[)]/**: Use TanStack Query hooks for server data (no inline fetches in components). Import query keys from src/lib/queryKeys.ts (canonical). Keep components presentational; Tailwind-first styling. Example: import { keys } from 'src/lib/queryKeys' and call a typed hook rather than fetch in the component.

Files:

  • src/app/(client)/layout.tsx
  • src/app/(client)/(core)/dashboard/page.tsx
  • src/app/(client)/(core)/dashboard/components/Onboarding.tsx
src/shadcn-ui/**

⚙️ CodeRabbit configuration file

src/shadcn-ui/**: Generated shadcn primitives. Do not modify these files. Create wrapper or derivative components under src/app/(client)/components/** and compose from the shadcn primitives instead.

Files:

  • src/shadcn-ui/sonner.tsx
  • src/shadcn-ui/button.tsx
src/lib/validation/**

⚙️ CodeRabbit configuration file

src/lib/validation/**: Define Zod schemas and infer types via z.infer. Avoid duplicate shapes; export schemas for reuse in Services and API routes. Co-locate schema files by domain.

Files:

  • src/lib/validation/aiKeys.schemas.ts
src/app/[(]server[)]/services/**

⚙️ CodeRabbit configuration file

src/app/[(]server[)]/services/**: All business logic lives here. No Request/Response/cookies; accept plain inputs and return typed results. Reuse shared types and Zod schemas from src/lib/validation/**. Keep services framework-agnostic and unit-testable.

Files:

  • src/app/(server)/services/aiProvider/service.ts
  • src/app/(server)/services/environment/service.ts
prisma/**

⚙️ CodeRabbit configuration file

prisma/**: If schema changes, include proper Prisma migration(s) and update seed/types. Avoid schema edits without migrations.

Files:

  • prisma/schema.prisma
  • prisma/seed.ts
🧬 Code graph analysis (5)
src/app/(server)/api/settings/environment/route.ts (3)
src/lib/auth.ts (1)
  • session (125-132)
src/app/(server)/services/environment/service.ts (2)
  • environmentService (63-63)
  • validateAiKey (44-50)
src/lib/validation/aiKeys.schemas.ts (2)
  • CreateAiKeySchema (3-16)
  • AiKeyIdParamSchema (18-20)
src/app/(server)/api/settings/aiProviders/route.ts (2)
src/app/(server)/api/settings/environment/route.ts (1)
  • GET (8-29)
src/app/(server)/services/aiProvider/service.ts (1)
  • aiProviderService (26-26)
src/app/(client)/layout.tsx (2)
src/app/(client)/providers/Providers.tsx (1)
  • AppProviders (19-56)
src/shadcn-ui/sonner.tsx (1)
  • Toaster (29-29)
src/app/(client)/(core)/dashboard/page.tsx (1)
src/app/(client)/(core)/dashboard/components/DashboardContent.tsx (1)
  • DashboardContent (10-76)
src/app/(client)/(core)/dashboard/components/Onboarding.tsx (3)
src/lib/validation/aiKeys.schemas.ts (2)
  • CreateAiKeyData (22-22)
  • CreateAiKeySchema (3-16)
src/app/(server)/services/aiProvider/service.ts (1)
  • AiProviderInterface (3-7)
src/lib/utils/onboardingUtils.ts (3)
  • nodeCommand (1-16)
  • curlCommand (18-30)
  • pythonCommand (32-47)
🪛 ast-grep (0.39.5)
src/app/(client)/(core)/dashboard/components/Onboarding.tsx

[warning] 300-300: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)
src/app/(client)/(core)/dashboard/components/Onboarding.tsx

[error] 301-301: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

🔇 Additional comments (6)
prisma/seed.ts (2)

87-92: Confirm admin bootstrap expectations before removing admin seeding.

If admin provisioning is required elsewhere, fine; otherwise, dropping it may block access. Verify docs and deployment flow.


46-79: Centralize provider codes & enforce uniqueness

  • Export provider codes as a shared constant consumed by both prisma/seed.ts and your Zod enum.
  • In prisma/schema.prisma, add @unique on the code field or @@unique([code]) in the AiProviders model.
  • Confirm your prisma db seed command is wired to run prisma/seed.ts (via a script in package.json or the prisma.seed setting).
src/app/(server)/services/aiProvider/service.ts (1)

14-23: LGTM — simple, framework-agnostic service.

Query is scoped and returns only needed fields. Good fit for services per guidelines.

src/lib/utils/onboardingUtils.ts (1)

1-17: Note: User-controlled interpolation in snippets.

Not blocking, but be mindful that apiKey/model are interpolated into code samples. Since these strings are later passed to Shiki and then sanitized before injection, risk is mitigated. Keep it this way and ensure sanitization remains strict on the render side.

src/app/(client)/(core)/dashboard/components/Onboarding.tsx (2)

118-133: API key field is read-only but user-visible.

If this is a server-generated LangRoute API key, ensure it’s fetched from the backend rather than a placeholder. Otherwise label it clearly as a demo key.


300-302: dangerouslySetInnerHTML remains a risk — keep sanitization strict.

Given you already sanitize, ensure no future relaxation of allowlist. Consider rendering via a code component without HTML injection if feasible.

Comment on lines +80 to +85
await prisma.aiProviders.createMany({
data: providers,
skipDuplicates: true,
});
console.log('\n🌱 AI Providers seeded/updated.\n');
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

createMany + skipDuplicates relies on unique constraints; consider idempotent upsert for safety.

If code isn’t uniquely indexed, createMany will insert duplicates. Prefer explicit upserts if uniqueness isn’t guaranteed.

Example (if needed):

await Promise.all(
  providers.map((p) =>
    prisma.aiProviders.upsert({
      where: { code: p.code },
      update: { name: p.name },
      create: p,
    }),
  ),
);
🤖 Prompt for AI Agents
In prisma/seed.ts around lines 80 to 85, the code uses
prisma.aiProviders.createMany with skipDuplicates which only prevents duplicates
if a unique constraint exists; replace this with idempotent upserts to guarantee
no duplicates regardless of schema: iterate over providers and perform
prisma.aiProviders.upsert for each entry (use the provider unique key such as
code in the where clause, provide create with the full provider object and
update with the fields to sync), run them concurrently (e.g., Promise.all) and
remove the createMany call and skipDuplicates.

Comment on lines +65 to +71
const getProviders = async () => {
const res = await fetch('/api/settings/aiProviders');
const data = await res.json();
setProviders(data);
setIsLoading(false);
};

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add error handling for provider fetch.

Network errors keep the spinner forever.

  const getProviders = async () => {
-    const res = await fetch('/api/settings/aiProviders');
-    const data = await res.json();
-    setProviders(data);
-    setIsLoading(false);
+    try {
+      const res = await fetch('/api/settings/aiProviders');
+      if (!res.ok) throw new Error(`HTTP ${res.status}`);
+      const data = await res.json();
+      setProviders(data);
+    } catch (e) {
+      toast.error('Failed to load providers.');
+    } finally {
+      setIsLoading(false);
+    }
   };
📝 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
const getProviders = async () => {
const res = await fetch('/api/settings/aiProviders');
const data = await res.json();
setProviders(data);
setIsLoading(false);
};
const getProviders = async () => {
try {
const res = await fetch('/api/settings/aiProviders');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
setProviders(data);
} catch (e) {
toast.error('Failed to load providers.');
} finally {
setIsLoading(false);
}
};
🤖 Prompt for AI Agents
In src/app/(client)/(core)/dashboard/components/Onboarding.tsx around lines 65
to 71, the provider fetch lacks error handling so network failures leave the
spinner running; wrap the fetch in a try/catch and check res.ok, parse JSON only
on success, call setProviders with the data on success, and ensure
setIsLoading(false) is executed in a finally block; on error (network or non-ok
status) set an error state or empty providers array and log or surface a
user-friendly error message so the UI stops showing the spinner.

Comment on lines +76 to +82
const onSubmit = async (data: CreateAiKeyData) => {
setIsFormLoading(true);
if (Object.keys(errors).length !== 0) {
return;
}
const res = await fetch('/api/settings/environment', {
method: 'POST',
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix loading spinner stuck on validation errors.

Early return leaves isFormLoading true.

  const onSubmit = async (data: CreateAiKeyData) => {
    setIsFormLoading(true);
    if (Object.keys(errors).length !== 0) {
-      return;
+      setIsFormLoading(false);
+      return;
    }
📝 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
const onSubmit = async (data: CreateAiKeyData) => {
setIsFormLoading(true);
if (Object.keys(errors).length !== 0) {
return;
}
const res = await fetch('/api/settings/environment', {
method: 'POST',
const onSubmit = async (data: CreateAiKeyData) => {
setIsFormLoading(true);
if (Object.keys(errors).length !== 0) {
setIsFormLoading(false);
return;
}
const res = await fetch('/api/settings/environment', {
method: 'POST',
🤖 Prompt for AI Agents
In src/app/(client)/(core)/dashboard/components/Onboarding.tsx around lines 76
to 82, the onSubmit handler sets setIsFormLoading(true) but returns early on
validation errors leaving the spinner stuck; move or add setIsFormLoading(false)
before the early return and refactor the async submit to guarantee
setIsFormLoading(false) runs on all code paths (e.g., wrap the fetch/response
logic in try/finally or ensure every return resets loading) so the loading state
is always cleared after validation or network errors.

Comment on lines +230 to +244
const codeToHtmlSanitized = async () => {
const html = await codeToHtml(codes[activeTab].code, {
lang: codes[activeTab].lang,
theme: 'dark-plus',
});
return sanitizeHtml(html, {
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['span']),
allowedAttributes: {
...sanitizeHtml.defaults.allowedAttributes,
span: ['className', 'style'],
code: ['className', 'style'],
pre: ['className', 'style'],
},
});
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Shiki HTML sanitization: fix attributes and tighten allowlist.

  • sanitize-html expects 'class', not 'className' — current config strips classes and breaks highlighting.
  • Limit allowedTags to just ['pre', 'code', 'span'] for safety.
-    return sanitizeHtml(html, {
-      allowedTags: sanitizeHtml.defaults.allowedTags.concat(['span']),
-      allowedAttributes: {
-        ...sanitizeHtml.defaults.allowedAttributes,
-        span: ['className', 'style'],
-        code: ['className', 'style'],
-        pre: ['className', 'style'],
-      },
-    });
+    return sanitizeHtml(html, {
+      allowedTags: ['pre', 'code', 'span'],
+      allowedAttributes: {
+        span: ['class', 'style'],
+        code: ['class', 'style'],
+        pre: ['class', 'style'],
+      },
+    });
📝 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
const codeToHtmlSanitized = async () => {
const html = await codeToHtml(codes[activeTab].code, {
lang: codes[activeTab].lang,
theme: 'dark-plus',
});
return sanitizeHtml(html, {
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['span']),
allowedAttributes: {
...sanitizeHtml.defaults.allowedAttributes,
span: ['className', 'style'],
code: ['className', 'style'],
pre: ['className', 'style'],
},
});
};
const codeToHtmlSanitized = async () => {
const html = await codeToHtml(codes[activeTab].code, {
lang: codes[activeTab].lang,
theme: 'dark-plus',
});
return sanitizeHtml(html, {
allowedTags: ['pre', 'code', 'span'],
allowedAttributes: {
span: ['class', 'style'],
code: ['class', 'style'],
pre: ['class', 'style'],
},
});
};
🤖 Prompt for AI Agents
In src/app/(client)/(core)/dashboard/components/Onboarding.tsx around lines 230
to 244, the sanitizeHtml configuration is incorrect: it uses 'className' (which
sanitize-html ignores) and exposes too many tags; change allowedTags to exactly
['pre','code','span'] and replace 'className' with 'class' for span, code, and
pre in allowedAttributes, keeping 'style' as needed (you can spread
sanitizeHtml.defaults.allowedAttributes but overwrite span/code/pre to
['class','style'] to ensure highlighting classes are preserved and the allowlist
is tightened).

Comment on lines +52 to +60
async deleteAiKey(keyId: string, userId: string) {
const deleted = await prisma.aiKeys.delete({
where: {
id: keyId,
createdBy: userId,
},
});
return deleted;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Same issue for delete; use deleteMany and handle zero-deletes.

delete({ where: { id, createdBy }}) is invalid. Use deleteMany and check count.

-	async deleteAiKey(keyId: string, userId: string) {
-		const deleted = await prisma.aiKeys.delete({
-			where: {
-				id: keyId,
-				createdBy: userId,
-			},
-		});
-		return deleted;
-	}
+	async deleteAiKey(keyId: string, userId: string) {
+		const res = await prisma.aiKeys.deleteMany({
+			where: { id: keyId, createdBy: userId },
+		});
+		if (res.count === 0) {
+			throw new Error('AI key not found or not owned by user');
+		}
+		return { success: true };
+	}
🤖 Prompt for AI Agents
In src/app/(server)/services/environment/service.ts around lines 52 to 60, the
call to prisma.aiKeys.delete with a composite where ({ id: keyId, createdBy:
userId }) is invalid; replace it with prisma.aiKeys.deleteMany({ where: { id:
keyId, createdBy: userId } }) and inspect the returned count; if count === 0,
handle it (e.g., throw a not-found/permission error or return null) otherwise
return confirmation (or the count) so callers know whether a row was actually
deleted.

Comment on lines +4 to +16
provider: z.enum([
'gpt3.5-turbo',
'gpt4',
'gpt4o',
'gpt4o-mini',
'claude-2',
'claude-instant-100k',
'llama2-70b-chat',
'llama3-70b-chat',
]),
apiKey: z.string().trim().min(1).max(100),
aiKey: z.string().trim().min(1),
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Avoid duplicating provider codes across seed, services, and validation.

Extract provider codes into a shared constant (e.g., src/lib/constants/aiProviders.ts) and consume it here via z.enum([...PROVIDERS] as const).

Also consider increasing apiKey max length (100 may be too short for some providers); 256 is safer.

🤖 Prompt for AI Agents
In src/lib/validation/aiKeys.schemas.ts around lines 4 to 16, the provider enum
and apiKey length are hardcoded; extract provider codes into a shared constant
and increase apiKey max. Create src/lib/constants/aiProviders.ts exporting a
readonly array like PROVIDERS = ['gpt3.5-turbo', ...] as const, import that
constant here and replace z.enum([...]) with z.enum([...PROVIDERS] as const),
then change apiKey validation max from 100 to 256 and keep aiKey as-is; update
the import paths accordingly and run type checks to ensure the const assertion
satisfies zod's z.enum.

Comment on lines +18 to +20
export const AiKeyIdParamSchema = z.object({
id: z.uuid({ error: 'Invalid AI key ID format' }),
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Incorrect Zod option key for UUID message.

Use message (not error). Or use z.string().uuid({ message }).

Apply:

-export const AiKeyIdParamSchema = z.object({
-	id: z.uuid({ error: 'Invalid AI key ID format' }),
-});
+export const AiKeyIdParamSchema = z.object({
+  id: z.string().uuid({ message: 'Invalid AI key ID format' }),
+});
📝 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
export const AiKeyIdParamSchema = z.object({
id: z.uuid({ error: 'Invalid AI key ID format' }),
});
export const AiKeyIdParamSchema = z.object({
id: z.string().uuid({ message: 'Invalid AI key ID format' }),
});
🤖 Prompt for AI Agents
In src/lib/validation/aiKeys.schemas.ts around lines 18 to 20, the Zod uuid
option uses the wrong key "error" which is ignored; change it to use "message"
(or switch to z.string().uuid({ message: 'Invalid AI key ID format' })) so the
custom validation message is applied correctly.

Comment on lines 16 to 18
outline:
'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 data-[active=true]:bg-accent data-[active=true]:text-accent-foreground data-[active=true]:border-accent-foreground dark:data-[active=true]:bg-accent/70 dark:data-[active=true]:text-accent-foreground dark:data-[active=true]:border-accent-foreground',
secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Do not modify shadcn primitives; move active-state styling to a wrapper component.

Per repo guidelines, avoid editing files under src/shadcn-ui/. Revert the variant changes here and implement active-state classes in a wrapper under src/app/(client)/components/ that composes Button and conditionally adds the active classes.

Example wrapper (outside this file):

// src/app/(client)/components/ui/Button.tsx
import { Button as BaseButton } from '@/shadcn-ui/button';
import { cn } from '@/lib/utils';

type Props = React.ComponentProps<typeof BaseButton> & { isActive?: boolean };

export function Button({ isActive, className, variant, ...props }: Props) {
  return (
    <BaseButton
      data-active={isActive ? true : undefined}
      className={cn(
        className,
        isActive && variant === 'outline' &&
          'bg-accent text-accent-foreground border-accent-foreground dark:bg-accent/70 dark:text-accent-foreground dark:border-accent-foreground',
      )}
      variant={variant}
      {...props}
    />
  );
}
🤖 Prompt for AI Agents
In src/shadcn-ui/button.tsx around lines 16–18, the variant definitions were
modified to include active-state styling; per repo rules, revert those variant
changes so shadcn primitives remain untouched, and instead implement a wrapper
component at src/app/(client)/components/ui/Button.tsx that composes the
original Button export: accept an isActive prop, forward all other props, set
data-active={isActive ? true : undefined}, and conditionally append the active
classes (only when variant === 'outline') via your cn utility (e.g., add
'bg-accent text-accent-foreground border-accent-foreground dark:bg-accent/70
dark:text-accent-foreground dark:border-accent-foreground') to the className
before passing to BaseButton.

Comment on lines +41 to 47
isActive = false,
...props
}: React.ComponentProps<'button'> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean;
isActive?: boolean;
}) {
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove custom prop/data attribute from the primitive; add via wrapper and consider using aria-pressed for semantics.

  • Remove isActive from the primitive and avoid setting data-active here.
  • In your wrapper, drive styles via data-active or aria-pressed for accessibility.

Proposed diff to revert this file’s public API change:

- isActive = false,
 ...
- isActive?: boolean;
 ...
- data-active={isActive ? true : undefined}

Also applies to: 53-54

🤖 Prompt for AI Agents
In src/shadcn-ui/button.tsx around lines 41-47 (also apply to 53-54): the Button
primitive currently accepts a custom isActive prop and sets data-active on the
underlying element; revert this by removing isActive from the component props
and TypeScript signature, stop setting data-active/data- attributes inside the
primitive, and restore the original public API shape; instead rely on the
wrapper component to add data-active or aria-pressed when needed so styles and
accessibility are driven from the wrapper rather than the primitive.

@mahid797
Copy link
Contributor

@DeboraSerra
Please review and resolve comments from CodeRabbit

Not all might be applicable & some are already resolved so you can ignore them


Also check out existing components like components\common\Button.tsx, components\common\ConditionalButtons.tsx & components\form\FormInput.tsx

Might need to pull latest changes from dev branch for these files
You can create new reusable components in src\app\(client)\components\ if needed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🛠 Backend Backend related issue. 🎨 Frontend Frontend related issue. ✨ New Feature Proposal or work to add a new feature. 🖌️ UX/UI Change User experience or interface design change.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants