feat: enhance recipe list with pagination, columns, size controls and search
Some checks failed
CI/CD Pipeline / Run Tests (push) Failing after 1m5s
CI/CD Pipeline / Code Quality (push) Failing after 6m56s
CI Pipeline / Lint Code (push) Failing after 5m35s
CI Pipeline / Test API Package (push) Failing after 19s
CI Pipeline / Test Web Package (push) Successful in 10m51s
CI Pipeline / Test Shared Package (push) Successful in 10m36s
CI Pipeline / Build All Packages (push) Has been skipped
Docker Build & Deploy / Push Docker Images (push) Has been cancelled
Docker Build & Deploy / Deploy to Staging (push) Has been cancelled
Docker Build & Deploy / Deploy to Production (push) Has been cancelled
E2E Tests / End-to-End Tests (push) Has been cancelled
E2E Tests / E2E Tests (Mobile) (push) Has been cancelled
Security Scanning / NPM Audit (push) Has been cancelled
Security Scanning / Dependency License Check (push) Has been cancelled
Security Scanning / Code Quality Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
Docker Build & Deploy / Build Docker Images (push) Has been cancelled
CI/CD Pipeline / Build and Push Docker Images (push) Has been skipped
CI Pipeline / Generate Coverage Report (push) Failing after 22s
Some checks failed
CI/CD Pipeline / Run Tests (push) Failing after 1m5s
CI/CD Pipeline / Code Quality (push) Failing after 6m56s
CI Pipeline / Lint Code (push) Failing after 5m35s
CI Pipeline / Test API Package (push) Failing after 19s
CI Pipeline / Test Web Package (push) Successful in 10m51s
CI Pipeline / Test Shared Package (push) Successful in 10m36s
CI Pipeline / Build All Packages (push) Has been skipped
Docker Build & Deploy / Push Docker Images (push) Has been cancelled
Docker Build & Deploy / Deploy to Staging (push) Has been cancelled
Docker Build & Deploy / Deploy to Production (push) Has been cancelled
E2E Tests / End-to-End Tests (push) Has been cancelled
E2E Tests / E2E Tests (Mobile) (push) Has been cancelled
Security Scanning / NPM Audit (push) Has been cancelled
Security Scanning / Dependency License Check (push) Has been cancelled
Security Scanning / Code Quality Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
Docker Build & Deploy / Build Docker Images (push) Has been cancelled
CI/CD Pipeline / Build and Push Docker Images (push) Has been skipped
CI Pipeline / Generate Coverage Report (push) Failing after 22s
- Add tag search parameter to backend recipes API - Add pagination controls (12, 24, 48, All items per page) - Add column controls (3, 6, 9 columns) - Add size slider (XS to XXL) for card sizing - Add search by title or tag with 400ms debounce - Add tag autocomplete via datalist - Add URL params for bookmarkable state - Add localStorage persistence for display preferences - Add comprehensive unit tests (34 frontend, 4 backend) - Coverage: 99.4% lines, 97.8% branches Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -135,6 +135,63 @@ describe('Recipes Routes - Integration Tests', () => {
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should support tag query parameter for filtering by tag name', async () => {
|
||||
const prisma = await import('../config/database');
|
||||
vi.mocked(prisma.default.recipe.findMany).mockResolvedValue([]);
|
||||
vi.mocked(prisma.default.recipe.count).mockResolvedValue(0);
|
||||
|
||||
await request(app).get('/recipes?tag=italian').expect(200);
|
||||
|
||||
expect(prisma.default.recipe.findMany).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
where: expect.objectContaining({
|
||||
tags: {
|
||||
some: {
|
||||
tag: {
|
||||
name: { equals: 'italian', mode: 'insensitive' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should support combining search and tag parameters', async () => {
|
||||
const prisma = await import('../config/database');
|
||||
vi.mocked(prisma.default.recipe.findMany).mockResolvedValue([]);
|
||||
vi.mocked(prisma.default.recipe.count).mockResolvedValue(0);
|
||||
|
||||
await request(app).get('/recipes?search=pasta&tag=dinner').expect(200);
|
||||
|
||||
expect(prisma.default.recipe.findMany).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
where: expect.objectContaining({
|
||||
OR: expect.any(Array),
|
||||
tags: expect.any(Object),
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should support category filter parameter', async () => {
|
||||
const prisma = await import('../config/database');
|
||||
vi.mocked(prisma.default.recipe.findMany).mockResolvedValue([]);
|
||||
vi.mocked(prisma.default.recipe.count).mockResolvedValue(0);
|
||||
|
||||
await request(app).get('/recipes?category=dessert').expect(200);
|
||||
|
||||
expect(prisma.default.recipe.findMany).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
where: expect.objectContaining({
|
||||
categories: {
|
||||
has: 'dessert'
|
||||
}
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /recipes/:id', () => {
|
||||
|
||||
@@ -102,7 +102,7 @@ async function autoAddToCookbooks(recipeId: string) {
|
||||
// Get all recipes
|
||||
router.get('/', async (req, res) => {
|
||||
try {
|
||||
const { page = '1', limit = '20', search, cuisine, category } = req.query;
|
||||
const { page = '1', limit = '20', search, cuisine, category, tag } = req.query;
|
||||
const pageNum = parseInt(page as string);
|
||||
const limitNum = parseInt(limit as string);
|
||||
const skip = (pageNum - 1) * limitNum;
|
||||
@@ -120,6 +120,15 @@ router.get('/', async (req, res) => {
|
||||
has: category as string
|
||||
};
|
||||
}
|
||||
if (tag) {
|
||||
where.tags = {
|
||||
some: {
|
||||
tag: {
|
||||
name: { equals: tag as string, mode: 'insensitive' }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const [recipes, total] = await Promise.all([
|
||||
prisma.recipe.findMany({
|
||||
|
||||
Reference in New Issue
Block a user