import { Router } from 'express'; import { pool } from '../db/pool'; import { requireAuth } from '../auth/middleware'; const router = Router(); router.use(requireAuth); function camelToSnake(str: string): string { return str.replace(/[A-Z]/g, l => `_${l.toLowerCase()}`); } // GET /api/chemicals router.get('/', async (req, res) => { try { const result = await pool.query( 'SELECT * FROM chemicals WHERE user_id = $1 ORDER BY created_at DESC', [req.user!.id] ); // Map snake_case columns back to camelCase for the frontend const rows = result.rows.map(snakeToCamel); res.json(rows); } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } }); // POST /api/chemicals router.post('/', async (req, res) => { try { const b = req.body; const result = await pool.query(` INSERT INTO chemicals ( user_id, pi_first_name, physical_state, chemical_name, bldg_code, lab, storage_location, storage_device, number_of_containers, amount_per_container, unit_of_measure, cas_number, chemical_formula, molecular_weight, vendor, catalog_number, lot_number, expiration_date, concentration, percentage_full, needs_manual_entry, scanned_image, comments, barcode, contact ) VALUES ( $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12, $13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25 ) RETURNING *`, [ req.user!.id, b.piFirstName, b.physicalState, b.chemicalName, b.bldgCode, b.lab, b.storageLocation, b.storageDevice, b.numberOfContainers, b.amountPerContainer, b.unitOfMeasure, b.casNumber, b.chemicalFormula ?? null, b.molecularWeight ?? null, b.vendor ?? null, b.catalogNumber ?? null, b.lotNumber ?? null, b.expirationDate ?? null, b.concentration ?? null, b.percentageFull ?? null, b.needsManualEntry ?? null, b.scannedImage ?? null, b.comments ?? null, b.barcode ?? null, b.contact ?? null, ] ); res.status(201).json(snakeToCamel(result.rows[0])); } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } }); // PATCH /api/chemicals/:id router.patch('/:id', async (req, res) => { try { const fields = Object.keys(req.body); if (fields.length === 0) return res.status(400).json({ error: 'No fields to update' }); const snakeFields = fields.map(camelToSnake); const setClauses = snakeFields.map((f, i) => `${f} = $${i + 3}`).join(', '); const values = fields.map(f => req.body[f]); const result = await pool.query( `UPDATE chemicals SET ${setClauses}, updated_at = NOW() WHERE id = $1 AND user_id = $2 RETURNING *`, [req.params.id, req.user!.id, ...values] ); if (result.rows.length === 0) return res.status(404).json({ error: 'Not found' }); res.json(snakeToCamel(result.rows[0])); } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } }); // DELETE /api/chemicals/:id router.delete('/:id', async (req, res) => { try { await pool.query('DELETE FROM chemicals WHERE id = $1 AND user_id = $2', [req.params.id, req.user!.id]); res.status(204).end(); } catch (err) { console.error(err); res.status(500).json({ error: 'Internal server error' }); } }); function snakeToCamel(row: Record): Record { return Object.fromEntries( Object.entries(row).map(([k, v]) => [ k.replace(/_([a-z])/g, (_, l) => l.toUpperCase()), v, ]) ); } export default router;