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
147 changes: 130 additions & 17 deletions .cursor/rules/medusa-admin.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,21 @@ export const useAdminDeleteResource = () => {
},
});
};

export const useAdminCreatePostSection = () => {
const queryClient = useQueryClient();

return useMutation<CreatePostSectionResponse, Error, CreatePostSectionInput>({
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
Expand Down Expand Up @@ -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<CreatePostSectionResponse, Error, CreatePostSectionInput>({
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
Expand Down Expand Up @@ -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

Expand All @@ -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
107 changes: 97 additions & 10 deletions .cursor/rules/medusa-backend.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand All @@ -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

Expand All @@ -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

Loading