Skip to content

Commit 557f4de

Browse files
committed
Merge branch 'post-section-apis' of github.com:lambda-curry/medusa2-starter into post-section-apis
2 parents d059312 + 2795525 commit 557f4de

2 files changed

Lines changed: 227 additions & 27 deletions

File tree

.cursor/rules/medusa-admin.mdc

Lines changed: 130 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,21 @@ export const useAdminDeleteResource = () => {
247247
},
248248
});
249249
};
250+
251+
export const useAdminCreatePostSection = () => {
252+
const queryClient = useQueryClient();
253+
254+
return useMutation<CreatePostSectionResponse, Error, CreatePostSectionInput>({
255+
mutationFn: async (data) => {
256+
return sdk.admin.pageBuilder.createPostSection(data);
257+
},
258+
mutationKey: QUERY_KEYS.POST_SECTIONS,
259+
onSuccess: () => {
260+
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.POST_SECTIONS });
261+
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.POSTS });
262+
},
263+
});
264+
};
250265
```
251266

252267
### Query Hooks
@@ -394,6 +409,22 @@ export const QUERY_KEYS = {
394409
AUTHORS: ['authors'],
395410
TAGS: ['tags'],
396411
} as const;
412+
413+
// Always invalidate related queries in mutations
414+
export const useAdminCreatePostSection = () => {
415+
const queryClient = useQueryClient();
416+
417+
return useMutation<CreatePostSectionResponse, Error, CreatePostSectionInput>({
418+
mutationFn: async (data) => {
419+
return sdk.admin.pageBuilder.createPostSection(data);
420+
},
421+
mutationKey: QUERY_KEYS.POST_SECTIONS,
422+
onSuccess: () => {
423+
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.POST_SECTIONS });
424+
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.POSTS });
425+
},
426+
});
427+
};
397428
```
398429

399430
### Error Handling
@@ -609,28 +640,33 @@ describe('useAdminCreateResource', () => {
609640
});
610641
```
611642

612-
## Common Anti-Patterns to Avoid
643+
## Verification Checklist
613644

614-
❌ **Don't**: Use inline styles or CSS-in-JS
615-
✅ **Do**: Use Tailwind CSS classes and Medusa UI components
645+
Before submitting admin UI code, ensure:
616646

617-
❌ **Don't**: Fetch data directly in components
618-
✅ **Do**: Use custom hooks with TanStack Query
647+
### State Management
648+
- [ ] All mutations invalidate related query keys using `QUERY_KEYS`
649+
- [ ] Query keys are centralized and use consistent naming
650+
- [ ] Loading and error states are properly handled
651+
- [ ] Optimistic updates are implemented where appropriate
619652

620-
❌ **Don't**: Mutate state directly
621-
✅ **Do**: Use React Hook Form for form state and TanStack Query for server state
622-
623-
❌ **Don't**: Create deeply nested component hierarchies
624-
✅ **Do**: Use composition and context for state sharing
625-
626-
❌ **Don't**: Skip error boundaries and error handling
627-
✅ **Do**: Implement proper error handling with toast notifications
653+
### Component Patterns
654+
- [ ] Event handlers use `stopPropagation()` for nested actions
655+
- [ ] Components use proper TypeScript generics for reusability
656+
- [ ] Form validation uses Zod schemas with proper error handling
657+
- [ ] Accessibility attributes (ARIA labels, roles) are included
628658

629-
❌ **Don't**: Use any types or skip TypeScript
630-
✅ **Do**: Define strict interfaces and use proper typing
659+
### Performance
660+
- [ ] Components use `memo()` for expensive renders
661+
- [ ] Event handlers are wrapped in `useCallback()` when needed
662+
- [ ] Heavy computations use `useMemo()`
663+
- [ ] Query data is properly selected to minimize re-renders
631664

632-
❌ **Don't**: Forget accessibility attributes
633-
✅ **Do**: Include proper ARIA labels, roles, and keyboard navigation
665+
### Error Handling
666+
- [ ] All async operations have try-catch blocks
667+
- [ ] User-friendly error messages are displayed via toast
668+
- [ ] Network errors are handled gracefully
669+
- [ ] Form validation errors are displayed inline
634670

635671
## Dependencies
636672

@@ -644,3 +680,80 @@ Required packages for Medusa v2 admin development:
644680
- `clsx`: Conditional class names
645681
- `react-router-dom`: Client-side routing
646682

683+
## Common Anti-Patterns to Avoid
684+
685+
❌ **Don't**: Fetch data directly in components
686+
```typescript
687+
// Bad - direct API calls in components
688+
const MyComponent = () => {
689+
const [data, setData] = useState(null);
690+
useEffect(() => {
691+
fetch('/api/data').then(res => setData(res));
692+
}, []);
693+
};
694+
```
695+
696+
✅ **Do**: Use custom hooks with TanStack Query
697+
```typescript
698+
// Good - use query hooks
699+
const MyComponent = () => {
700+
const { data, isLoading } = useAdminListResources();
701+
};
702+
```
703+
704+
❌ **Don't**: Skip event.stopPropagation() in nested actions
705+
```typescript
706+
// Bad - events bubble up unintentionally
707+
const handleDelete = async () => {
708+
await deleteItem(id); // Parent onClick also fires
709+
};
710+
```
711+
712+
✅ **Do**: Use stopPropagation for nested actions
713+
```typescript
714+
// Good - prevent event bubbling
715+
const handleDelete = async (event: MouseEvent) => {
716+
event.stopPropagation();
717+
await deleteItem(id);
718+
};
719+
```
720+
721+
❌ **Don't**: Forget to invalidate query cache
722+
```typescript
723+
// Bad - stale data after mutations
724+
const { mutate } = useMutation({
725+
mutationFn: createResource,
726+
// Missing onSuccess invalidation
727+
});
728+
```
729+
730+
✅ **Do**: Always invalidate related queries
731+
```typescript
732+
// Good - fresh data after mutations
733+
const { mutate } = useMutation({
734+
mutationFn: createResource,
735+
onSuccess: () => {
736+
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.RESOURCES });
737+
},
738+
});
739+
```
740+
741+
### Component Files
742+
- Use PascalCase for component files: `PostSectionListItem.tsx`
743+
- Co-locate related files in feature directories
744+
- Separate concerns: components, hooks, types, and tests
745+
746+
#### Hook Files
747+
- Group by functionality: `post-sections-mutations.ts`, `post-sections-queries.ts`
748+
- Use descriptive names: `useAdminCreatePostSection`
749+
- Export related hooks from index files
750+
751+
#### Type Files
752+
- Define interfaces close to usage
753+
- Use barrel exports for shared types
754+
- Prefer type-only imports: `import type { User } from './types'`
755+
756+
#### Test Files
757+
- Mirror source structure: `components/__tests__/Button.test.tsx`
758+
- Use descriptive test names
759+
- Group related tests in describe blocks

.cursor/rules/medusa-backend.mdc

Lines changed: 97 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,39 @@ export const createPostSectionSchema = z.object({
252252

253253
## Testing Patterns
254254

255+
### Workflow Testing
256+
```typescript
257+
import { createPostWorkflow } from '../create-post';
258+
259+
describe('createPostWorkflow', () => {
260+
it('should create post successfully', async () => {
261+
const input = {
262+
post: {
263+
title: 'Test Post',
264+
status: 'draft',
265+
author_id: 'author-1',
266+
},
267+
};
268+
269+
const { result } = await createPostWorkflow(container).run({ input });
270+
271+
expect(result).toMatchObject({
272+
id: expect.any(String),
273+
title: 'Test Post',
274+
status: 'draft',
275+
});
276+
});
277+
278+
it('should handle validation errors', async () => {
279+
const input = { post: { title: '' } }; // Invalid input
280+
281+
await expect(
282+
createPostWorkflow(container).run({ input })
283+
).rejects.toThrow('Title is required');
284+
});
285+
});
286+
```
287+
255288
### Unit Tests
256289
```typescript
257290
describe('PostSectionService', () => {
@@ -278,22 +311,77 @@ describe('PostSectionService', () => {
278311
- Test error scenarios and rollbacks
279312
- Validate API response formats
280313

314+
## Verification Checklist
315+
316+
Before submitting backend code, ensure:
317+
318+
### API Endpoints
319+
- [ ] All endpoints use workflow pattern for business logic
320+
- [ ] Proper middleware validation with Zod schemas
321+
- [ ] Consistent response structure with proper status codes
322+
- [ ] Error handling with appropriate HTTP status codes
323+
324+
### Workflows
325+
- [ ] Each workflow has a single responsibility
326+
- [ ] Steps are atomic and can be rolled back
327+
- [ ] Proper error handling and compensation logic
328+
- [ ] Input/output types are properly defined
329+
330+
### Database
331+
- [ ] Migrations are reversible and tested
332+
- [ ] Indexes are added for query performance
333+
- [ ] Foreign key constraints are properly defined
334+
- [ ] Soft deletes are used where appropriate
335+
336+
### Security
337+
- [ ] Input validation on all endpoints
338+
- [ ] Authentication checks where required
339+
- [ ] Proper error message sanitization
340+
- [ ] Rate limiting considerations
341+
281342
## Common Anti-Patterns to Avoid
282343

283344
❌ **Don't**: Put business logic directly in API routes
284-
✅ **Do**: Use workflows for all business operations
345+
```typescript
346+
// Bad - business logic in route handler
347+
export const POST = async (req: AuthenticatedMedusaRequest, res: MedusaResponse) => {
348+
const data = req.validatedBody;
349+
const postSection = await req.scope.resolve('postSectionService').create(data);
350+
// Missing validation, error handling, events
351+
res.json({ section: postSection });
352+
};
353+
```
285354

286-
❌ **Don't**: Import services directly in workflows
287-
✅ **Do**: Resolve services from container
355+
✅ **Do**: Use workflows for business logic
356+
```typescript
357+
// Good - workflow handles business logic
358+
export const POST = async (req: AuthenticatedMedusaRequest, res: MedusaResponse) => {
359+
const { result } = await createPostSectionWorkflow(req.scope).run({
360+
input: { section: req.validatedBody }
361+
});
362+
res.json({ section: result });
363+
};
364+
```
288365

289-
❌ **Don't**: Skip compensation logic in workflow steps
290-
✅ **Do**: Always implement proper rollback mechanisms
366+
❌ **Don't**: Skip input validation
367+
```typescript
368+
// Bad - no validation
369+
export const POST = async (req: AuthenticatedMedusaRequest, res: MedusaResponse) => {
370+
const data = req.body; // Unvalidated input
371+
// Process data...
372+
};
373+
```
291374

292-
❌ **Don't**: Use any types or skip validation
293-
✅ **Do**: Define strict TypeScript interfaces and validate inputs
375+
✅ **Do**: Use Zod middleware validation
376+
```typescript
377+
// Good - proper validation
378+
const createSchema = z.object({
379+
name: z.string().min(1),
380+
status: z.enum(['draft', 'published']).default('draft'),
381+
});
294382

295-
❌ **Don't**: Create endpoints without authentication
296-
✅ **Do**: Always use AuthenticatedMedusaRequest for admin routes
383+
export const validateCreate = validateAndTransformBody(createSchema);
384+
```
297385

298386
## Dependencies
299387

@@ -303,4 +391,3 @@ Required packages for Medusa v2 backend development:
303391
- `@mikro-orm/core`: Database ORM
304392
- `zod`: Schema validation
305393
- `@types/node`: Node.js type definitions
306-

0 commit comments

Comments
 (0)