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

- 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:
2026-01-14 05:40:52 +00:00
parent 104c181a09
commit 3bc211b4f5
6 changed files with 1428 additions and 136 deletions

View File

@@ -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', () => {