Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .cursor/rules/create-dialog-on-framework-editor.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
description:
globs:
alwaysApply: false
---
Rule: Shared Zod Schemas for Client & Server Validation in Next.js
When implementing features that require data validation using Zod schemas on both the client-side (e.g., in a React form within a Client Component) and the server-side (e.g., within a Next.js Server Action):
Centralize Schema Definitions:
Define your Zod schemas in dedicated TypeScript files (e.g., src/lib/schemas/feature-schemas.ts, src/features/my-feature/schemas.ts, or a common schemas.ts if broadly applicable).
These schema definition files should not include "use client" or "use server" directives, as they are meant to be neutral modules.
Server Action Implementation ("use server" files):
Server Action files (marked with "use server") must only export asynchronous functions.
Import the required Zod schema(s) from your centralized schema file(s) into the Server Action file.
Use the imported schema for robust server-side validation of any incoming data before processing or database operations. This is a critical security measure.
Client Component Implementation ("use client" files):
Import the same Zod schema(s) from your centralized schema file(s) into your Client Components that contain forms.
Use the schema with a resolver (like zodResolver for react-hook-form) to enable client-side validation, providing immediate feedback to the user.
When the form is submitted, call the appropriate Server Action, passing the validated (or raw) form data.
Rationale:
DRY (Don't Repeat Yourself): Maintains a single source of truth for validation logic, reducing redundancy and potential inconsistencies.
Next.js Constraints: Adheres to the Next.js App Router constraint that "use server" files can only export async functions. Objects like Zod schemas cannot be directly exported from these files.
Security: Ensures that all data is re-validated on the server, even if client-side validation is in place, as client-side checks can be bypassed.
User Experience: Provides immediate validation feedback on the client, improving the form-filling experience.
Example Workflow Outline:
Schema Definition (feature-schemas.ts):
Define the schema, e.g., export const myFeatureSchema = z.object({ /* ...schema details... / });
Client Component (MyFeatureForm.tsx - 'use client'):
Import the schema and the server action.
Set up the form (e.g., react-hook-form) using the schema with its resolver.
The form's onSubmit handler calls the server action.
Server Action (my-feature-action.ts - "use server"):
Mark the file with "use server".
Import the schema.
The exported async server action function receives form data, validates it using the schema, and then performs server-side operations.
2 changes: 1 addition & 1 deletion .cursor/rules/react-code.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ When writing React code follow these standards:
- Always name files that export components in PascalCase
- Always round corners to rounded-sm
- Always try to keep components small and modular
- Always use sonner instead of toast.
- Always use sonner instead of toast.
6 changes: 6 additions & 0 deletions .cursor/rules/server-actions.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
description:
globs: *.ts
alwaysApply: false
---
- Never use safe server actions, always use regular simple server actions.
43 changes: 43 additions & 0 deletions apps/framework-editor/app/(pages)/controls/ControlsClientPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use client';

import { useState } from 'react';
import PageLayout from "@/app/components/PageLayout";
import { DataTable } from "@/app/components/DataTable";
import { columns } from "./components/columns";
import { CreateControlDialog } from './components/CreateControlDialog';
import type { FrameworkEditorControlTemplate } from '@prisma/client';
import { useRouter } from 'next/navigation';

interface ControlsClientPageProps {
initialControls: FrameworkEditorControlTemplate[];
}

export function ControlsClientPage({ initialControls }: ControlsClientPageProps) {
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false);
const router = useRouter(); // Uncomment if needed

const handleRowClick = (control: FrameworkEditorControlTemplate) => {
router.push(`/controls/${control.id}`);
};

return (
<PageLayout breadcrumbs={[{ label: "Controls", href: "/controls" }]}>
<DataTable
data={initialControls}
columns={columns}
searchQueryParamName="controls-search"
searchPlaceholder="Search controls..."
onCreateClick={() => setIsCreateDialogOpen(true)}
createButtonLabel="Create New Control"
onRowClick={handleRowClick}
/>
<CreateControlDialog
isOpen={isCreateDialogOpen}
onOpenChange={setIsCreateDialogOpen}
onControlCreated={() => {
setIsCreateDialogOpen(false);
}}
/>
</PageLayout>
);
}
Loading
Loading