Initial commit — Shop Manager for Tony Moon
Full auto mechanic shop management tool: jobs, invoices, estimates, inspections, inventory, recommendations, technicians, appointments, reports, file uploads, kanban board, and more. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
179
server/src/routes/appointments.ts
Normal file
179
server/src/routes/appointments.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
import { Router, Response } from 'express';
|
||||
import db from '../database';
|
||||
import { AuthRequest } from '../middleware/auth';
|
||||
|
||||
const router = Router();
|
||||
|
||||
// List appointments
|
||||
router.get('/', (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const { status, date, ownerId } = req.query;
|
||||
let query = `
|
||||
SELECT a.*, o.first_name as owner_first_name, o.last_name as owner_last_name,
|
||||
o.phone as owner_phone, v.year, v.make, v.model
|
||||
FROM appointments a
|
||||
LEFT JOIN owners o ON a.owner_id = o.id
|
||||
LEFT JOIN vehicles v ON a.vehicle_id = v.id
|
||||
`;
|
||||
const conditions: string[] = [];
|
||||
const params: any[] = [];
|
||||
|
||||
if (status) { conditions.push('a.status = ?'); params.push(status); }
|
||||
if (date) { conditions.push('a.requested_date = ?'); params.push(date); }
|
||||
if (ownerId) { conditions.push('a.owner_id = ?'); params.push(Number(ownerId)); }
|
||||
if (conditions.length > 0) query += ' WHERE ' + conditions.join(' AND ');
|
||||
query += ' ORDER BY a.requested_date ASC, a.requested_time ASC';
|
||||
|
||||
const appointments = db.prepare(query).all(...params);
|
||||
res.json(appointments);
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Get appointment
|
||||
router.get('/:id', (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const appointment = db.prepare(`
|
||||
SELECT a.*, o.first_name as owner_first_name, o.last_name as owner_last_name,
|
||||
o.phone as owner_phone, o.email as owner_email, v.year, v.make, v.model
|
||||
FROM appointments a
|
||||
LEFT JOIN owners o ON a.owner_id = o.id
|
||||
LEFT JOIN vehicles v ON a.vehicle_id = v.id
|
||||
WHERE a.id = ?
|
||||
`).get(req.params.id);
|
||||
if (!appointment) return res.status(404).json({ error: 'Appointment not found' });
|
||||
res.json(appointment);
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Create appointment
|
||||
router.post('/', (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const { ownerId, shopId, vehicleId, serviceDescription, requestedDate, requestedTime, durationMinutes, notes } = req.body;
|
||||
if (!requestedDate) return res.status(400).json({ error: 'Requested date required' });
|
||||
|
||||
const result = db.prepare(`
|
||||
INSERT INTO appointments (owner_id, shop_id, vehicle_id, service_description, requested_date, requested_time, duration_minutes, notes)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(ownerId || null, shopId || null, vehicleId || null, serviceDescription || null,
|
||||
requestedDate, requestedTime || null, durationMinutes || 60, notes || null);
|
||||
|
||||
const appointment = db.prepare('SELECT * FROM appointments WHERE id = ?').get(result.lastInsertRowid);
|
||||
res.status(201).json(appointment);
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Update appointment
|
||||
router.put('/:id', (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const appt = db.prepare('SELECT * FROM appointments WHERE id = ?').get(req.params.id) as any;
|
||||
if (!appt) return res.status(404).json({ error: 'Appointment not found' });
|
||||
|
||||
const { ownerId, vehicleId, serviceDescription, requestedDate, requestedTime, durationMinutes, status, notes } = req.body;
|
||||
db.prepare(`
|
||||
UPDATE appointments SET owner_id = ?, vehicle_id = ?, service_description = ?,
|
||||
requested_date = ?, requested_time = ?, duration_minutes = ?, status = ?, notes = ?,
|
||||
updated_at = datetime('now') WHERE id = ?
|
||||
`).run(
|
||||
ownerId !== undefined ? (ownerId || null) : appt.owner_id,
|
||||
vehicleId !== undefined ? (vehicleId || null) : appt.vehicle_id,
|
||||
serviceDescription ?? appt.service_description,
|
||||
requestedDate ?? appt.requested_date,
|
||||
requestedTime ?? appt.requested_time,
|
||||
durationMinutes ?? appt.duration_minutes,
|
||||
status ?? appt.status,
|
||||
notes ?? appt.notes,
|
||||
appt.id,
|
||||
);
|
||||
|
||||
const updated = db.prepare('SELECT * FROM appointments WHERE id = ?').get(appt.id);
|
||||
res.json(updated);
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Delete appointment
|
||||
router.delete('/:id', (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
db.prepare('DELETE FROM appointments WHERE id = ?').run(req.params.id);
|
||||
res.json({ success: true });
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// --- Deferred Services ---
|
||||
router.get('/deferred', (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const { status, vehicleId } = req.query;
|
||||
let query = `
|
||||
SELECT d.*, o.first_name as owner_first_name, o.last_name as owner_last_name,
|
||||
v.year, v.make, v.model, j.job_number
|
||||
FROM deferred_services d
|
||||
LEFT JOIN owners o ON d.owner_id = o.id
|
||||
LEFT JOIN vehicles v ON d.vehicle_id = v.id
|
||||
LEFT JOIN jobs j ON d.job_id = j.id
|
||||
`;
|
||||
const conditions: string[] = [];
|
||||
const params: any[] = [];
|
||||
if (status) { conditions.push('d.status = ?'); params.push(status); }
|
||||
if (vehicleId) { conditions.push('d.vehicle_id = ?'); params.push(Number(vehicleId)); }
|
||||
if (conditions.length > 0) query += ' WHERE ' + conditions.join(' AND ');
|
||||
query += ' ORDER BY d.follow_up_date ASC';
|
||||
|
||||
const deferred = db.prepare(query).all(...params);
|
||||
res.json(deferred);
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/deferred', (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const { jobId, vehicleId, ownerId, description, reason, followUpDate } = req.body;
|
||||
if (!description) return res.status(400).json({ error: 'Description required' });
|
||||
|
||||
const result = db.prepare(`
|
||||
INSERT INTO deferred_services (job_id, vehicle_id, owner_id, description, reason, follow_up_date)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`).run(jobId || null, vehicleId || null, ownerId || null, description, reason || null, followUpDate || null);
|
||||
|
||||
const deferred = db.prepare('SELECT * FROM deferred_services WHERE id = ?').get(result.lastInsertRowid);
|
||||
res.status(201).json(deferred);
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.put('/deferred/:id', (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
const existing = db.prepare('SELECT * FROM deferred_services WHERE id = ?').get(req.params.id) as any;
|
||||
if (!existing) return res.status(404).json({ error: 'Deferred service not found' });
|
||||
|
||||
const { status, followUpDate } = req.body;
|
||||
db.prepare('UPDATE deferred_services SET status = ?, follow_up_date = ? WHERE id = ?')
|
||||
.run(status ?? existing.status, followUpDate ?? existing.follow_up_date, existing.id);
|
||||
|
||||
const updated = db.prepare('SELECT * FROM deferred_services WHERE id = ?').get(existing.id);
|
||||
res.json(updated);
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.delete('/deferred/:id', (req: AuthRequest, res: Response) => {
|
||||
try {
|
||||
db.prepare('DELETE FROM deferred_services WHERE id = ?').run(req.params.id);
|
||||
res.json({ success: true });
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user