Add dictionary feature with term management and UI components
- Implement backend API for term CRUD operations - Add frontend dictionary page with search and filtering - Integrate shadcn/ui components (Dialog) - Create term management UI with add/edit/delete functionality - Update database seed with initial terms - Add API client for term operations - Complete Phase 2 of development plan
This commit is contained in:
134
packages/backend/src/routes/terms.ts
Normal file
134
packages/backend/src/routes/terms.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { prisma } from '../lib/prisma.js';
|
||||
import { WordType, CefrLevel } from '@prisma/client';
|
||||
|
||||
const router = Router();
|
||||
|
||||
/**
|
||||
* GET /api/terms
|
||||
* Get all terms with optional filtering, search, and pagination
|
||||
* Query params:
|
||||
* - query: search text (searches wordText and normalizedText)
|
||||
* - wordType: filter by word type (NOUN, VERB, etc.)
|
||||
* - cefrLevel: filter by CEFR level (A1, A2, B1, B2, C1, C2)
|
||||
* - page: page number (default: 1)
|
||||
* - limit: items per page (default: 20)
|
||||
*/
|
||||
router.get('/', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const {
|
||||
query = '',
|
||||
wordType,
|
||||
cefrLevel,
|
||||
page = '1',
|
||||
limit = '20',
|
||||
} = req.query;
|
||||
|
||||
const pageNum = parseInt(page as string, 10);
|
||||
const limitNum = parseInt(limit as string, 10);
|
||||
const skip = (pageNum - 1) * limitNum;
|
||||
|
||||
// Build where clause
|
||||
const where: any = {};
|
||||
|
||||
// Add search filter
|
||||
if (query && typeof query === 'string' && query.trim()) {
|
||||
where.OR = [
|
||||
{ wordText: { contains: query.trim() } },
|
||||
{ normalizedText: { contains: query.trim() } },
|
||||
];
|
||||
}
|
||||
|
||||
// Add wordType filter
|
||||
if (wordType && typeof wordType === 'string') {
|
||||
const validWordTypes = Object.values(WordType);
|
||||
if (validWordTypes.includes(wordType as WordType)) {
|
||||
where.wordType = wordType as WordType;
|
||||
}
|
||||
}
|
||||
|
||||
// Add cefrLevel filter
|
||||
if (cefrLevel && typeof cefrLevel === 'string') {
|
||||
const validCefrLevels = Object.values(CefrLevel);
|
||||
if (validCefrLevels.includes(cefrLevel as CefrLevel)) {
|
||||
where.cefrLevel = cefrLevel as CefrLevel;
|
||||
}
|
||||
}
|
||||
|
||||
// Get total count for pagination
|
||||
const total = await prisma.term.count({ where });
|
||||
|
||||
// Get terms with media
|
||||
const terms = await prisma.term.findMany({
|
||||
where,
|
||||
include: {
|
||||
media: {
|
||||
orderBy: {
|
||||
createdAt: 'asc',
|
||||
},
|
||||
},
|
||||
examples: {
|
||||
orderBy: {
|
||||
createdAt: 'asc',
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
wordText: 'asc',
|
||||
},
|
||||
skip,
|
||||
take: limitNum,
|
||||
});
|
||||
|
||||
res.json({
|
||||
terms,
|
||||
pagination: {
|
||||
page: pageNum,
|
||||
limit: limitNum,
|
||||
total,
|
||||
totalPages: Math.ceil(total / limitNum),
|
||||
},
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching terms:', error);
|
||||
res.status(500).json({ error: 'Failed to fetch terms', message: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /api/terms/:id
|
||||
* Get a single term by ID with all related data
|
||||
*/
|
||||
router.get('/:id', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const term = await prisma.term.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
media: {
|
||||
orderBy: {
|
||||
createdAt: 'asc',
|
||||
},
|
||||
},
|
||||
examples: {
|
||||
orderBy: {
|
||||
createdAt: 'asc',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!term) {
|
||||
return res.status(404).json({ error: 'Term not found' });
|
||||
}
|
||||
|
||||
res.json({ term });
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching term:', error);
|
||||
res.status(500).json({ error: 'Failed to fetch term', message: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
Reference in New Issue
Block a user