feat: Implement initial structure for Directus Mayo API extension
- Added main router in src/index.js to register endpoints. - Implemented GET /mayo-api/products to fetch product list with pagination and filters. - Implemented GET /mayo-api/dictionaries to fetch various dictionaries for frontend use. - Created separate files for routes, repositories, serializers, and utilities to maintain clean architecture. - Added utility functions for async handling, pagination, and order search parsing. - Introduced serializers for products and dictionaries to format data for frontend consumption. - Established repository functions for database queries related to products and dictionaries. - Updated package.json to include license information. - Created documentation for the API extension detailing current state and future implementation plans.
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
export default (router) => {
|
||||
router.get('/', (req, res) => res.send('Hello, World!'));
|
||||
};
|
||||
|
||||
|
||||
export default {
|
||||
id: 'mayo-api',
|
||||
|
||||
handler: (router, { database }) => {
|
||||
router.get('/products/by-order/:orderNumber/:orderYear/:productIndex/timeline', async (req, res) => {
|
||||
if (!req.accountability?.user) {
|
||||
return res.status(401).json({ error: 'Unauthorized' })
|
||||
}
|
||||
|
||||
const { orderNumber, orderYear, productIndex } = req.params
|
||||
|
||||
const normalizedOrderNumber = Number(orderNumber)
|
||||
const normalizedOrderYear = Number(orderYear)
|
||||
const normalizedProductIndex = Number(productIndex)
|
||||
|
||||
if (
|
||||
Number.isNaN(normalizedOrderNumber) ||
|
||||
Number.isNaN(normalizedOrderYear) ||
|
||||
Number.isNaN(normalizedProductIndex)
|
||||
) {
|
||||
return res.status(400).json({ error: 'Invalid order code' })
|
||||
}
|
||||
|
||||
const orderProduct = await database('mayo_order_products as op')
|
||||
.select(
|
||||
'op.id as order_product_id',
|
||||
'op.product_id',
|
||||
'op.produc_order_idx',
|
||||
'o.id as order_id',
|
||||
'o.order_number',
|
||||
'o.order_year',
|
||||
'p.note',
|
||||
'm.id as model_id',
|
||||
'm.name as model_name'
|
||||
)
|
||||
.join('mayo_orders as o', 'op.order_id', 'o.id')
|
||||
.join('mayo_products as p', 'op.product_id', 'p.id')
|
||||
.leftJoin('mayo_models as m', 'p.model_id', 'm.id')
|
||||
.where('o.order_number', normalizedOrderNumber)
|
||||
.where('o.order_year', normalizedOrderYear)
|
||||
.where('op.produc_order_idx', normalizedProductIndex)
|
||||
.first()
|
||||
|
||||
if (!orderProduct) {
|
||||
return res.status(404).json({ error: 'Product not found' })
|
||||
}
|
||||
|
||||
const events = await database('mayo_events as e')
|
||||
.select(
|
||||
'e.id',
|
||||
'e.ordinal',
|
||||
'e.event_kind',
|
||||
'e.event_date',
|
||||
'p.id as part_id',
|
||||
'p.part_type'
|
||||
)
|
||||
.join('mayo_parts as p', 'e.part_id', 'p.id')
|
||||
.where('p.product_id', orderProduct.product_id)
|
||||
.orderBy('e.ordinal', 'asc')
|
||||
|
||||
const eventIds = events.map((event) => event.id)
|
||||
|
||||
const [operations, notes, photos] = await Promise.all([
|
||||
database('mayo_event_operations as eo')
|
||||
.select(
|
||||
'eo.event_id',
|
||||
'o.id as operation_id',
|
||||
'o.name',
|
||||
'o.description'
|
||||
)
|
||||
.leftJoin('mayo_operations as o', 'eo.operation_id', 'o.id')
|
||||
.whereIn('eo.event_id', eventIds),
|
||||
|
||||
database('mayo_event_notes')
|
||||
.select('event_id', 'note_type', 'note')
|
||||
.whereIn('event_id', eventIds),
|
||||
|
||||
database('mayo_event_photos')
|
||||
.select('event_id', 'photo_url')
|
||||
.whereIn('event_id', eventIds),
|
||||
])
|
||||
|
||||
const operationsByEventId = new Map(operations.map((operation) => [operation.event_id, operation]))
|
||||
const notesByEventId = new Map(notes.map((note) => [note.event_id, note]))
|
||||
|
||||
const photosByEventId = photos.reduce((acc, photo) => {
|
||||
if (!acc.has(photo.event_id)) acc.set(photo.event_id, [])
|
||||
acc.get(photo.event_id).push({
|
||||
file_id: photo.photo_url,
|
||||
url: photo.photo_url ? `/assets/${photo.photo_url}` : null,
|
||||
})
|
||||
return acc
|
||||
}, new Map())
|
||||
|
||||
const timeline = events.map((event) => ({
|
||||
id: event.id,
|
||||
ordinal: event.ordinal,
|
||||
kind: event.event_kind,
|
||||
date: event.event_date,
|
||||
part: {
|
||||
id: event.part_id,
|
||||
type: event.part_type,
|
||||
},
|
||||
operation: operationsByEventId.get(event.id) ?? null,
|
||||
note: notesByEventId.get(event.id) ?? null,
|
||||
photos: photosByEventId.get(event.id) ?? [],
|
||||
}))
|
||||
|
||||
return res.json({
|
||||
product: {
|
||||
id: orderProduct.product_id,
|
||||
order_code: `${String(orderProduct.order_number).padStart(4, '0')}/${orderProduct.order_year}/${orderProduct.produc_order_idx}`,
|
||||
model: {
|
||||
id: orderProduct.model_id,
|
||||
name: orderProduct.model_name,
|
||||
},
|
||||
note: orderProduct.note,
|
||||
},
|
||||
timeline,
|
||||
})
|
||||
})
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user