diff --git a/.cursor/rules/medusa-admin.mdc b/.cursor/rules/medusa-admin.mdc index 048cd8257..92ad938f7 100644 --- a/.cursor/rules/medusa-admin.mdc +++ b/.cursor/rules/medusa-admin.mdc @@ -247,6 +247,21 @@ export const useAdminDeleteResource = () => { }, }); }; + +export const useAdminCreatePostSection = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (data) => { + return sdk.admin.pageBuilder.createPostSection(data); + }, + mutationKey: QUERY_KEYS.POST_SECTIONS, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: QUERY_KEYS.POST_SECTIONS }); + queryClient.invalidateQueries({ queryKey: QUERY_KEYS.POSTS }); + }, + }); +}; ``` ### Query Hooks @@ -394,6 +409,22 @@ export const QUERY_KEYS = { AUTHORS: ['authors'], TAGS: ['tags'], } as const; + +// Always invalidate related queries in mutations +export const useAdminCreatePostSection = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (data) => { + return sdk.admin.pageBuilder.createPostSection(data); + }, + mutationKey: QUERY_KEYS.POST_SECTIONS, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: QUERY_KEYS.POST_SECTIONS }); + queryClient.invalidateQueries({ queryKey: QUERY_KEYS.POSTS }); + }, + }); +}; ``` ### Error Handling @@ -609,28 +640,33 @@ describe('useAdminCreateResource', () => { }); ``` -## Common Anti-Patterns to Avoid +## Verification Checklist -❌ **Don't**: Use inline styles or CSS-in-JS -✅ **Do**: Use Tailwind CSS classes and Medusa UI components +Before submitting admin UI code, ensure: -❌ **Don't**: Fetch data directly in components -✅ **Do**: Use custom hooks with TanStack Query +### State Management +- [ ] All mutations invalidate related query keys using `QUERY_KEYS` +- [ ] Query keys are centralized and use consistent naming +- [ ] Loading and error states are properly handled +- [ ] Optimistic updates are implemented where appropriate -❌ **Don't**: Mutate state directly -✅ **Do**: Use React Hook Form for form state and TanStack Query for server state - -❌ **Don't**: Create deeply nested component hierarchies -✅ **Do**: Use composition and context for state sharing - -❌ **Don't**: Skip error boundaries and error handling -✅ **Do**: Implement proper error handling with toast notifications +### Component Patterns +- [ ] Event handlers use `stopPropagation()` for nested actions +- [ ] Components use proper TypeScript generics for reusability +- [ ] Form validation uses Zod schemas with proper error handling +- [ ] Accessibility attributes (ARIA labels, roles) are included -❌ **Don't**: Use any types or skip TypeScript -✅ **Do**: Define strict interfaces and use proper typing +### Performance +- [ ] Components use `memo()` for expensive renders +- [ ] Event handlers are wrapped in `useCallback()` when needed +- [ ] Heavy computations use `useMemo()` +- [ ] Query data is properly selected to minimize re-renders -❌ **Don't**: Forget accessibility attributes -✅ **Do**: Include proper ARIA labels, roles, and keyboard navigation +### Error Handling +- [ ] All async operations have try-catch blocks +- [ ] User-friendly error messages are displayed via toast +- [ ] Network errors are handled gracefully +- [ ] Form validation errors are displayed inline ## Dependencies @@ -644,3 +680,80 @@ Required packages for Medusa v2 admin development: - `clsx`: Conditional class names - `react-router-dom`: Client-side routing +## Common Anti-Patterns to Avoid + +❌ **Don't**: Fetch data directly in components +```typescript +// Bad - direct API calls in components +const MyComponent = () => { + const [data, setData] = useState(null); + useEffect(() => { + fetch('/api/data').then(res => setData(res)); + }, []); +}; +``` + +✅ **Do**: Use custom hooks with TanStack Query +```typescript +// Good - use query hooks +const MyComponent = () => { + const { data, isLoading } = useAdminListResources(); +}; +``` + +❌ **Don't**: Skip event.stopPropagation() in nested actions +```typescript +// Bad - events bubble up unintentionally +const handleDelete = async () => { + await deleteItem(id); // Parent onClick also fires +}; +``` + +✅ **Do**: Use stopPropagation for nested actions +```typescript +// Good - prevent event bubbling +const handleDelete = async (event: MouseEvent) => { + event.stopPropagation(); + await deleteItem(id); +}; +``` + +❌ **Don't**: Forget to invalidate query cache +```typescript +// Bad - stale data after mutations +const { mutate } = useMutation({ + mutationFn: createResource, + // Missing onSuccess invalidation +}); +``` + +✅ **Do**: Always invalidate related queries +```typescript +// Good - fresh data after mutations +const { mutate } = useMutation({ + mutationFn: createResource, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: QUERY_KEYS.RESOURCES }); + }, +}); +``` + +### Component Files +- Use PascalCase for component files: `PostSectionListItem.tsx` +- Co-locate related files in feature directories +- Separate concerns: components, hooks, types, and tests + +#### Hook Files +- Group by functionality: `post-sections-mutations.ts`, `post-sections-queries.ts` +- Use descriptive names: `useAdminCreatePostSection` +- Export related hooks from index files + +#### Type Files +- Define interfaces close to usage +- Use barrel exports for shared types +- Prefer type-only imports: `import type { User } from './types'` + +#### Test Files +- Mirror source structure: `components/__tests__/Button.test.tsx` +- Use descriptive test names +- Group related tests in describe blocks diff --git a/.cursor/rules/medusa-backend.mdc b/.cursor/rules/medusa-backend.mdc index e31f9f3ea..621be347e 100644 --- a/.cursor/rules/medusa-backend.mdc +++ b/.cursor/rules/medusa-backend.mdc @@ -252,6 +252,39 @@ export const createPostSectionSchema = z.object({ ## Testing Patterns +### Workflow Testing +```typescript +import { createPostWorkflow } from '../create-post'; + +describe('createPostWorkflow', () => { + it('should create post successfully', async () => { + const input = { + post: { + title: 'Test Post', + status: 'draft', + author_id: 'author-1', + }, + }; + + const { result } = await createPostWorkflow(container).run({ input }); + + expect(result).toMatchObject({ + id: expect.any(String), + title: 'Test Post', + status: 'draft', + }); + }); + + it('should handle validation errors', async () => { + const input = { post: { title: '' } }; // Invalid input + + await expect( + createPostWorkflow(container).run({ input }) + ).rejects.toThrow('Title is required'); + }); +}); +``` + ### Unit Tests ```typescript describe('PostSectionService', () => { @@ -278,22 +311,77 @@ describe('PostSectionService', () => { - Test error scenarios and rollbacks - Validate API response formats +## Verification Checklist + +Before submitting backend code, ensure: + +### API Endpoints +- [ ] All endpoints use workflow pattern for business logic +- [ ] Proper middleware validation with Zod schemas +- [ ] Consistent response structure with proper status codes +- [ ] Error handling with appropriate HTTP status codes + +### Workflows +- [ ] Each workflow has a single responsibility +- [ ] Steps are atomic and can be rolled back +- [ ] Proper error handling and compensation logic +- [ ] Input/output types are properly defined + +### Database +- [ ] Migrations are reversible and tested +- [ ] Indexes are added for query performance +- [ ] Foreign key constraints are properly defined +- [ ] Soft deletes are used where appropriate + +### Security +- [ ] Input validation on all endpoints +- [ ] Authentication checks where required +- [ ] Proper error message sanitization +- [ ] Rate limiting considerations + ## Common Anti-Patterns to Avoid ❌ **Don't**: Put business logic directly in API routes -✅ **Do**: Use workflows for all business operations +```typescript +// Bad - business logic in route handler +export const POST = async (req: AuthenticatedMedusaRequest, res: MedusaResponse) => { + const data = req.validatedBody; + const postSection = await req.scope.resolve('postSectionService').create(data); + // Missing validation, error handling, events + res.json({ section: postSection }); +}; +``` -❌ **Don't**: Import services directly in workflows -✅ **Do**: Resolve services from container +✅ **Do**: Use workflows for business logic +```typescript +// Good - workflow handles business logic +export const POST = async (req: AuthenticatedMedusaRequest, res: MedusaResponse) => { + const { result } = await createPostSectionWorkflow(req.scope).run({ + input: { section: req.validatedBody } + }); + res.json({ section: result }); +}; +``` -❌ **Don't**: Skip compensation logic in workflow steps -✅ **Do**: Always implement proper rollback mechanisms +❌ **Don't**: Skip input validation +```typescript +// Bad - no validation +export const POST = async (req: AuthenticatedMedusaRequest, res: MedusaResponse) => { + const data = req.body; // Unvalidated input + // Process data... +}; +``` -❌ **Don't**: Use any types or skip validation -✅ **Do**: Define strict TypeScript interfaces and validate inputs +✅ **Do**: Use Zod middleware validation +```typescript +// Good - proper validation +const createSchema = z.object({ + name: z.string().min(1), + status: z.enum(['draft', 'published']).default('draft'), +}); -❌ **Don't**: Create endpoints without authentication -✅ **Do**: Always use AuthenticatedMedusaRequest for admin routes +export const validateCreate = validateAndTransformBody(createSchema); +``` ## Dependencies @@ -303,4 +391,3 @@ Required packages for Medusa v2 backend development: - `@mikro-orm/core`: Database ORM - `zod`: Schema validation - `@types/node`: Node.js type definitions -