"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.canvassAdminRouter = exports.canvassVolunteerRouter = void 0; const express_1 = require("express"); const client_1 = require("@prisma/client"); const canvass_service_1 = require("./canvass.service"); const canvass_schemas_1 = require("./canvass.schemas"); const locations_schemas_1 = require("../locations/locations.schemas"); const locations_service_1 = require("../locations/locations.service"); const geocoding_service_1 = require("../geocoding/geocoding.service"); const validate_1 = require("../../../middleware/validate"); const auth_middleware_1 = require("../../../middleware/auth.middleware"); const rbac_middleware_1 = require("../../../middleware/rbac.middleware"); const rate_limit_1 = require("../../../middleware/rate-limit"); const roles_1 = require("../../../utils/roles"); // ─── Volunteer Router ──────────────────────────────────────────────── const volunteerRouter = (0, express_1.Router)(); exports.canvassVolunteerRouter = volunteerRouter; volunteerRouter.use(auth_middleware_1.authenticate); // GET /api/map/canvass/my/assignments volunteerRouter.get('/my/assignments', async (req, res, next) => { try { const assignments = await canvass_service_1.canvassService.getMyAssignments(req.user.id); res.json(assignments); } catch (err) { next(err); } }); // GET /api/map/canvass/my/stats volunteerRouter.get('/my/stats', async (req, res, next) => { try { const stats = await canvass_service_1.canvassService.getMyStats(req.user.id); res.json(stats); } catch (err) { next(err); } }); // GET /api/map/canvass/my/visits volunteerRouter.get('/my/visits', (0, validate_1.validate)(canvass_schemas_1.listMyVisitsSchema, 'query'), async (req, res, next) => { try { const result = await canvass_service_1.canvassService.getMyVisits(req.user.id, req.query); res.json(result); } catch (err) { next(err); } }); // GET /api/map/canvass/my/session volunteerRouter.get('/my/session', async (req, res, next) => { try { const session = await canvass_service_1.canvassService.getActiveSession(req.user.id); res.json(session); } catch (err) { next(err); } }); // POST /api/map/canvass/sessions volunteerRouter.post('/sessions', (0, validate_1.validate)(canvass_schemas_1.startSessionSchema), async (req, res, next) => { try { const session = await canvass_service_1.canvassService.startSession(req.user.id, req.body); res.status(201).json(session); } catch (err) { next(err); } }); // POST /api/map/canvass/sessions/:id/end volunteerRouter.post('/sessions/:id/end', async (req, res, next) => { try { const id = req.params.id; const session = await canvass_service_1.canvassService.endSession(id, req.user.id); res.json(session); } catch (err) { next(err); } }); // GET /api/map/canvass/cuts/:cutId/locations volunteerRouter.get('/cuts/:cutId/locations', async (req, res, next) => { try { const cutId = req.params.cutId; const bounds = req.query.minLat ? { minLat: parseFloat(req.query.minLat), maxLat: parseFloat(req.query.maxLat), minLng: parseFloat(req.query.minLng), maxLng: parseFloat(req.query.maxLng), } : undefined; const limit = req.query.limit ? parseInt(req.query.limit) : undefined; const locations = await canvass_service_1.canvassService.getCutLocationsForCanvass(cutId, req.user.id, bounds, limit); res.json(locations); } catch (err) { next(err); } }); // GET /api/map/canvass/cuts/:cutId/route volunteerRouter.get('/cuts/:cutId/route', (0, validate_1.validate)(canvass_schemas_1.walkingRouteSchema, 'query'), async (req, res, next) => { try { const cutId = req.params.cutId; const route = await canvass_service_1.canvassService.getWalkingRoute(cutId, req.user.id, req.query); res.json(route); } catch (err) { next(err); } }); // GET /api/map/canvass/locations — all locations with visit annotations volunteerRouter.get('/locations', async (req, res, next) => { try { const bounds = req.query.minLat ? { minLat: parseFloat(req.query.minLat), maxLat: parseFloat(req.query.maxLat), minLng: parseFloat(req.query.minLng), maxLng: parseFloat(req.query.maxLng), } : undefined; const limit = req.query.limit ? parseInt(req.query.limit) : undefined; const locations = await canvass_service_1.canvassService.getAllLocationsForCanvass(req.user.id, bounds, limit); res.json(locations); } catch (err) { next(err); } }); // PUT /api/map/canvass/locations/:id — role-gated address editing (deprecated path, should be /addresses/:id) volunteerRouter.put('/locations/:id', (0, validate_1.validate)(canvass_schemas_1.volunteerUpdateLocationSchema), async (req, res, next) => { try { const id = req.params.id; const address = await canvass_service_1.canvassService.updateAddressAsVolunteer(id, req.user.id, req.user.role, req.body); res.json(address); } catch (err) { next(err); } }); // POST /api/map/canvass/locations — create a new location (role-gated fields) volunteerRouter.post('/locations', (0, validate_1.validate)(canvass_schemas_1.volunteerCreateLocationSchema), async (req, res, next) => { try { const data = { ...req.body }; // Strip fields based on role const userRoles = req.user.roles || [req.user.role]; const isAdmin = userRoles.some((r) => r === client_1.UserRole.SUPER_ADMIN || r === client_1.UserRole.MAP_ADMIN); if (!isAdmin) { delete data.firstName; delete data.lastName; delete data.email; delete data.phone; } if (userRoles.length === 1 && userRoles[0] === client_1.UserRole.TEMP) { delete data.supportLevel; delete data.sign; delete data.signSize; delete data.notes; } const location = await locations_service_1.locationsService.create(data, req.user.id); res.status(201).json(location); } catch (err) { next(err); } }); // POST /api/map/canvass/reverse-geocode — reverse geocode lat/lng volunteerRouter.post('/reverse-geocode', (0, validate_1.validate)(locations_schemas_1.reverseGeocodeSchema), async (req, res, next) => { try { const result = await locations_service_1.locationsService.reverseGeocode(req.body.latitude, req.body.longitude); res.json(result); } catch (err) { next(err); } }); // POST /api/map/canvass/geocode-search — geocode an address for map flyTo volunteerRouter.post('/geocode-search', rate_limit_1.canvassGeocodeRateLimit, (0, validate_1.validate)(locations_schemas_1.geocodeAddressSchema), async (req, res, next) => { try { const result = await geocoding_service_1.geocodingService.geocode(req.body.address); if (!result) { res.status(404).json({ error: { message: 'Address not found', code: 'GEOCODE_FAILED' } }); return; } res.json(result); } catch (err) { next(err); } }); // POST /api/map/canvass/visits volunteerRouter.post('/visits', rate_limit_1.canvassVisitRateLimit, (0, validate_1.validate)(canvass_schemas_1.recordVisitSchema), async (req, res, next) => { try { const visit = await canvass_service_1.canvassService.recordVisit(req.user.id, req.body); res.status(201).json(visit); } catch (err) { next(err); } }); // POST /api/map/canvass/visits/bulk - Record visit to all unvisited units in building volunteerRouter.post('/visits/bulk', rate_limit_1.canvassBulkVisitRateLimit, // Stricter rate limit for bulk operations (0, validate_1.validate)(canvass_schemas_1.bulkRecordVisitSchema), async (req, res, next) => { try { const result = await canvass_service_1.canvassService.recordBulkVisit(req.user.id, req.body); res.status(201).json(result); } catch (err) { next(err); } }); // ─── Admin Router ──────────────────────────────────────────────────── const adminRouter = (0, express_1.Router)(); exports.canvassAdminRouter = adminRouter; adminRouter.use(auth_middleware_1.authenticate); adminRouter.use((0, rbac_middleware_1.requireRole)(...roles_1.MAP_ROLES)); // GET /api/map/canvass/stats adminRouter.get('/stats', async (_req, res, next) => { try { const stats = await canvass_service_1.canvassService.getAdminStats(); res.json(stats); } catch (err) { next(err); } }); // GET /api/map/canvass/stats/cuts/:cutId adminRouter.get('/stats/cuts/:cutId', async (req, res, next) => { try { const cutId = req.params.cutId; const stats = await canvass_service_1.canvassService.getCutStats(cutId); res.json(stats); } catch (err) { next(err); } }); // GET /api/map/canvass/activity adminRouter.get('/activity', (0, validate_1.validate)(canvass_schemas_1.adminActivitySchema, 'query'), async (req, res, next) => { try { const result = await canvass_service_1.canvassService.getAdminActivity(req.query); res.json(result); } catch (err) { next(err); } }); // GET /api/map/canvass/volunteers adminRouter.get('/volunteers', async (_req, res, next) => { try { const volunteers = await canvass_service_1.canvassService.getVolunteers(); res.json(volunteers); } catch (err) { next(err); } }); // GET /api/map/canvass/volunteers/:userId adminRouter.get('/volunteers/:userId', async (req, res, next) => { try { const userId = req.params.userId; const stats = await canvass_service_1.canvassService.getVolunteerStats(userId); res.json(stats); } catch (err) { next(err); } }); // GET /api/map/canvass/visits adminRouter.get('/visits', (0, validate_1.validate)(canvass_schemas_1.adminVisitsSchema, 'query'), async (req, res, next) => { try { const result = await canvass_service_1.canvassService.getAdminVisits(req.query); res.json(result); } catch (err) { next(err); } }); // GET /api/map/canvass/trends adminRouter.get('/trends', (0, validate_1.validate)(canvass_schemas_1.outcomeTrendsQuerySchema, 'query'), async (req, res, next) => { try { const result = await canvass_service_1.canvassService.getOutcomeTrends(req.query); res.json(result); } catch (err) { next(err); } }); //# sourceMappingURL=canvass.routes.js.map