-
Notifications
You must be signed in to change notification settings - Fork 6
5 develop environment setup wizard #53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Conversation
WalkthroughAdds 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested labels
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate 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. Comment |
SepidehShahbazi
left a comment
There was a problem hiding this 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.tsxinto 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(wrappingFormField) to reduce redundant form code, plus aLoadingSpinnerfor loading states, worth refactoring to use those. - It may be cleaner to use
TanStack Queryfor form data fetching. Centralizing requests and caching there can reduce per-page code and make data access more consistent across the app.
There was a problem hiding this 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
⛔ Files ignored due to path filters (2)
package-lock.jsonis excluded by!**/package-lock.jsonand included by nonepackage.jsonis 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.tssrc/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.tsxsrc/app/(client)/(core)/dashboard/page.tsxsrc/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.tsxsrc/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.tssrc/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.prismaprisma/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.tsand your Zod enum.- In
prisma/schema.prisma, add@uniqueon thecodefield or@@unique([code])in theAiProvidersmodel.- Confirm your
prisma db seedcommand is wired to runprisma/seed.ts(via a script inpackage.jsonor theprisma.seedsetting).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.
| await prisma.aiProviders.createMany({ | ||
| data: providers, | ||
| skipDuplicates: true, | ||
| }); | ||
| console.log('\n🌱 AI Providers seeded/updated.\n'); | ||
| } |
There was a problem hiding this comment.
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.
| const getProviders = async () => { | ||
| const res = await fetch('/api/settings/aiProviders'); | ||
| const data = await res.json(); | ||
| setProviders(data); | ||
| setIsLoading(false); | ||
| }; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| const onSubmit = async (data: CreateAiKeyData) => { | ||
| setIsFormLoading(true); | ||
| if (Object.keys(errors).length !== 0) { | ||
| return; | ||
| } | ||
| const res = await fetch('/api/settings/environment', { | ||
| method: 'POST', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| 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'], | ||
| }, | ||
| }); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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).
| async deleteAiKey(keyId: string, userId: string) { | ||
| const deleted = await prisma.aiKeys.delete({ | ||
| where: { | ||
| id: keyId, | ||
| createdBy: userId, | ||
| }, | ||
| }); | ||
| return deleted; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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), | ||
| }); |
There was a problem hiding this comment.
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.
| export const AiKeyIdParamSchema = z.object({ | ||
| id: z.uuid({ error: 'Invalid AI key ID format' }), | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| 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', |
There was a problem hiding this comment.
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.
| isActive = false, | ||
| ...props | ||
| }: React.ComponentProps<'button'> & | ||
| VariantProps<typeof buttonVariants> & { | ||
| asChild?: boolean; | ||
| isActive?: boolean; | ||
| }) { |
There was a problem hiding this comment.
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.
|
@DeboraSerra
Also check out existing components like
|
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
API
Database
Chores