Upgrade system finished
This commit is contained in:
@@ -1 +1 @@
|
||||
{"version":3,"file":"canvass.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/canvass/canvass.routes.ts"],"names":[],"mappings":"AAyBA,QAAA,MAAM,eAAe,4CAAW,CAAC;AAgQjC,QAAA,MAAM,WAAW,4CAAW,CAAC;AAsF7B,OAAO,EAAE,eAAe,IAAI,sBAAsB,EAAE,WAAW,IAAI,kBAAkB,EAAE,CAAC"}
|
||||
{"version":3,"file":"canvass.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/canvass/canvass.routes.ts"],"names":[],"mappings":"AAyBA,QAAA,MAAM,eAAe,4CAAW,CAAC;AAgQjC,QAAA,MAAM,WAAW,4CAAW,CAAC;AAoG7B,OAAO,EAAE,eAAe,IAAI,sBAAsB,EAAE,WAAW,IAAI,kBAAkB,EAAE,CAAC"}
|
||||
20
api/dist/modules/map/canvass/canvass.routes.js
vendored
20
api/dist/modules/map/canvass/canvass.routes.js
vendored
@@ -12,7 +12,7 @@ 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 MAP_ADMIN_ROLES = [client_1.UserRole.SUPER_ADMIN, client_1.UserRole.MAP_ADMIN];
|
||||
const roles_1 = require("../../../utils/roles");
|
||||
// ─── Volunteer Router ────────────────────────────────────────────────
|
||||
const volunteerRouter = (0, express_1.Router)();
|
||||
exports.canvassVolunteerRouter = volunteerRouter;
|
||||
@@ -138,17 +138,17 @@ volunteerRouter.put('/locations/:id', (0, validate_1.validate)(canvass_schemas_1
|
||||
// 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 role = req.user.role;
|
||||
const data = { ...req.body };
|
||||
// Strip fields based on role
|
||||
const isAdmin = role === client_1.UserRole.SUPER_ADMIN || role === client_1.UserRole.MAP_ADMIN;
|
||||
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 (role === client_1.UserRole.TEMP) {
|
||||
if (userRoles.length === 1 && userRoles[0] === client_1.UserRole.TEMP) {
|
||||
delete data.supportLevel;
|
||||
delete data.sign;
|
||||
delete data.signSize;
|
||||
@@ -210,7 +210,7 @@ volunteerRouter.post('/visits/bulk', rate_limit_1.canvassBulkVisitRateLimit, //
|
||||
const adminRouter = (0, express_1.Router)();
|
||||
exports.canvassAdminRouter = adminRouter;
|
||||
adminRouter.use(auth_middleware_1.authenticate);
|
||||
adminRouter.use((0, rbac_middleware_1.requireRole)(...MAP_ADMIN_ROLES));
|
||||
adminRouter.use((0, rbac_middleware_1.requireRole)(...roles_1.MAP_ROLES));
|
||||
// GET /api/map/canvass/stats
|
||||
adminRouter.get('/stats', async (_req, res, next) => {
|
||||
try {
|
||||
@@ -273,4 +273,14 @@ adminRouter.get('/visits', (0, validate_1.validate)(canvass_schemas_1.adminVisit
|
||||
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
|
||||
File diff suppressed because one or more lines are too long
@@ -30,20 +30,20 @@ export declare const recordVisitSchema: z.ZodObject<{
|
||||
updateLocation: boolean;
|
||||
durationSeconds?: number | undefined;
|
||||
sessionId?: string | undefined;
|
||||
notes?: string | undefined;
|
||||
shiftId?: string | undefined;
|
||||
supportLevel?: "LEVEL_1" | "LEVEL_2" | "LEVEL_3" | "LEVEL_4" | undefined;
|
||||
signSize?: string | undefined;
|
||||
notes?: string | undefined;
|
||||
}, {
|
||||
outcome: "NOT_HOME" | "REFUSED" | "MOVED" | "ALREADY_VOTED" | "SPOKE_WITH" | "LEFT_LITERATURE" | "COME_BACK_LATER";
|
||||
addressId: string;
|
||||
durationSeconds?: number | undefined;
|
||||
sessionId?: string | undefined;
|
||||
notes?: string | undefined;
|
||||
shiftId?: string | undefined;
|
||||
supportLevel?: "LEVEL_1" | "LEVEL_2" | "LEVEL_3" | "LEVEL_4" | undefined;
|
||||
signSize?: string | undefined;
|
||||
notes?: string | undefined;
|
||||
signRequested?: boolean | undefined;
|
||||
signSize?: string | undefined;
|
||||
updateLocation?: boolean | undefined;
|
||||
}>;
|
||||
export declare const bulkRecordVisitSchema: z.ZodObject<{
|
||||
@@ -56,14 +56,14 @@ export declare const bulkRecordVisitSchema: z.ZodObject<{
|
||||
outcome: "NOT_HOME" | "REFUSED" | "MOVED";
|
||||
locationId: string;
|
||||
sessionId?: string | undefined;
|
||||
shiftId?: string | undefined;
|
||||
notes?: string | undefined;
|
||||
shiftId?: string | undefined;
|
||||
}, {
|
||||
outcome: "NOT_HOME" | "REFUSED" | "MOVED";
|
||||
locationId: string;
|
||||
sessionId?: string | undefined;
|
||||
shiftId?: string | undefined;
|
||||
notes?: string | undefined;
|
||||
shiftId?: string | undefined;
|
||||
}>;
|
||||
export declare const startSessionSchema: z.ZodObject<{
|
||||
cutId: z.ZodString;
|
||||
@@ -156,17 +156,17 @@ export declare const adminVisitsSchema: z.ZodObject<{
|
||||
sortOrder: "asc" | "desc";
|
||||
userId?: string | undefined;
|
||||
outcome?: "NOT_HOME" | "REFUSED" | "MOVED" | "ALREADY_VOTED" | "SPOKE_WITH" | "LEFT_LITERATURE" | "COME_BACK_LATER" | undefined;
|
||||
shiftId?: string | undefined;
|
||||
cutId?: string | undefined;
|
||||
shiftId?: string | undefined;
|
||||
}, {
|
||||
limit?: number | undefined;
|
||||
userId?: string | undefined;
|
||||
page?: number | undefined;
|
||||
outcome?: "NOT_HOME" | "REFUSED" | "MOVED" | "ALREADY_VOTED" | "SPOKE_WITH" | "LEFT_LITERATURE" | "COME_BACK_LATER" | undefined;
|
||||
shiftId?: string | undefined;
|
||||
cutId?: string | undefined;
|
||||
sortBy?: "outcome" | "visitedAt" | undefined;
|
||||
sortOrder?: "asc" | "desc" | undefined;
|
||||
outcome?: "NOT_HOME" | "REFUSED" | "MOVED" | "ALREADY_VOTED" | "SPOKE_WITH" | "LEFT_LITERATURE" | "COME_BACK_LATER" | undefined;
|
||||
cutId?: string | undefined;
|
||||
shiftId?: string | undefined;
|
||||
}>;
|
||||
export declare const volunteerUpdateLocationSchema: z.ZodObject<{
|
||||
supportLevel: z.ZodOptional<z.ZodNullable<z.ZodNativeEnum<{
|
||||
@@ -187,25 +187,25 @@ export declare const volunteerUpdateLocationSchema: z.ZodObject<{
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
email?: string | null | undefined;
|
||||
phone?: string | null | undefined;
|
||||
address?: string | null | undefined;
|
||||
unitNumber?: string | null | undefined;
|
||||
notes?: string | null | undefined;
|
||||
firstName?: string | null | undefined;
|
||||
lastName?: string | null | undefined;
|
||||
supportLevel?: "LEVEL_1" | "LEVEL_2" | "LEVEL_3" | "LEVEL_4" | null | undefined;
|
||||
address?: string | null | undefined;
|
||||
unitNumber?: string | null | undefined;
|
||||
sign?: boolean | undefined;
|
||||
signSize?: string | null | undefined;
|
||||
notes?: string | null | undefined;
|
||||
}, {
|
||||
email?: string | null | undefined;
|
||||
phone?: string | null | undefined;
|
||||
address?: string | null | undefined;
|
||||
unitNumber?: string | null | undefined;
|
||||
notes?: string | null | undefined;
|
||||
firstName?: string | null | undefined;
|
||||
lastName?: string | null | undefined;
|
||||
supportLevel?: "LEVEL_1" | "LEVEL_2" | "LEVEL_3" | "LEVEL_4" | null | undefined;
|
||||
address?: string | null | undefined;
|
||||
unitNumber?: string | null | undefined;
|
||||
sign?: boolean | undefined;
|
||||
signSize?: string | null | undefined;
|
||||
notes?: string | null | undefined;
|
||||
}>;
|
||||
export declare const volunteerCreateLocationSchema: z.ZodObject<{
|
||||
address: z.ZodString;
|
||||
@@ -232,25 +232,25 @@ export declare const volunteerCreateLocationSchema: z.ZodObject<{
|
||||
sign: boolean;
|
||||
email?: string | undefined;
|
||||
phone?: string | undefined;
|
||||
unitNumber?: string | undefined;
|
||||
notes?: string | undefined;
|
||||
firstName?: string | undefined;
|
||||
lastName?: string | undefined;
|
||||
supportLevel?: "LEVEL_1" | "LEVEL_2" | "LEVEL_3" | "LEVEL_4" | undefined;
|
||||
unitNumber?: string | undefined;
|
||||
signSize?: string | undefined;
|
||||
notes?: string | undefined;
|
||||
}, {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
address: string;
|
||||
email?: string | undefined;
|
||||
phone?: string | undefined;
|
||||
unitNumber?: string | undefined;
|
||||
notes?: string | undefined;
|
||||
firstName?: string | undefined;
|
||||
lastName?: string | undefined;
|
||||
supportLevel?: "LEVEL_1" | "LEVEL_2" | "LEVEL_3" | "LEVEL_4" | undefined;
|
||||
unitNumber?: string | undefined;
|
||||
sign?: boolean | undefined;
|
||||
signSize?: string | undefined;
|
||||
notes?: string | undefined;
|
||||
}>;
|
||||
export type RecordVisitInput = z.infer<typeof recordVisitSchema>;
|
||||
export type BulkRecordVisitInput = z.infer<typeof bulkRecordVisitSchema>;
|
||||
@@ -259,6 +259,20 @@ export type WalkingRouteInput = z.infer<typeof walkingRouteSchema>;
|
||||
export type ListMyVisitsInput = z.infer<typeof listMyVisitsSchema>;
|
||||
export type AdminActivityInput = z.infer<typeof adminActivitySchema>;
|
||||
export type AdminVisitsInput = z.infer<typeof adminVisitsSchema>;
|
||||
export declare const outcomeTrendsQuerySchema: z.ZodObject<{
|
||||
granularity: z.ZodDefault<z.ZodEnum<["day", "week"]>>;
|
||||
dateFrom: z.ZodOptional<z.ZodString>;
|
||||
dateTo: z.ZodOptional<z.ZodString>;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
granularity: "week" | "day";
|
||||
dateFrom?: string | undefined;
|
||||
dateTo?: string | undefined;
|
||||
}, {
|
||||
dateFrom?: string | undefined;
|
||||
dateTo?: string | undefined;
|
||||
granularity?: "week" | "day" | undefined;
|
||||
}>;
|
||||
export type VolunteerUpdateLocationInput = z.infer<typeof volunteerUpdateLocationSchema>;
|
||||
export type VolunteerCreateLocationInput = z.infer<typeof volunteerCreateLocationSchema>;
|
||||
export type OutcomeTrendsQueryInput = z.infer<typeof outcomeTrendsQuerySchema>;
|
||||
//# sourceMappingURL=canvass.schemas.d.ts.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"canvass.schemas.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/canvass/canvass.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAW5B,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;EAMhC,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;EAK7B,CAAC;AAEH,eAAO,MAAM,gBAAgB,gDAE3B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;EAI7B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;EAG7B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;EAM9B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAS5B,CAAC;AAEH,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYxC,CAAC;AAEH,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAexC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AACjE,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACzE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACnE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACnE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACnE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACrE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AACjE,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AACzF,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC"}
|
||||
{"version":3,"file":"canvass.schemas.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/canvass/canvass.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAW5B,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;EAMhC,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;EAK7B,CAAC;AAEH,eAAO,MAAM,gBAAgB,gDAE3B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;EAI7B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;EAG7B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;EAM9B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAS5B,CAAC;AAEH,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYxC,CAAC;AAEH,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAexC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AACjE,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACzE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACnE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACnE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACnE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACrE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AACjE,eAAO,MAAM,wBAAwB;;;;;;;;;;;;EAInC,CAAC;AAEH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AACzF,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AACzF,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC"}
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.volunteerCreateLocationSchema = exports.volunteerUpdateLocationSchema = exports.adminVisitsSchema = exports.adminActivitySchema = exports.listMyVisitsSchema = exports.walkingRouteSchema = exports.endSessionSchema = exports.startSessionSchema = exports.bulkRecordVisitSchema = exports.recordVisitSchema = void 0;
|
||||
exports.outcomeTrendsQuerySchema = exports.volunteerCreateLocationSchema = exports.volunteerUpdateLocationSchema = exports.adminVisitsSchema = exports.adminActivitySchema = exports.listMyVisitsSchema = exports.walkingRouteSchema = exports.endSessionSchema = exports.startSessionSchema = exports.bulkRecordVisitSchema = exports.recordVisitSchema = void 0;
|
||||
const zod_1 = require("zod");
|
||||
const client_1 = require("@prisma/client");
|
||||
exports.recordVisitSchema = zod_1.z.object({
|
||||
@@ -86,4 +86,9 @@ exports.volunteerCreateLocationSchema = zod_1.z.object({
|
||||
email: zod_1.z.string().email().optional().or(zod_1.z.literal('')),
|
||||
phone: zod_1.z.string().optional(),
|
||||
});
|
||||
exports.outcomeTrendsQuerySchema = zod_1.z.object({
|
||||
granularity: zod_1.z.enum(['day', 'week']).default('day'),
|
||||
dateFrom: zod_1.z.string().optional(),
|
||||
dateTo: zod_1.z.string().optional(),
|
||||
});
|
||||
//# sourceMappingURL=canvass.schemas.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
|
||||
import { Prisma, UserRole } from '@prisma/client';
|
||||
import type { RecordVisitInput, BulkRecordVisitInput, StartSessionInput, WalkingRouteInput, ListMyVisitsInput, AdminActivityInput, AdminVisitsInput, VolunteerUpdateLocationInput } from './canvass.schemas';
|
||||
import type { RecordVisitInput, BulkRecordVisitInput, StartSessionInput, WalkingRouteInput, ListMyVisitsInput, AdminActivityInput, AdminVisitsInput, VolunteerUpdateLocationInput, OutcomeTrendsQueryInput } from './canvass.schemas';
|
||||
export declare const canvassService: {
|
||||
getMyAssignments(userId: string): Promise<{
|
||||
shiftId: string;
|
||||
@@ -32,12 +32,12 @@ export declare const canvassService: {
|
||||
durationSeconds: number | null;
|
||||
userId: string;
|
||||
sessionId: string | null;
|
||||
notes: string | null;
|
||||
outcome: import(".prisma/client").$Enums.VisitOutcome;
|
||||
shiftId: string | null;
|
||||
supportLevel: import(".prisma/client").$Enums.SupportLevel | null;
|
||||
signSize: string | null;
|
||||
notes: string | null;
|
||||
signRequested: boolean;
|
||||
signSize: string | null;
|
||||
visitedAt: Date;
|
||||
addressId: string;
|
||||
})[];
|
||||
@@ -61,12 +61,12 @@ export declare const canvassService: {
|
||||
status: import(".prisma/client").$Enums.CanvassSessionStatus;
|
||||
id: string;
|
||||
userId: string;
|
||||
cutId: string;
|
||||
shiftId: string | null;
|
||||
startedAt: Date;
|
||||
endedAt: Date | null;
|
||||
startLatitude: Prisma.Decimal | null;
|
||||
startLongitude: Prisma.Decimal | null;
|
||||
cutId: string;
|
||||
}) | null>;
|
||||
startSession(userId: string, data: StartSessionInput): Promise<{
|
||||
shift: {
|
||||
@@ -81,23 +81,23 @@ export declare const canvassService: {
|
||||
status: import(".prisma/client").$Enums.CanvassSessionStatus;
|
||||
id: string;
|
||||
userId: string;
|
||||
cutId: string;
|
||||
shiftId: string | null;
|
||||
startedAt: Date;
|
||||
endedAt: Date | null;
|
||||
startLatitude: Prisma.Decimal | null;
|
||||
startLongitude: Prisma.Decimal | null;
|
||||
cutId: string;
|
||||
}>;
|
||||
endSession(sessionId: string, userId: string): Promise<{
|
||||
status: import(".prisma/client").$Enums.CanvassSessionStatus;
|
||||
id: string;
|
||||
userId: string;
|
||||
cutId: string;
|
||||
shiftId: string | null;
|
||||
startedAt: Date;
|
||||
endedAt: Date | null;
|
||||
startLatitude: Prisma.Decimal | null;
|
||||
startLongitude: Prisma.Decimal | null;
|
||||
cutId: string;
|
||||
}>;
|
||||
getCutLocationsForCanvass(cutId: string, userId: string, bounds?: {
|
||||
minLat: number;
|
||||
@@ -121,13 +121,13 @@ export declare const canvassService: {
|
||||
id: string;
|
||||
email: string | null;
|
||||
phone: string | null;
|
||||
unitNumber: string | null;
|
||||
notes: string | null;
|
||||
firstName: string | null;
|
||||
lastName: string | null;
|
||||
supportLevel: import(".prisma/client").$Enums.SupportLevel | null;
|
||||
unitNumber: string | null;
|
||||
sign: boolean;
|
||||
signSize: string | null;
|
||||
notes: string | null;
|
||||
}[]>;
|
||||
getAllLocationsForCanvass(userId: string, bounds?: {
|
||||
minLat: number;
|
||||
@@ -151,13 +151,13 @@ export declare const canvassService: {
|
||||
id: string;
|
||||
email: string | null;
|
||||
phone: string | null;
|
||||
unitNumber: string | null;
|
||||
notes: string | null;
|
||||
firstName: string | null;
|
||||
lastName: string | null;
|
||||
supportLevel: import(".prisma/client").$Enums.SupportLevel | null;
|
||||
unitNumber: string | null;
|
||||
sign: boolean;
|
||||
signSize: string | null;
|
||||
notes: string | null;
|
||||
}[]>;
|
||||
updateAddressAsVolunteer(addressId: string, userId: string, role: UserRole, data: VolunteerUpdateLocationInput): Promise<{
|
||||
id: string;
|
||||
@@ -165,16 +165,16 @@ export declare const canvassService: {
|
||||
phone: string | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
notes: string | null;
|
||||
createdByUserId: string | null;
|
||||
updatedByUserId: string | null;
|
||||
unitNumber: string | null;
|
||||
addrGuid: string | null;
|
||||
firstName: string | null;
|
||||
lastName: string | null;
|
||||
supportLevel: import(".prisma/client").$Enums.SupportLevel | null;
|
||||
unitNumber: string | null;
|
||||
addrGuid: string | null;
|
||||
sign: boolean;
|
||||
signSize: string | null;
|
||||
notes: string | null;
|
||||
locationId: string;
|
||||
}>;
|
||||
getWalkingRoute(cutId: string, userId: string, filters: WalkingRouteInput): Promise<import("./canvass-route.service").RouteResult>;
|
||||
@@ -191,12 +191,12 @@ export declare const canvassService: {
|
||||
durationSeconds: number | null;
|
||||
userId: string;
|
||||
sessionId: string | null;
|
||||
notes: string | null;
|
||||
outcome: import(".prisma/client").$Enums.VisitOutcome;
|
||||
shiftId: string | null;
|
||||
supportLevel: import(".prisma/client").$Enums.SupportLevel | null;
|
||||
signSize: string | null;
|
||||
notes: string | null;
|
||||
signRequested: boolean;
|
||||
signSize: string | null;
|
||||
visitedAt: Date;
|
||||
addressId: string;
|
||||
}>;
|
||||
@@ -207,12 +207,12 @@ export declare const canvassService: {
|
||||
durationSeconds: number | null;
|
||||
userId: string;
|
||||
sessionId: string | null;
|
||||
notes: string | null;
|
||||
outcome: import(".prisma/client").$Enums.VisitOutcome;
|
||||
shiftId: string | null;
|
||||
supportLevel: import(".prisma/client").$Enums.SupportLevel | null;
|
||||
signSize: string | null;
|
||||
notes: string | null;
|
||||
signRequested: boolean;
|
||||
signSize: string | null;
|
||||
visitedAt: Date;
|
||||
addressId: string;
|
||||
}[];
|
||||
@@ -252,12 +252,12 @@ export declare const canvassService: {
|
||||
durationSeconds: number | null;
|
||||
userId: string;
|
||||
sessionId: string | null;
|
||||
notes: string | null;
|
||||
outcome: import(".prisma/client").$Enums.VisitOutcome;
|
||||
shiftId: string | null;
|
||||
supportLevel: import(".prisma/client").$Enums.SupportLevel | null;
|
||||
signSize: string | null;
|
||||
notes: string | null;
|
||||
signRequested: boolean;
|
||||
signSize: string | null;
|
||||
visitedAt: Date;
|
||||
addressId: string;
|
||||
})[];
|
||||
@@ -301,12 +301,12 @@ export declare const canvassService: {
|
||||
durationSeconds: number | null;
|
||||
userId: string;
|
||||
sessionId: string | null;
|
||||
notes: string | null;
|
||||
outcome: import(".prisma/client").$Enums.VisitOutcome;
|
||||
shiftId: string | null;
|
||||
supportLevel: import(".prisma/client").$Enums.SupportLevel | null;
|
||||
signSize: string | null;
|
||||
notes: string | null;
|
||||
signRequested: boolean;
|
||||
signSize: string | null;
|
||||
visitedAt: Date;
|
||||
addressId: string;
|
||||
})[];
|
||||
@@ -317,6 +317,15 @@ export declare const canvassService: {
|
||||
totalPages: number;
|
||||
};
|
||||
}>;
|
||||
getOutcomeTrends(filters: OutcomeTrendsQueryInput): Promise<{
|
||||
granularity: "week" | "day";
|
||||
dateFrom: string;
|
||||
dateTo: string;
|
||||
series: {
|
||||
date: string;
|
||||
}[];
|
||||
totals: Record<string, number>;
|
||||
}>;
|
||||
recalculateCutCompletion(cutId: string): Promise<void>;
|
||||
closeAbandonedSessions(): Promise<number>;
|
||||
};
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"canvass.service.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/canvass/canvass.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAoD,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AASpG,OAAO,KAAK,EACV,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,4BAA4B,EAC7B,MAAM,mBAAmB,CAAC;AAwE3B,eAAO,MAAM,cAAc;6BAGM,MAAM;;;;;;;;;;;uBAgCZ,MAAM;;;;;;wBAuBL,MAAM,WAAW,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BA6B7B,MAAM;;;;;;;;;;;;;;;;;;;;yBAUV,MAAM,QAAQ,iBAAiB;;;;;;;;;;;;;;;;;;;;0BAgC9B,MAAM,UAAU,MAAM;;;;;;;;;;;qCAwBzC,MAAM,UACL,MAAM,WACL;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,UACnE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;sCAoGN,MAAM,WACL;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,UACnE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;wCAuFH,MAAM,UACT,MAAM,QACR,QAAQ,QACR,4BAA4B;;;;;;;;;;;;;;;;;;2BA+BP,MAAM,UAAU,MAAM,WAAW,iBAAiB;wBA2BrD,MAAM,QAAQ,gBAAgB;;;;;;;;;;;;;;;;;;;;;;4BA0D1B,MAAM,QAAQ,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;uBAqHvC,MAAM;;;;;;;;;8BAuDC,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8BA0FlB,MAAM;;;;;;4BAIR,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oCA4DR,MAAM;;CA8D7C,CAAC"}
|
||||
{"version":3,"file":"canvass.service.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/canvass/canvass.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAoD,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAgBpG,OAAO,KAAK,EACV,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,4BAA4B,EAC5B,uBAAuB,EACxB,MAAM,mBAAmB,CAAC;AAwE3B,eAAO,MAAM,cAAc;6BAGM,MAAM;;;;;;;;;;;uBAgCZ,MAAM;;;;;;wBAuBL,MAAM,WAAW,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BA6B7B,MAAM;;;;;;;;;;;;;;;;;;;;yBAUV,MAAM,QAAQ,iBAAiB;;;;;;;;;;;;;;;;;;;;0BAgC9B,MAAM,UAAU,MAAM;;;;;;;;;;;qCA4GzC,MAAM,UACL,MAAM,WACL;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,UACnE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;sCAoGN,MAAM,WACL;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,UACnE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;wCAuFH,MAAM,UACT,MAAM,QACR,QAAQ,QACR,4BAA4B;;;;;;;;;;;;;;;;;;2BA+BP,MAAM,UAAU,MAAM,WAAW,iBAAiB;wBA2BrD,MAAM,QAAQ,gBAAgB;;;;;;;;;;;;;;;;;;;;;;4BAuH1B,MAAM,QAAQ,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;uBAqHvC,MAAM;;;;;;;;;8BAuDC,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8BA0FlB,MAAM;;;;;;4BAIR,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8BA0Dd,uBAAuB;;;;;;;;;oCAoDjB,MAAM;;CA8D7C,CAAC"}
|
||||
187
api/dist/modules/map/canvass/canvass.service.js
vendored
187
api/dist/modules/map/canvass/canvass.service.js
vendored
@@ -8,8 +8,15 @@ const error_handler_1 = require("../../../middleware/error-handler");
|
||||
const logger_1 = require("../../../utils/logger");
|
||||
const metrics_1 = require("../../../utils/metrics");
|
||||
const spatial_1 = require("../../../utils/spatial");
|
||||
const crm_activity_1 = require("../../../utils/crm-activity");
|
||||
const canvass_route_service_1 = require("./canvass-route.service");
|
||||
const metrics_2 = require("../../../utils/metrics");
|
||||
const notification_queue_service_1 = require("../../../services/notification-queue.service");
|
||||
const notification_helper_1 = require("../../../services/notification.helper");
|
||||
const env_1 = require("../../../config/env");
|
||||
const rocketchat_webhook_service_1 = require("../../../services/rocketchat-webhook.service");
|
||||
const listmonk_event_sync_service_1 = require("../../../services/listmonk-event-sync.service");
|
||||
const achievements_service_1 = require("../../social/achievements.service");
|
||||
const ADDRESS_SELECT = {
|
||||
id: true,
|
||||
unitNumber: true,
|
||||
@@ -201,6 +208,87 @@ exports.canvassService = {
|
||||
});
|
||||
// Recalculate cut completion percentage
|
||||
await this.recalculateCutCompletion(session.cutId);
|
||||
// Notify Rocket.Chat
|
||||
try {
|
||||
const [rcUser, rcCut, rcVisitCount] = await Promise.all([
|
||||
database_1.prisma.user.findUnique({ where: { id: userId }, select: { name: true, email: true } }),
|
||||
database_1.prisma.cut.findUnique({ where: { id: session.cutId }, select: { name: true } }),
|
||||
database_1.prisma.canvassVisit.count({ where: { sessionId } }),
|
||||
]);
|
||||
rocketchat_webhook_service_1.rocketchatWebhookService.onCanvassSessionCompleted({
|
||||
userName: rcUser?.name || rcUser?.email || 'Unknown',
|
||||
visitCount: rcVisitCount,
|
||||
cutName: rcCut?.name || undefined,
|
||||
}).catch(() => { });
|
||||
}
|
||||
catch { /* non-critical */ }
|
||||
// Notification: volunteer session summary
|
||||
try {
|
||||
if (await (0, notification_helper_1.isNotificationEnabled)('notifyVolunteerSessionSummary')) {
|
||||
const [user, cut, visitCount, outcomeGroups, trackingSession] = await Promise.all([
|
||||
database_1.prisma.user.findUnique({ where: { id: userId }, select: { email: true, name: true } }),
|
||||
database_1.prisma.cut.findUnique({ where: { id: session.cutId }, select: { name: true } }),
|
||||
database_1.prisma.canvassVisit.count({ where: { sessionId } }),
|
||||
database_1.prisma.canvassVisit.groupBy({ by: ['outcome'], where: { sessionId }, _count: true }),
|
||||
database_1.prisma.trackingSession.findFirst({
|
||||
where: { userId, canvassSessionId: sessionId },
|
||||
select: { totalDistanceM: true },
|
||||
}),
|
||||
]);
|
||||
if (user && visitCount > 0) {
|
||||
const durationMs = updated.endedAt
|
||||
? updated.endedAt.getTime() - session.startedAt.getTime()
|
||||
: 0;
|
||||
const durationMinutes = Math.round(durationMs / 60000);
|
||||
const distanceKm = trackingSession?.totalDistanceM
|
||||
? Number(trackingSession.totalDistanceM) / 1000
|
||||
: 0;
|
||||
const outcomeBreakdown = {};
|
||||
for (const row of outcomeGroups) {
|
||||
outcomeBreakdown[row.outcome] = row._count;
|
||||
}
|
||||
await notification_queue_service_1.notificationQueueService.enqueue({
|
||||
type: 'volunteer-session-summary',
|
||||
volunteerEmail: user.email,
|
||||
volunteerName: user.name || user.email,
|
||||
cutName: cut?.name || 'Unknown area',
|
||||
sessionDate: session.startedAt.toLocaleDateString('en-CA', {
|
||||
weekday: 'long', year: 'numeric', month: 'long', day: 'numeric',
|
||||
}),
|
||||
visitCount,
|
||||
durationMinutes,
|
||||
distanceKm,
|
||||
outcomeBreakdown,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger_1.logger.error('Failed to enqueue session summary notification:', err);
|
||||
}
|
||||
// Listmonk event sync — add canvasser to subscribers
|
||||
try {
|
||||
const [syncUser, syncCut, syncVisitCount, syncOutcomes] = await Promise.all([
|
||||
database_1.prisma.user.findUnique({ where: { id: userId }, select: { email: true, name: true } }),
|
||||
database_1.prisma.cut.findUnique({ where: { id: session.cutId }, select: { name: true } }),
|
||||
database_1.prisma.canvassVisit.count({ where: { sessionId } }),
|
||||
database_1.prisma.canvassVisit.groupBy({ by: ['outcome'], where: { sessionId }, _count: true }),
|
||||
]);
|
||||
if (syncUser) {
|
||||
const outcomes = {};
|
||||
for (const row of syncOutcomes) {
|
||||
outcomes[row.outcome] = row._count;
|
||||
}
|
||||
listmonk_event_sync_service_1.listmonkEventSyncService.onCanvassSessionCompleted({
|
||||
email: syncUser.email,
|
||||
name: syncUser.name || syncUser.email,
|
||||
cutName: syncCut?.name || 'Unknown',
|
||||
visitCount: syncVisitCount,
|
||||
outcomes,
|
||||
}).catch(() => { });
|
||||
}
|
||||
}
|
||||
catch { /* non-critical */ }
|
||||
return updated;
|
||||
},
|
||||
async getCutLocationsForCanvass(cutId, userId, bounds, limit) {
|
||||
@@ -448,12 +536,68 @@ exports.canvassService = {
|
||||
const prefix = `[${timestamp}] ${data.notes}`;
|
||||
updateData.notes = address.notes ? `${prefix}\n${address.notes}` : prefix;
|
||||
}
|
||||
await database_1.prisma.address.update({
|
||||
const updatedAddress = await database_1.prisma.address.update({
|
||||
where: { id: data.addressId },
|
||||
data: updateData,
|
||||
include: { location: { select: { address: true } } },
|
||||
});
|
||||
// Sync support level change to Listmonk (fire-and-forget)
|
||||
if (updatedAddress.email) {
|
||||
const name = [updatedAddress.firstName, updatedAddress.lastName].filter(Boolean).join(' ');
|
||||
listmonk_event_sync_service_1.listmonkEventSyncService.onAddressUpdated({
|
||||
email: updatedAddress.email,
|
||||
name,
|
||||
supportLevel: updatedAddress.supportLevel,
|
||||
sign: updatedAddress.sign,
|
||||
address: updatedAddress.location.address,
|
||||
}).catch(() => { });
|
||||
}
|
||||
}
|
||||
(0, metrics_2.recordCanvassVisit)(data.outcome);
|
||||
// CRM activity via ContactAddress lookup (fire-and-forget)
|
||||
database_1.prisma.contactAddress.findFirst({
|
||||
where: { addressId: data.addressId },
|
||||
select: { contactId: true },
|
||||
}).then((ca) => {
|
||||
if (ca) {
|
||||
(0, crm_activity_1.recordCrmActivity)({
|
||||
contactId: ca.contactId,
|
||||
activityType: 'CANVASS_VISIT',
|
||||
title: `Canvass visit: ${data.outcome}`,
|
||||
metadata: { addressId: data.addressId, outcome: data.outcome, visitId: visit.id },
|
||||
}).catch(() => { });
|
||||
}
|
||||
}).catch(() => { });
|
||||
// Achievement check (fire-and-forget)
|
||||
achievements_service_1.achievementsService.checkAndUnlock(userId, ['canvass']).catch(() => { });
|
||||
// Notification: sign request alert for admins
|
||||
if (data.signRequested) {
|
||||
try {
|
||||
if (await (0, notification_helper_1.isNotificationEnabled)('notifyAdminSignRequested')) {
|
||||
const adminEmails = await (0, notification_helper_1.getAdminEmailsByRole)([client_1.UserRole.SUPER_ADMIN, client_1.UserRole.MAP_ADMIN]);
|
||||
if (adminEmails.length > 0) {
|
||||
const [volunteer, shift] = await Promise.all([
|
||||
database_1.prisma.user.findUnique({ where: { id: userId }, select: { name: true } }),
|
||||
data.shiftId ? database_1.prisma.shift.findUnique({ where: { id: data.shiftId }, select: { title: true } }) : null,
|
||||
]);
|
||||
const addressStr = visit.address?.location?.address || 'Unknown address';
|
||||
const adminUrl = `${env_1.env.ADMIN_URL || 'http://localhost:3000'}/app/canvass/dashboard`;
|
||||
await notification_queue_service_1.notificationQueueService.enqueue({
|
||||
type: 'admin-sign-requested',
|
||||
adminEmails,
|
||||
volunteerName: volunteer?.name || 'Unknown',
|
||||
address: addressStr,
|
||||
shiftTitle: shift?.title || 'No shift',
|
||||
signSize: data.signSize || '',
|
||||
adminUrl,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger_1.logger.error('Failed to enqueue sign request notification:', err);
|
||||
}
|
||||
}
|
||||
return visit;
|
||||
},
|
||||
async recordBulkVisit(userId, data) {
|
||||
@@ -736,6 +880,47 @@ exports.canvassService = {
|
||||
pagination: { page, limit, total, totalPages: Math.ceil(total / limit) },
|
||||
};
|
||||
},
|
||||
async getOutcomeTrends(filters) {
|
||||
const { granularity } = filters;
|
||||
const dateTo = filters.dateTo ? new Date(filters.dateTo) : new Date();
|
||||
const dateFrom = filters.dateFrom
|
||||
? new Date(filters.dateFrom)
|
||||
: new Date(dateTo.getTime() - 30 * 24 * 60 * 60 * 1000);
|
||||
// Ensure dateTo covers end of day
|
||||
const dateToEnd = new Date(dateTo);
|
||||
dateToEnd.setHours(23, 59, 59, 999);
|
||||
const rows = await database_1.prisma.$queryRaw `
|
||||
SELECT DATE_TRUNC(${granularity}, "visitedAt") as period,
|
||||
outcome::text as outcome,
|
||||
COUNT(*)::int as count
|
||||
FROM canvass_visits
|
||||
WHERE "visitedAt" >= ${dateFrom} AND "visitedAt" <= ${dateToEnd}
|
||||
GROUP BY period, outcome
|
||||
ORDER BY period ASC
|
||||
`;
|
||||
// Pivot rows into series: [{ date, NOT_HOME: n, SPOKE_WITH: n, ... }]
|
||||
const pivotMap = new Map();
|
||||
const totals = {};
|
||||
for (const row of rows) {
|
||||
const dateStr = row.period.toISOString().split('T')[0];
|
||||
if (!pivotMap.has(dateStr)) {
|
||||
pivotMap.set(dateStr, {});
|
||||
}
|
||||
pivotMap.get(dateStr)[row.outcome] = row.count;
|
||||
totals[row.outcome] = (totals[row.outcome] || 0) + row.count;
|
||||
}
|
||||
const series = Array.from(pivotMap.entries()).map(([date, outcomes]) => ({
|
||||
date,
|
||||
...outcomes,
|
||||
}));
|
||||
return {
|
||||
granularity,
|
||||
dateFrom: dateFrom.toISOString().split('T')[0],
|
||||
dateTo: dateTo.toISOString().split('T')[0],
|
||||
series,
|
||||
totals,
|
||||
};
|
||||
},
|
||||
// ─── Helpers ───────────────────────────────────────────────────────
|
||||
async recalculateCutCompletion(cutId) {
|
||||
const cut = await database_1.prisma.cut.findUnique({ where: { id: cutId } });
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
{"version":3,"file":"cuts.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/cuts/cuts.routes.ts"],"names":[],"mappings":"AAwBA,QAAA,MAAM,WAAW,4CAAW,CAAC;AAyJ7B,QAAA,MAAM,YAAY,4CAAW,CAAC;AAe9B,OAAO,EAAE,WAAW,IAAI,eAAe,EAAE,YAAY,IAAI,gBAAgB,EAAE,CAAC"}
|
||||
{"version":3,"file":"cuts.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/cuts/cuts.routes.ts"],"names":[],"mappings":"AAsBA,QAAA,MAAM,WAAW,4CAAW,CAAC;AAyJ7B,QAAA,MAAM,YAAY,4CAAW,CAAC;AAe9B,OAAO,EAAE,WAAW,IAAI,eAAe,EAAE,YAAY,IAAI,gBAAgB,EAAE,CAAC"}
|
||||
5
api/dist/modules/map/cuts/cuts.routes.js
vendored
5
api/dist/modules/map/cuts/cuts.routes.js
vendored
@@ -5,13 +5,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.cutsPublicRouter = exports.cutsAdminRouter = void 0;
|
||||
const express_1 = require("express");
|
||||
const client_1 = require("@prisma/client");
|
||||
const multer_1 = __importDefault(require("multer"));
|
||||
const cuts_service_1 = require("./cuts.service");
|
||||
const cuts_schemas_1 = require("./cuts.schemas");
|
||||
const validate_1 = require("../../../middleware/validate");
|
||||
const auth_middleware_1 = require("../../../middleware/auth.middleware");
|
||||
const rbac_middleware_1 = require("../../../middleware/rbac.middleware");
|
||||
const roles_1 = require("../../../utils/roles");
|
||||
const geojsonUpload = (0, multer_1.default)({
|
||||
storage: multer_1.default.memoryStorage(),
|
||||
limits: { fileSize: 10 * 1024 * 1024 },
|
||||
@@ -24,12 +24,11 @@ const geojsonUpload = (0, multer_1.default)({
|
||||
}
|
||||
},
|
||||
});
|
||||
const MAP_ADMIN_ROLES = [client_1.UserRole.SUPER_ADMIN, client_1.UserRole.MAP_ADMIN];
|
||||
// --- Admin Router ---
|
||||
const adminRouter = (0, express_1.Router)();
|
||||
exports.cutsAdminRouter = adminRouter;
|
||||
adminRouter.use(auth_middleware_1.authenticate);
|
||||
adminRouter.use((0, rbac_middleware_1.requireRole)(...MAP_ADMIN_ROLES));
|
||||
adminRouter.use((0, rbac_middleware_1.requireRole)(...roles_1.MAP_ROLES));
|
||||
// GET /api/map/cuts — list paginated
|
||||
adminRouter.get('/', (0, validate_1.validate)(cuts_schemas_1.listCutsSchema, 'query'), async (req, res, next) => {
|
||||
try {
|
||||
|
||||
2
api/dist/modules/map/cuts/cuts.routes.js.map
vendored
2
api/dist/modules/map/cuts/cuts.routes.js.map
vendored
File diff suppressed because one or more lines are too long
10
api/dist/modules/map/cuts/cuts.schemas.d.ts
vendored
10
api/dist/modules/map/cuts/cuts.schemas.d.ts
vendored
@@ -19,9 +19,9 @@ export declare const createCutSchema: z.ZodObject<{
|
||||
assignedTo: z.ZodOptional<z.ZodString>;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
name: string;
|
||||
isPublic: boolean;
|
||||
color: string;
|
||||
opacity: number;
|
||||
isPublic: boolean;
|
||||
isOfficial: boolean;
|
||||
geojson: string;
|
||||
showLocations: boolean;
|
||||
@@ -35,9 +35,9 @@ export declare const createCutSchema: z.ZodObject<{
|
||||
geojson: string;
|
||||
category?: "CUSTOM" | "WARD" | "NEIGHBORHOOD" | "DISTRICT" | undefined;
|
||||
description?: string | undefined;
|
||||
isPublic?: boolean | undefined;
|
||||
color?: string | undefined;
|
||||
opacity?: number | undefined;
|
||||
isPublic?: boolean | undefined;
|
||||
isOfficial?: boolean | undefined;
|
||||
bounds?: string | undefined;
|
||||
showLocations?: boolean | undefined;
|
||||
@@ -68,9 +68,9 @@ export declare const updateCutSchema: z.ZodObject<{
|
||||
name?: string | undefined;
|
||||
category?: "CUSTOM" | "WARD" | "NEIGHBORHOOD" | "DISTRICT" | null | undefined;
|
||||
description?: string | null | undefined;
|
||||
isPublic?: boolean | undefined;
|
||||
color?: string | undefined;
|
||||
opacity?: number | undefined;
|
||||
isPublic?: boolean | undefined;
|
||||
isOfficial?: boolean | undefined;
|
||||
geojson?: string | undefined;
|
||||
bounds?: string | null | undefined;
|
||||
@@ -83,9 +83,9 @@ export declare const updateCutSchema: z.ZodObject<{
|
||||
name?: string | undefined;
|
||||
category?: "CUSTOM" | "WARD" | "NEIGHBORHOOD" | "DISTRICT" | null | undefined;
|
||||
description?: string | null | undefined;
|
||||
isPublic?: boolean | undefined;
|
||||
color?: string | undefined;
|
||||
opacity?: number | undefined;
|
||||
isPublic?: boolean | undefined;
|
||||
isOfficial?: boolean | undefined;
|
||||
geojson?: string | undefined;
|
||||
bounds?: string | null | undefined;
|
||||
@@ -112,8 +112,8 @@ export declare const listCutsSchema: z.ZodObject<{
|
||||
category?: "CUSTOM" | "WARD" | "NEIGHBORHOOD" | "DISTRICT" | undefined;
|
||||
}, {
|
||||
search?: string | undefined;
|
||||
limit?: number | undefined;
|
||||
category?: "CUSTOM" | "WARD" | "NEIGHBORHOOD" | "DISTRICT" | undefined;
|
||||
limit?: number | undefined;
|
||||
page?: number | undefined;
|
||||
}>;
|
||||
export type CreateCutInput = z.infer<typeof createCutSchema>;
|
||||
|
||||
12
api/dist/modules/map/cuts/cuts.service.d.ts
vendored
12
api/dist/modules/map/cuts/cuts.service.d.ts
vendored
@@ -9,10 +9,10 @@ export declare const cutsService: {
|
||||
updatedAt: Date;
|
||||
category: import(".prisma/client").$Enums.CutCategory | null;
|
||||
description: string | null;
|
||||
isPublic: boolean;
|
||||
createdByUserId: string | null;
|
||||
color: string;
|
||||
opacity: Prisma.Decimal;
|
||||
isPublic: boolean;
|
||||
isOfficial: boolean;
|
||||
geojson: string;
|
||||
bounds: string | null;
|
||||
@@ -37,10 +37,10 @@ export declare const cutsService: {
|
||||
updatedAt: Date;
|
||||
category: import(".prisma/client").$Enums.CutCategory | null;
|
||||
description: string | null;
|
||||
isPublic: boolean;
|
||||
createdByUserId: string | null;
|
||||
color: string;
|
||||
opacity: Prisma.Decimal;
|
||||
isPublic: boolean;
|
||||
isOfficial: boolean;
|
||||
geojson: string;
|
||||
bounds: string | null;
|
||||
@@ -58,10 +58,10 @@ export declare const cutsService: {
|
||||
updatedAt: Date;
|
||||
category: import(".prisma/client").$Enums.CutCategory | null;
|
||||
description: string | null;
|
||||
isPublic: boolean;
|
||||
createdByUserId: string | null;
|
||||
color: string;
|
||||
opacity: Prisma.Decimal;
|
||||
isPublic: boolean;
|
||||
isOfficial: boolean;
|
||||
geojson: string;
|
||||
bounds: string | null;
|
||||
@@ -79,10 +79,10 @@ export declare const cutsService: {
|
||||
updatedAt: Date;
|
||||
category: import(".prisma/client").$Enums.CutCategory | null;
|
||||
description: string | null;
|
||||
isPublic: boolean;
|
||||
createdByUserId: string | null;
|
||||
color: string;
|
||||
opacity: Prisma.Decimal;
|
||||
isPublic: boolean;
|
||||
isOfficial: boolean;
|
||||
geojson: string;
|
||||
bounds: string | null;
|
||||
@@ -110,8 +110,9 @@ export declare const cutsService: {
|
||||
updatedAt: Date;
|
||||
latitude: Prisma.Decimal;
|
||||
longitude: Prisma.Decimal;
|
||||
address: string;
|
||||
createdByUserId: string | null;
|
||||
updatedByUserId: string | null;
|
||||
address: string;
|
||||
postalCode: string | null;
|
||||
province: string | null;
|
||||
federalDistrict: string | null;
|
||||
@@ -122,7 +123,6 @@ export declare const cutsService: {
|
||||
buildingNotes: string | null;
|
||||
geocodeConfidence: number | null;
|
||||
geocodeProvider: import(".prisma/client").$Enums.GeocodeProvider | null;
|
||||
updatedByUserId: string | null;
|
||||
}[]>;
|
||||
exportSingleGeoJson(id: string): Promise<{
|
||||
type: "Feature";
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"geocoding.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/geocoding/geocoding.routes.ts"],"names":[],"mappings":"AAeA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAmBxB,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,CAAC"}
|
||||
{"version":3,"file":"geocoding.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/geocoding/geocoding.routes.ts"],"names":[],"mappings":"AAaA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAmBxB,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,CAAC"}
|
||||
@@ -3,12 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.geocodingRouter = void 0;
|
||||
const express_1 = require("express");
|
||||
const zod_1 = require("zod");
|
||||
const client_1 = require("@prisma/client");
|
||||
const geocoding_service_1 = require("./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 MAP_ADMIN_ROLES = [client_1.UserRole.SUPER_ADMIN, client_1.UserRole.MAP_ADMIN];
|
||||
const roles_1 = require("../../../utils/roles");
|
||||
const searchSchema = zod_1.z.object({
|
||||
q: zod_1.z.string().min(2, 'Query must be at least 2 characters'),
|
||||
limit: zod_1.z.coerce.number().int().min(1).max(10).default(5),
|
||||
@@ -16,7 +15,7 @@ const searchSchema = zod_1.z.object({
|
||||
const router = (0, express_1.Router)();
|
||||
exports.geocodingRouter = router;
|
||||
router.use(auth_middleware_1.authenticate);
|
||||
router.use((0, rbac_middleware_1.requireRole)(...MAP_ADMIN_ROLES));
|
||||
router.use((0, rbac_middleware_1.requireRole)(...roles_1.MAP_ROLES));
|
||||
// GET /api/map/geocoding/search?q=Ottawa&limit=5
|
||||
router.get('/search', (0, validate_1.validate)(searchSchema, 'query'), async (req, res, next) => {
|
||||
try {
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"geocoding.routes.js","sourceRoot":"","sources":["../../../../src/modules/map/geocoding/geocoding.routes.ts"],"names":[],"mappings":";;;AAAA,qCAAkE;AAClE,6BAAwB;AACxB,2CAA0C;AAC1C,2DAAuD;AACvD,2DAAwD;AACxD,yEAAmE;AACnE,yEAAkE;AAElE,MAAM,eAAe,GAAe,CAAC,iBAAQ,CAAC,WAAW,EAAE,iBAAQ,CAAC,SAAS,CAAC,CAAC;AAE/E,MAAM,YAAY,GAAG,OAAC,CAAC,MAAM,CAAC;IAC5B,CAAC,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,qCAAqC,CAAC;IAC3D,KAAK,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;CACzD,CAAC,CAAC;AAEH,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAmBL,iCAAe;AAlBlC,MAAM,CAAC,GAAG,CAAC,8BAAY,CAAC,CAAC;AACzB,MAAM,CAAC,GAAG,CAAC,IAAA,6BAAW,EAAC,GAAG,eAAe,CAAC,CAAC,CAAC;AAE5C,iDAAiD;AACjD,MAAM,CAAC,GAAG,CACR,SAAS,EACT,IAAA,mBAAQ,EAAC,YAAY,EAAE,OAAO,CAAC,EAC/B,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IACxD,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,KAAgD,CAAC;QAC1E,MAAM,OAAO,GAAG,MAAM,oCAAgB,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QACxD,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,CAAC;AACH,CAAC,CACF,CAAC"}
|
||||
{"version":3,"file":"geocoding.routes.js","sourceRoot":"","sources":["../../../../src/modules/map/geocoding/geocoding.routes.ts"],"names":[],"mappings":";;;AAAA,qCAAkE;AAClE,6BAAwB;AACxB,2DAAuD;AACvD,2DAAwD;AACxD,yEAAmE;AACnE,yEAAkE;AAClE,gDAAiD;AAEjD,MAAM,YAAY,GAAG,OAAC,CAAC,MAAM,CAAC;IAC5B,CAAC,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,qCAAqC,CAAC;IAC3D,KAAK,EAAE,OAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;CACzD,CAAC,CAAC;AAEH,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAmBL,iCAAe;AAlBlC,MAAM,CAAC,GAAG,CAAC,8BAAY,CAAC,CAAC;AACzB,MAAM,CAAC,GAAG,CAAC,IAAA,6BAAW,EAAC,GAAG,iBAAS,CAAC,CAAC,CAAC;AAEtC,iDAAiD;AACjD,MAAM,CAAC,GAAG,CACR,SAAS,EACT,IAAA,mBAAQ,EAAC,YAAY,EAAE,OAAO,CAAC,EAC/B,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IACxD,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,KAAgD,CAAC;QAC1E,MAAM,OAAO,GAAG,MAAM,oCAAgB,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QACxD,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,CAAC;AACH,CAAC,CACF,CAAC"}
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"bulk-geocode.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/locations/bulk-geocode.routes.ts"],"names":[],"mappings":"AAUA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAsCxB,OAAO,EAAE,MAAM,IAAI,iBAAiB,EAAE,CAAC"}
|
||||
{"version":3,"file":"bulk-geocode.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/locations/bulk-geocode.routes.ts"],"names":[],"mappings":"AAQA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAsCxB,OAAO,EAAE,MAAM,IAAI,iBAAiB,EAAE,CAAC"}
|
||||
@@ -2,17 +2,16 @@
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.bulkGeocodeRouter = void 0;
|
||||
const express_1 = require("express");
|
||||
const client_1 = require("@prisma/client");
|
||||
const geocode_queue_service_1 = require("../../../services/geocode-queue.service");
|
||||
const bulk_geocode_schemas_1 = require("./bulk-geocode.schemas");
|
||||
const validate_1 = require("../../../middleware/validate");
|
||||
const auth_middleware_1 = require("../../../middleware/auth.middleware");
|
||||
const rbac_middleware_1 = require("../../../middleware/rbac.middleware");
|
||||
const MAP_ADMIN_ROLES = [client_1.UserRole.SUPER_ADMIN, client_1.UserRole.MAP_ADMIN];
|
||||
const roles_1 = require("../../../utils/roles");
|
||||
const router = (0, express_1.Router)();
|
||||
exports.bulkGeocodeRouter = router;
|
||||
router.use(auth_middleware_1.authenticate);
|
||||
router.use((0, rbac_middleware_1.requireRole)(...MAP_ADMIN_ROLES));
|
||||
router.use((0, rbac_middleware_1.requireRole)(...roles_1.MAP_ROLES));
|
||||
// POST /api/map/locations/bulk-geocode — start bulk geocoding job
|
||||
router.post('/', (0, validate_1.validate)(bulk_geocode_schemas_1.bulkGeocodeSchema), async (req, res, next) => {
|
||||
try {
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"bulk-geocode.routes.js","sourceRoot":"","sources":["../../../../src/modules/map/locations/bulk-geocode.routes.ts"],"names":[],"mappings":";;;AAAA,qCAAkE;AAClE,2CAA0C;AAC1C,mFAA8E;AAC9E,iEAA2D;AAC3D,2DAAwD;AACxD,yEAAmE;AACnE,yEAAkE;AAElE,MAAM,eAAe,GAAe,CAAC,iBAAQ,CAAC,WAAW,EAAE,iBAAQ,CAAC,SAAS,CAAC,CAAC;AAE/E,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAsCL,mCAAiB;AArCpC,MAAM,CAAC,GAAG,CAAC,8BAAY,CAAC,CAAC;AACzB,MAAM,CAAC,GAAG,CAAC,IAAA,6BAAW,EAAC,GAAG,eAAe,CAAC,CAAC,CAAC;AAE5C,kEAAkE;AAClE,MAAM,CAAC,IAAI,CACT,GAAG,EACH,IAAA,mBAAQ,EAAC,wCAAiB,CAAC,EAC3B,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IACxD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,2CAAmB,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAK,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;QACjF,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,4BAA4B,EAAE,CAAC,CAAC;IAC7D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,8DAA8D;AAC9D,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IAC9E,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,2CAAmB,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,KAAe,CAAC,CAAC;QAClF,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACnB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,+DAA+D;AAC/D,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAa,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IAC9E,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,2CAAmB,CAAC,QAAQ,EAAE,CAAC;QACnD,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,CAAC;AACH,CAAC,CAAC,CAAC"}
|
||||
{"version":3,"file":"bulk-geocode.routes.js","sourceRoot":"","sources":["../../../../src/modules/map/locations/bulk-geocode.routes.ts"],"names":[],"mappings":";;;AAAA,qCAAkE;AAClE,mFAA8E;AAC9E,iEAA2D;AAC3D,2DAAwD;AACxD,yEAAmE;AACnE,yEAAkE;AAClE,gDAAiD;AAEjD,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAsCL,mCAAiB;AArCpC,MAAM,CAAC,GAAG,CAAC,8BAAY,CAAC,CAAC;AACzB,MAAM,CAAC,GAAG,CAAC,IAAA,6BAAW,EAAC,GAAG,iBAAS,CAAC,CAAC,CAAC;AAEtC,kEAAkE;AAClE,MAAM,CAAC,IAAI,CACT,GAAG,EACH,IAAA,mBAAQ,EAAC,wCAAiB,CAAC,EAC3B,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IACxD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,2CAAmB,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAK,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;QACjF,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,4BAA4B,EAAE,CAAC,CAAC;IAC7D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,8DAA8D;AAC9D,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IAC9E,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,2CAAmB,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,KAAe,CAAC,CAAC;QAClF,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACnB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,+DAA+D;AAC/D,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAa,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IAC9E,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,2CAAmB,CAAC,QAAQ,EAAE,CAAC;QACnD,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,CAAC;AACH,CAAC,CAAC,CAAC"}
|
||||
@@ -6,13 +6,13 @@ export declare const bulkGeocodeSchema: z.ZodObject<{
|
||||
limit: z.ZodOptional<z.ZodNumber>;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
limit?: number | undefined;
|
||||
buildingType?: "SINGLE_FAMILY" | "MULTI_UNIT" | "MIXED_USE" | "COMMERCIAL" | undefined;
|
||||
cutId?: string | undefined;
|
||||
buildingType?: "SINGLE_FAMILY" | "MULTI_UNIT" | "MIXED_USE" | "COMMERCIAL" | undefined;
|
||||
confidenceThreshold?: number | undefined;
|
||||
}, {
|
||||
limit?: number | undefined;
|
||||
buildingType?: "SINGLE_FAMILY" | "MULTI_UNIT" | "MIXED_USE" | "COMMERCIAL" | undefined;
|
||||
cutId?: string | undefined;
|
||||
buildingType?: "SINGLE_FAMILY" | "MULTI_UNIT" | "MIXED_USE" | "COMMERCIAL" | undefined;
|
||||
confidenceThreshold?: number | undefined;
|
||||
}>;
|
||||
export type BulkGeocodeInput = z.infer<typeof bulkGeocodeSchema>;
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"locations.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/locations/locations.routes.ts"],"names":[],"mappings":"AAiDA,QAAA,MAAM,WAAW,4CAAW,CAAC;AA4R7B,QAAA,MAAM,YAAY,4CAAW,CAAC;AA4B9B,OAAO,EAAE,WAAW,IAAI,oBAAoB,EAAE,YAAY,IAAI,qBAAqB,EAAE,CAAC"}
|
||||
{"version":3,"file":"locations.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/locations/locations.routes.ts"],"names":[],"mappings":"AA+CA,QAAA,MAAM,WAAW,4CAAW,CAAC;AA4R7B,QAAA,MAAM,YAAY,4CAAW,CAAC;AA4B9B,OAAO,EAAE,WAAW,IAAI,oBAAoB,EAAE,YAAY,IAAI,qBAAqB,EAAE,CAAC"}
|
||||
@@ -5,7 +5,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.locationsPublicRouter = exports.locationsAdminRouter = void 0;
|
||||
const express_1 = require("express");
|
||||
const client_1 = require("@prisma/client");
|
||||
const multer_1 = __importDefault(require("multer"));
|
||||
const locations_service_1 = require("./locations.service");
|
||||
const locations_schemas_1 = require("./locations.schemas");
|
||||
@@ -14,7 +13,7 @@ const database_1 = require("../../../config/database");
|
||||
const validate_1 = require("../../../middleware/validate");
|
||||
const auth_middleware_1 = require("../../../middleware/auth.middleware");
|
||||
const rbac_middleware_1 = require("../../../middleware/rbac.middleware");
|
||||
const MAP_ADMIN_ROLES = [client_1.UserRole.SUPER_ADMIN, client_1.UserRole.MAP_ADMIN];
|
||||
const roles_1 = require("../../../utils/roles");
|
||||
// Multer config for CSV upload (memory storage, 10MB limit)
|
||||
const upload = (0, multer_1.default)({
|
||||
storage: multer_1.default.memoryStorage(),
|
||||
@@ -45,7 +44,7 @@ const bulkUpload = (0, multer_1.default)({
|
||||
const adminRouter = (0, express_1.Router)();
|
||||
exports.locationsAdminRouter = adminRouter;
|
||||
adminRouter.use(auth_middleware_1.authenticate);
|
||||
adminRouter.use((0, rbac_middleware_1.requireRole)(...MAP_ADMIN_ROLES));
|
||||
adminRouter.use((0, rbac_middleware_1.requireRole)(...roles_1.MAP_ROLES));
|
||||
// GET /api/map/locations — list with pagination + filters
|
||||
adminRouter.get('/', (0, validate_1.validate)(locations_schemas_1.listLocationsSchema, 'query'), async (req, res, next) => {
|
||||
try {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -25,27 +25,27 @@ export declare const createLocationSchema: z.ZodObject<{
|
||||
phone?: string | undefined;
|
||||
latitude?: number | undefined;
|
||||
longitude?: number | undefined;
|
||||
buildingNotes?: string | undefined;
|
||||
unitNumber?: string | undefined;
|
||||
notes?: string | undefined;
|
||||
firstName?: string | undefined;
|
||||
lastName?: string | undefined;
|
||||
supportLevel?: "LEVEL_1" | "LEVEL_2" | "LEVEL_3" | "LEVEL_4" | undefined;
|
||||
unitNumber?: string | undefined;
|
||||
buildingNotes?: string | undefined;
|
||||
signSize?: string | undefined;
|
||||
notes?: string | undefined;
|
||||
}, {
|
||||
address: string;
|
||||
email?: string | undefined;
|
||||
phone?: string | undefined;
|
||||
latitude?: number | undefined;
|
||||
longitude?: number | undefined;
|
||||
buildingNotes?: string | undefined;
|
||||
unitNumber?: string | undefined;
|
||||
notes?: string | undefined;
|
||||
firstName?: string | undefined;
|
||||
lastName?: string | undefined;
|
||||
supportLevel?: "LEVEL_1" | "LEVEL_2" | "LEVEL_3" | "LEVEL_4" | undefined;
|
||||
unitNumber?: string | undefined;
|
||||
buildingNotes?: string | undefined;
|
||||
sign?: boolean | undefined;
|
||||
signSize?: string | undefined;
|
||||
notes?: string | undefined;
|
||||
}>;
|
||||
export declare const updateLocationSchema: z.ZodObject<{
|
||||
address: z.ZodOptional<z.ZodString>;
|
||||
@@ -79,35 +79,35 @@ export declare const updateLocationSchema: z.ZodObject<{
|
||||
phone?: string | null | undefined;
|
||||
latitude?: number | null | undefined;
|
||||
longitude?: number | null | undefined;
|
||||
notes?: string | null | undefined;
|
||||
firstName?: string | null | undefined;
|
||||
lastName?: string | null | undefined;
|
||||
supportLevel?: "LEVEL_1" | "LEVEL_2" | "LEVEL_3" | "LEVEL_4" | null | undefined;
|
||||
address?: string | undefined;
|
||||
unitNumber?: string | null | undefined;
|
||||
postalCode?: string | null | undefined;
|
||||
province?: string | null | undefined;
|
||||
buildingType?: "SINGLE_FAMILY" | "MULTI_UNIT" | "MIXED_USE" | "COMMERCIAL" | null | undefined;
|
||||
buildingNotes?: string | null | undefined;
|
||||
unitNumber?: string | null | undefined;
|
||||
firstName?: string | null | undefined;
|
||||
lastName?: string | null | undefined;
|
||||
supportLevel?: "LEVEL_1" | "LEVEL_2" | "LEVEL_3" | "LEVEL_4" | null | undefined;
|
||||
sign?: boolean | undefined;
|
||||
signSize?: string | null | undefined;
|
||||
notes?: string | null | undefined;
|
||||
}, {
|
||||
email?: string | null | undefined;
|
||||
phone?: string | null | undefined;
|
||||
latitude?: number | null | undefined;
|
||||
longitude?: number | null | undefined;
|
||||
notes?: string | null | undefined;
|
||||
firstName?: string | null | undefined;
|
||||
lastName?: string | null | undefined;
|
||||
supportLevel?: "LEVEL_1" | "LEVEL_2" | "LEVEL_3" | "LEVEL_4" | null | undefined;
|
||||
address?: string | undefined;
|
||||
unitNumber?: string | null | undefined;
|
||||
postalCode?: string | null | undefined;
|
||||
province?: string | null | undefined;
|
||||
buildingType?: "SINGLE_FAMILY" | "MULTI_UNIT" | "MIXED_USE" | "COMMERCIAL" | null | undefined;
|
||||
buildingNotes?: string | null | undefined;
|
||||
unitNumber?: string | null | undefined;
|
||||
firstName?: string | null | undefined;
|
||||
lastName?: string | null | undefined;
|
||||
supportLevel?: "LEVEL_1" | "LEVEL_2" | "LEVEL_3" | "LEVEL_4" | null | undefined;
|
||||
sign?: boolean | undefined;
|
||||
signSize?: string | null | undefined;
|
||||
notes?: string | null | undefined;
|
||||
}>;
|
||||
export declare const listLocationsSchema: z.ZodObject<{
|
||||
page: z.ZodDefault<z.ZodNumber>;
|
||||
@@ -126,21 +126,21 @@ export declare const listLocationsSchema: z.ZodObject<{
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
limit: number;
|
||||
page: number;
|
||||
sortBy: "createdAt" | "address" | "supportLevel";
|
||||
sortBy: "createdAt" | "supportLevel" | "address";
|
||||
sortOrder: "asc" | "desc";
|
||||
search?: string | undefined;
|
||||
supportLevel?: "LEVEL_1" | "LEVEL_2" | "LEVEL_3" | "LEVEL_4" | undefined;
|
||||
hasSign?: boolean | undefined;
|
||||
confidenceLevel?: "high" | "medium" | "low" | "none" | undefined;
|
||||
confidenceLevel?: "none" | "low" | "medium" | "high" | undefined;
|
||||
}, {
|
||||
search?: string | undefined;
|
||||
limit?: number | undefined;
|
||||
page?: number | undefined;
|
||||
sortBy?: "createdAt" | "supportLevel" | "address" | undefined;
|
||||
sortOrder?: "asc" | "desc" | undefined;
|
||||
supportLevel?: "LEVEL_1" | "LEVEL_2" | "LEVEL_3" | "LEVEL_4" | undefined;
|
||||
hasSign?: boolean | undefined;
|
||||
confidenceLevel?: "high" | "medium" | "low" | "none" | undefined;
|
||||
sortBy?: "createdAt" | "address" | "supportLevel" | undefined;
|
||||
sortOrder?: "asc" | "desc" | undefined;
|
||||
confidenceLevel?: "none" | "low" | "medium" | "high" | undefined;
|
||||
}>;
|
||||
export declare const locationIdSchema: z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
@@ -185,7 +185,7 @@ export declare const bulkImportSchema: z.ZodObject<{
|
||||
batchSize: z.ZodDefault<z.ZodNumber>;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
format: "standard" | "nar";
|
||||
filterType: "city" | "cut" | "province" | "none" | "mapArea";
|
||||
filterType: "none" | "city" | "cut" | "province" | "mapArea";
|
||||
residentialOnly: boolean;
|
||||
deduplicateRadius: number;
|
||||
skipGeocoding: boolean;
|
||||
@@ -194,9 +194,9 @@ export declare const bulkImportSchema: z.ZodObject<{
|
||||
filterCity?: string | undefined;
|
||||
filterProvince?: string | undefined;
|
||||
}, {
|
||||
cutId?: string | undefined;
|
||||
format?: "standard" | "nar" | undefined;
|
||||
filterType?: "city" | "cut" | "province" | "none" | "mapArea" | undefined;
|
||||
cutId?: string | undefined;
|
||||
filterType?: "none" | "city" | "cut" | "province" | "mapArea" | undefined;
|
||||
filterCity?: string | undefined;
|
||||
filterProvince?: string | undefined;
|
||||
residentialOnly?: boolean | undefined;
|
||||
|
||||
@@ -16,10 +16,10 @@ export declare const locationsService: {
|
||||
locations: ({
|
||||
addresses: {
|
||||
id: string;
|
||||
unitNumber: string | null;
|
||||
firstName: string | null;
|
||||
lastName: string | null;
|
||||
supportLevel: import(".prisma/client").$Enums.SupportLevel | null;
|
||||
unitNumber: string | null;
|
||||
sign: boolean;
|
||||
}[];
|
||||
} & {
|
||||
@@ -28,8 +28,9 @@ export declare const locationsService: {
|
||||
updatedAt: Date;
|
||||
latitude: Prisma.Decimal;
|
||||
longitude: Prisma.Decimal;
|
||||
address: string;
|
||||
createdByUserId: string | null;
|
||||
updatedByUserId: string | null;
|
||||
address: string;
|
||||
postalCode: string | null;
|
||||
province: string | null;
|
||||
federalDistrict: string | null;
|
||||
@@ -40,7 +41,6 @@ export declare const locationsService: {
|
||||
buildingNotes: string | null;
|
||||
geocodeConfidence: number | null;
|
||||
geocodeProvider: import(".prisma/client").$Enums.GeocodeProvider | null;
|
||||
updatedByUserId: string | null;
|
||||
})[];
|
||||
pagination: {
|
||||
page: number;
|
||||
@@ -54,13 +54,13 @@ export declare const locationsService: {
|
||||
id: string;
|
||||
email: string | null;
|
||||
phone: string | null;
|
||||
unitNumber: string | null;
|
||||
notes: string | null;
|
||||
firstName: string | null;
|
||||
lastName: string | null;
|
||||
supportLevel: import(".prisma/client").$Enums.SupportLevel | null;
|
||||
unitNumber: string | null;
|
||||
sign: boolean;
|
||||
signSize: string | null;
|
||||
notes: string | null;
|
||||
}[];
|
||||
} & {
|
||||
id: string;
|
||||
@@ -68,8 +68,9 @@ export declare const locationsService: {
|
||||
updatedAt: Date;
|
||||
latitude: Prisma.Decimal;
|
||||
longitude: Prisma.Decimal;
|
||||
address: string;
|
||||
createdByUserId: string | null;
|
||||
updatedByUserId: string | null;
|
||||
address: string;
|
||||
postalCode: string | null;
|
||||
province: string | null;
|
||||
federalDistrict: string | null;
|
||||
@@ -80,16 +81,16 @@ export declare const locationsService: {
|
||||
buildingNotes: string | null;
|
||||
geocodeConfidence: number | null;
|
||||
geocodeProvider: import(".prisma/client").$Enums.GeocodeProvider | null;
|
||||
updatedByUserId: string | null;
|
||||
}>;
|
||||
create(data: CreateLocationInput, userId: string): Promise<{
|
||||
create(data: CreateLocationInput, userId: string | null): Promise<{
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
latitude: Prisma.Decimal;
|
||||
longitude: Prisma.Decimal;
|
||||
address: string;
|
||||
createdByUserId: string | null;
|
||||
updatedByUserId: string | null;
|
||||
address: string;
|
||||
postalCode: string | null;
|
||||
province: string | null;
|
||||
federalDistrict: string | null;
|
||||
@@ -100,7 +101,6 @@ export declare const locationsService: {
|
||||
buildingNotes: string | null;
|
||||
geocodeConfidence: number | null;
|
||||
geocodeProvider: import(".prisma/client").$Enums.GeocodeProvider | null;
|
||||
updatedByUserId: string | null;
|
||||
}>;
|
||||
update(id: string, data: UpdateLocationInput, userId: string): Promise<{
|
||||
id: string;
|
||||
@@ -108,8 +108,9 @@ export declare const locationsService: {
|
||||
updatedAt: Date;
|
||||
latitude: Prisma.Decimal;
|
||||
longitude: Prisma.Decimal;
|
||||
address: string;
|
||||
createdByUserId: string | null;
|
||||
updatedByUserId: string | null;
|
||||
address: string;
|
||||
postalCode: string | null;
|
||||
province: string | null;
|
||||
federalDistrict: string | null;
|
||||
@@ -120,7 +121,6 @@ export declare const locationsService: {
|
||||
buildingNotes: string | null;
|
||||
geocodeConfidence: number | null;
|
||||
geocodeProvider: import(".prisma/client").$Enums.GeocodeProvider | null;
|
||||
updatedByUserId: string | null;
|
||||
}>;
|
||||
delete(id: string): Promise<void>;
|
||||
bulkDelete(ids: string[]): Promise<{
|
||||
@@ -139,11 +139,11 @@ export declare const locationsService: {
|
||||
createdAt: Date;
|
||||
userId: string | null;
|
||||
action: import(".prisma/client").$Enums.LocationHistoryAction;
|
||||
metadata: Prisma.JsonValue | null;
|
||||
locationId: string;
|
||||
field: string | null;
|
||||
oldValue: string | null;
|
||||
newValue: string | null;
|
||||
metadata: Prisma.JsonValue | null;
|
||||
})[];
|
||||
pagination: {
|
||||
page: number;
|
||||
@@ -195,13 +195,13 @@ export declare const locationsService: {
|
||||
id: string;
|
||||
email: string | null;
|
||||
phone: string | null;
|
||||
unitNumber: string | null;
|
||||
notes: string | null;
|
||||
firstName: string | null;
|
||||
lastName: string | null;
|
||||
supportLevel: import(".prisma/client").$Enums.SupportLevel | null;
|
||||
unitNumber: string | null;
|
||||
sign: boolean;
|
||||
signSize: string | null;
|
||||
notes: string | null;
|
||||
}[];
|
||||
} & {
|
||||
id: string;
|
||||
@@ -209,8 +209,9 @@ export declare const locationsService: {
|
||||
updatedAt: Date;
|
||||
latitude: Prisma.Decimal;
|
||||
longitude: Prisma.Decimal;
|
||||
address: string;
|
||||
createdByUserId: string | null;
|
||||
updatedByUserId: string | null;
|
||||
address: string;
|
||||
postalCode: string | null;
|
||||
province: string | null;
|
||||
federalDistrict: string | null;
|
||||
@@ -221,7 +222,6 @@ export declare const locationsService: {
|
||||
buildingNotes: string | null;
|
||||
geocodeConfidence: number | null;
|
||||
geocodeProvider: import(".prisma/client").$Enums.GeocodeProvider | null;
|
||||
updatedByUserId: string | null;
|
||||
})[]>;
|
||||
getPublicLocations(bounds?: {
|
||||
minLat: number;
|
||||
@@ -232,14 +232,14 @@ export declare const locationsService: {
|
||||
id: string;
|
||||
latitude: Prisma.Decimal;
|
||||
longitude: Prisma.Decimal;
|
||||
address: string;
|
||||
addresses: {
|
||||
id: string;
|
||||
unitNumber: string | null;
|
||||
supportLevel: import(".prisma/client").$Enums.SupportLevel | null;
|
||||
unitNumber: string | null;
|
||||
sign: boolean;
|
||||
signSize: string | null;
|
||||
}[];
|
||||
address: string;
|
||||
}[]>;
|
||||
importFromCsv(buffer: Buffer, userId: string): Promise<{
|
||||
total: number;
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"locations.service.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/locations/locations.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,YAAY,EAA0C,MAAM,gBAAgB,CAAC;AAU9F,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAKzH,UAAU,aAAa;IACrB,UAAU,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC1B,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAkLD,eAAO,MAAM,gBAAgB;qBACJ,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA0FtB,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA0BN,mBAAmB,UAAU,MAAM;;;;;;;;;;;;;;;;;;;;eA2ErC,MAAM,QAAQ,mBAAmB,UAAU,MAAM;;;;;;;;;;;;;;;;;;;;eA2HjD,MAAM;oBAQD,MAAM,EAAE;;;2BAOD,MAAM,SAAQ,MAAM,UAAa,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;4BA4BtC,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAwIR;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,UAAS,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCA4C3E;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;;;;;;;;;;;;;0BA0CxE,MAAM,UAAU,MAAM;;;;;;;6BA4GnB,MAAM,aAAa,MAAM;uBAS9C,MAAM,UACN,MAAM,WACL,eAAe,YACd,aAAa;;;;;;;;0BAgUG;QAAE,YAAY,CAAC,EAAE,YAAY,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE;CAiD/E,CAAC"}
|
||||
{"version":3,"file":"locations.service.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/locations/locations.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,YAAY,EAA0C,MAAM,gBAAgB,CAAC;AAW9F,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAKzH,UAAU,aAAa;IACrB,UAAU,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC1B,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAkLD,eAAO,MAAM,gBAAgB;qBACJ,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA0FtB,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA0BN,mBAAmB,UAAU,MAAM,GAAG,IAAI;;;;;;;;;;;;;;;;;;;;eA2E5C,MAAM,QAAQ,mBAAmB,UAAU,MAAM;;;;;;;;;;;;;;;;;;;;eA2HjD,MAAM;oBAQD,MAAM,EAAE;;;2BAOD,MAAM,SAAQ,MAAM,UAAa,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;4BA4BtC,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAwIR;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,UAAS,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCA4C3E;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;;;;;;;;;;;;;0BA2DxE,MAAM,UAAU,MAAM;;;;;;;6BA4GnB,MAAM,aAAa,MAAM;uBAS9C,MAAM,UACN,MAAM,WACL,eAAe,YACd,aAAa;;;;;;;;0BAgUG;QAAE,YAAY,CAAC,EAAE,YAAY,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE;CAyD/E,CAAC"}
|
||||
@@ -14,6 +14,7 @@ const geocoding_service_1 = require("../geocoding/geocoding.service");
|
||||
const logger_1 = require("../../../utils/logger");
|
||||
const metrics_1 = require("../../../utils/metrics");
|
||||
const spatial_1 = require("../../../utils/spatial");
|
||||
const settings_service_1 = require("../settings/settings.service");
|
||||
// Statistics Canada Lambert Conformal Conic projection (EPSG:3347) → WGS84 (EPSG:4326)
|
||||
proj4_1.default.defs('EPSG:3347', '+proj=lcc +lat_1=49 +lat_2=77 +lat_0=63.390675 +lon_0=-91.86666666666666 +x_0=6200000 +y_0=3000000 +ellps=GRS80 +units=m +no_defs');
|
||||
// Map CSV header names to our fields (case-insensitive, flexible)
|
||||
@@ -685,6 +686,21 @@ exports.locationsService = {
|
||||
},
|
||||
take: 5000, // Safety limit
|
||||
});
|
||||
// Server-side enforcement: strip sensitive fields based on map visibility settings
|
||||
const mapSettings = await settings_service_1.mapSettingsService.get();
|
||||
if (!mapSettings.publicShowSupportLevels || !mapSettings.publicShowSignInfo) {
|
||||
for (const loc of locations) {
|
||||
for (const addr of loc.addresses) {
|
||||
if (!mapSettings.publicShowSupportLevels) {
|
||||
addr.supportLevel = null;
|
||||
}
|
||||
if (!mapSettings.publicShowSignInfo) {
|
||||
addr.sign = false;
|
||||
addr.signSize = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const durationSeconds = (Date.now() - startTime) / 1000;
|
||||
(0, metrics_1.recordLocationQuery)('public', !!bounds, locations.length, durationSeconds);
|
||||
return locations;
|
||||
@@ -1099,6 +1115,14 @@ exports.locationsService = {
|
||||
include: { addresses: true },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
});
|
||||
// Sanitize a field value against CSV formula injection.
|
||||
// Spreadsheet apps treat cells starting with =, +, -, @, \t, \r as formulas.
|
||||
// Prefixing with an apostrophe causes Excel/Sheets to treat the value as plain text.
|
||||
function sanitizeCsvField(value) {
|
||||
if (/^[=+\-@\t\r]/.test(value))
|
||||
return `'${value}`;
|
||||
return value;
|
||||
}
|
||||
// Flatten: one row per address
|
||||
const rows = [];
|
||||
for (const loc of locations) {
|
||||
@@ -1109,16 +1133,16 @@ exports.locationsService = {
|
||||
if (filters?.hasSign !== undefined && addr.sign !== filters.hasSign)
|
||||
continue;
|
||||
rows.push({
|
||||
address: loc.address || '',
|
||||
unitNumber: addr.unitNumber || '',
|
||||
firstName: addr.firstName || '',
|
||||
lastName: addr.lastName || '',
|
||||
email: addr.email || '',
|
||||
phone: addr.phone || '',
|
||||
address: sanitizeCsvField(loc.address || ''),
|
||||
unitNumber: sanitizeCsvField(addr.unitNumber || ''),
|
||||
firstName: sanitizeCsvField(addr.firstName || ''),
|
||||
lastName: sanitizeCsvField(addr.lastName || ''),
|
||||
email: sanitizeCsvField(addr.email || ''),
|
||||
phone: sanitizeCsvField(addr.phone || ''),
|
||||
supportLevel: addr.supportLevel || '',
|
||||
sign: addr.sign ? 'Yes' : 'No',
|
||||
signSize: addr.signSize || '',
|
||||
notes: addr.notes || '',
|
||||
notes: sanitizeCsvField(addr.notes || ''),
|
||||
latitude: loc.latitude?.toString() || '',
|
||||
longitude: loc.longitude?.toString() || '',
|
||||
geocodeConfidence: loc.geocodeConfidence?.toString() || '',
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
{"version":3,"file":"nar-import.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/locations/nar-import.routes.ts"],"names":[],"mappings":"AAwBA,QAAA,MAAM,eAAe,4CAAW,CAAC;AAoFjC,OAAO,EAAE,eAAe,EAAE,CAAC"}
|
||||
{"version":3,"file":"nar-import.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/locations/nar-import.routes.ts"],"names":[],"mappings":"AAsBA,QAAA,MAAM,eAAe,4CAAW,CAAC;AAoFjC,OAAO,EAAE,eAAe,EAAE,CAAC"}
|
||||
@@ -2,7 +2,6 @@
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.narImportRouter = void 0;
|
||||
const express_1 = require("express");
|
||||
const client_1 = require("@prisma/client");
|
||||
const zod_1 = require("zod");
|
||||
const crypto_1 = require("crypto");
|
||||
const nar_import_service_1 = require("./nar-import.service");
|
||||
@@ -11,7 +10,7 @@ const logger_1 = require("../../../utils/logger");
|
||||
const auth_middleware_1 = require("../../../middleware/auth.middleware");
|
||||
const rbac_middleware_1 = require("../../../middleware/rbac.middleware");
|
||||
const validate_1 = require("../../../middleware/validate");
|
||||
const MAP_ADMIN_ROLES = [client_1.UserRole.SUPER_ADMIN, client_1.UserRole.MAP_ADMIN];
|
||||
const roles_1 = require("../../../utils/roles");
|
||||
const serverImportSchema = zod_1.z.object({
|
||||
provinceCode: zod_1.z.string().min(1).max(2),
|
||||
filterType: zod_1.z.enum(['none', 'city', 'postalPrefix', 'cut']).default('none'),
|
||||
@@ -25,7 +24,7 @@ const serverImportSchema = zod_1.z.object({
|
||||
const narImportRouter = (0, express_1.Router)();
|
||||
exports.narImportRouter = narImportRouter;
|
||||
narImportRouter.use(auth_middleware_1.authenticate);
|
||||
narImportRouter.use((0, rbac_middleware_1.requireRole)(...MAP_ADMIN_ROLES));
|
||||
narImportRouter.use((0, rbac_middleware_1.requireRole)(...roles_1.MAP_ROLES));
|
||||
// GET /api/map/nar-import/datasets — list available NAR datasets by province
|
||||
narImportRouter.get('/datasets', async (_req, res, next) => {
|
||||
try {
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"nar-import.routes.js","sourceRoot":"","sources":["../../../../src/modules/map/locations/nar-import.routes.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"nar-import.routes.js","sourceRoot":"","sources":["../../../../src/modules/map/locations/nar-import.routes.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"settings.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/settings/settings.routes.ts"],"names":[],"mappings":"AAUA,QAAA,MAAM,MAAM,4CAAW,CAAC;AA+BxB,OAAO,EAAE,MAAM,IAAI,iBAAiB,EAAE,CAAC"}
|
||||
{"version":3,"file":"settings.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/settings/settings.routes.ts"],"names":[],"mappings":"AAQA,QAAA,MAAM,MAAM,4CAAW,CAAC;AA+BxB,OAAO,EAAE,MAAM,IAAI,iBAAiB,EAAE,CAAC"}
|
||||
@@ -2,13 +2,12 @@
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.mapSettingsRouter = void 0;
|
||||
const express_1 = require("express");
|
||||
const client_1 = require("@prisma/client");
|
||||
const settings_service_1 = require("./settings.service");
|
||||
const settings_schemas_1 = require("./settings.schemas");
|
||||
const validate_1 = require("../../../middleware/validate");
|
||||
const auth_middleware_1 = require("../../../middleware/auth.middleware");
|
||||
const rbac_middleware_1 = require("../../../middleware/rbac.middleware");
|
||||
const MAP_ADMIN_ROLES = [client_1.UserRole.SUPER_ADMIN, client_1.UserRole.MAP_ADMIN];
|
||||
const roles_1 = require("../../../utils/roles");
|
||||
const router = (0, express_1.Router)();
|
||||
exports.mapSettingsRouter = router;
|
||||
// GET /api/map/settings — public (needed for map center/zoom)
|
||||
@@ -22,7 +21,7 @@ router.get('/', async (_req, res, next) => {
|
||||
}
|
||||
});
|
||||
// PUT /api/map/settings — admin only
|
||||
router.put('/', auth_middleware_1.authenticate, (0, rbac_middleware_1.requireRole)(...MAP_ADMIN_ROLES), (0, validate_1.validate)(settings_schemas_1.updateMapSettingsSchema), async (req, res, next) => {
|
||||
router.put('/', auth_middleware_1.authenticate, (0, rbac_middleware_1.requireRole)(...roles_1.MAP_ROLES), (0, validate_1.validate)(settings_schemas_1.updateMapSettingsSchema), async (req, res, next) => {
|
||||
try {
|
||||
const settings = await settings_service_1.mapSettingsService.update(req.body);
|
||||
res.json(settings);
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"settings.routes.js","sourceRoot":"","sources":["../../../../src/modules/map/settings/settings.routes.ts"],"names":[],"mappings":";;;AAAA,qCAAkE;AAClE,2CAA0C;AAC1C,yDAAwD;AACxD,yDAA6D;AAC7D,2DAAwD;AACxD,yEAAmE;AACnE,yEAAkE;AAElE,MAAM,eAAe,GAAe,CAAC,iBAAQ,CAAC,WAAW,EAAE,iBAAQ,CAAC,SAAS,CAAC,CAAC;AAE/E,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AA+BL,mCAAiB;AA7BpC,8DAA8D;AAC9D,MAAM,CAAC,GAAG,CACR,GAAG,EACH,KAAK,EAAE,IAAa,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IACzD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,qCAAkB,CAAC,GAAG,EAAE,CAAC;QAChD,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,qCAAqC;AACrC,MAAM,CAAC,GAAG,CACR,GAAG,EACH,8BAAY,EACZ,IAAA,6BAAW,EAAC,GAAG,eAAe,CAAC,EAC/B,IAAA,mBAAQ,EAAC,0CAAuB,CAAC,EACjC,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IACxD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,qCAAkB,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC3D,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,CAAC;AACH,CAAC,CACF,CAAC"}
|
||||
{"version":3,"file":"settings.routes.js","sourceRoot":"","sources":["../../../../src/modules/map/settings/settings.routes.ts"],"names":[],"mappings":";;;AAAA,qCAAkE;AAClE,yDAAwD;AACxD,yDAA6D;AAC7D,2DAAwD;AACxD,yEAAmE;AACnE,yEAAkE;AAClE,gDAAiD;AAEjD,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AA+BL,mCAAiB;AA7BpC,8DAA8D;AAC9D,MAAM,CAAC,GAAG,CACR,GAAG,EACH,KAAK,EAAE,IAAa,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IACzD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,qCAAkB,CAAC,GAAG,EAAE,CAAC;QAChD,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,qCAAqC;AACrC,MAAM,CAAC,GAAG,CACR,GAAG,EACH,8BAAY,EACZ,IAAA,6BAAW,EAAC,GAAG,iBAAS,CAAC,EACzB,IAAA,mBAAQ,EAAC,0CAAuB,CAAC,EACjC,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;IACxD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,qCAAkB,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC3D,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,CAAC;AACH,CAAC,CACF,CAAC"}
|
||||
@@ -12,6 +12,13 @@ export declare const updateMapSettingsSchema: z.ZodObject<{
|
||||
qrCode2Label: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
||||
qrCode3Url: z.ZodUnion<[z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodLiteral<"">]>;
|
||||
qrCode3Label: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
||||
publicMapEnabled: z.ZodOptional<z.ZodBoolean>;
|
||||
publicShowLocations: z.ZodOptional<z.ZodBoolean>;
|
||||
publicShowSupportLevels: z.ZodOptional<z.ZodBoolean>;
|
||||
publicShowCuts: z.ZodOptional<z.ZodBoolean>;
|
||||
publicShowEvents: z.ZodOptional<z.ZodBoolean>;
|
||||
publicShowAddresses: z.ZodOptional<z.ZodBoolean>;
|
||||
publicShowSignInfo: z.ZodOptional<z.ZodBoolean>;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
latitude?: number | undefined;
|
||||
longitude?: number | undefined;
|
||||
@@ -25,6 +32,13 @@ export declare const updateMapSettingsSchema: z.ZodObject<{
|
||||
qrCode2Label?: string | null | undefined;
|
||||
qrCode3Url?: string | null | undefined;
|
||||
qrCode3Label?: string | null | undefined;
|
||||
publicMapEnabled?: boolean | undefined;
|
||||
publicShowLocations?: boolean | undefined;
|
||||
publicShowSupportLevels?: boolean | undefined;
|
||||
publicShowCuts?: boolean | undefined;
|
||||
publicShowEvents?: boolean | undefined;
|
||||
publicShowAddresses?: boolean | undefined;
|
||||
publicShowSignInfo?: boolean | undefined;
|
||||
}, {
|
||||
latitude?: number | undefined;
|
||||
longitude?: number | undefined;
|
||||
@@ -38,6 +52,13 @@ export declare const updateMapSettingsSchema: z.ZodObject<{
|
||||
qrCode2Label?: string | null | undefined;
|
||||
qrCode3Url?: string | null | undefined;
|
||||
qrCode3Label?: string | null | undefined;
|
||||
publicMapEnabled?: boolean | undefined;
|
||||
publicShowLocations?: boolean | undefined;
|
||||
publicShowSupportLevels?: boolean | undefined;
|
||||
publicShowCuts?: boolean | undefined;
|
||||
publicShowEvents?: boolean | undefined;
|
||||
publicShowAddresses?: boolean | undefined;
|
||||
publicShowSignInfo?: boolean | undefined;
|
||||
}>;
|
||||
export type UpdateMapSettingsInput = z.infer<typeof updateMapSettingsSchema>;
|
||||
//# sourceMappingURL=settings.schemas.d.ts.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"settings.schemas.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/settings/settings.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAalC,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC"}
|
||||
{"version":3,"file":"settings.schemas.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/settings/settings.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoBlC,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC"}
|
||||
@@ -15,5 +15,12 @@ exports.updateMapSettingsSchema = zod_1.z.object({
|
||||
qrCode2Label: zod_1.z.string().nullable().optional(),
|
||||
qrCode3Url: zod_1.z.string().url().nullable().optional().or(zod_1.z.literal('')),
|
||||
qrCode3Label: zod_1.z.string().nullable().optional(),
|
||||
publicMapEnabled: zod_1.z.boolean().optional(),
|
||||
publicShowLocations: zod_1.z.boolean().optional(),
|
||||
publicShowSupportLevels: zod_1.z.boolean().optional(),
|
||||
publicShowCuts: zod_1.z.boolean().optional(),
|
||||
publicShowEvents: zod_1.z.boolean().optional(),
|
||||
publicShowAddresses: zod_1.z.boolean().optional(),
|
||||
publicShowSignInfo: zod_1.z.boolean().optional(),
|
||||
});
|
||||
//# sourceMappingURL=settings.schemas.js.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"settings.schemas.js","sourceRoot":"","sources":["../../../../src/modules/map/settings/settings.schemas.ts"],"names":[],"mappings":";;;AAAA,6BAAwB;AAEX,QAAA,uBAAuB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC9C,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IAChD,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IACnD,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IAChD,cAAc,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAChD,iBAAiB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACnD,eAAe,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACjD,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,OAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACpE,YAAY,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC9C,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,OAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACpE,YAAY,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC9C,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,OAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACpE,YAAY,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CAC/C,CAAC,CAAC"}
|
||||
{"version":3,"file":"settings.schemas.js","sourceRoot":"","sources":["../../../../src/modules/map/settings/settings.schemas.ts"],"names":[],"mappings":";;;AAAA,6BAAwB;AAEX,QAAA,uBAAuB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC9C,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IAChD,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IACnD,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IAChD,cAAc,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAChD,iBAAiB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACnD,eAAe,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACjD,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,OAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACpE,YAAY,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC9C,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,OAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACpE,YAAY,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC9C,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,OAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACpE,YAAY,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC9C,gBAAgB,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IACxC,mBAAmB,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC3C,uBAAuB,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC/C,cAAc,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IACtC,gBAAgB,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IACxC,mBAAmB,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAC3C,kBAAkB,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CAC3C,CAAC,CAAC"}
|
||||
@@ -4,9 +4,9 @@ export declare const mapSettingsService: {
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
createdBy: string | null;
|
||||
latitude: import("@prisma/client/runtime/library").Decimal | null;
|
||||
longitude: import("@prisma/client/runtime/library").Decimal | null;
|
||||
createdBy: string | null;
|
||||
zoom: number | null;
|
||||
walkSheetTitle: string | null;
|
||||
walkSheetSubtitle: string | null;
|
||||
@@ -17,14 +17,21 @@ export declare const mapSettingsService: {
|
||||
qrCode2Label: string | null;
|
||||
qrCode3Url: string | null;
|
||||
qrCode3Label: string | null;
|
||||
publicMapEnabled: boolean;
|
||||
publicShowLocations: boolean;
|
||||
publicShowSupportLevels: boolean;
|
||||
publicShowCuts: boolean;
|
||||
publicShowEvents: boolean;
|
||||
publicShowAddresses: boolean;
|
||||
publicShowSignInfo: boolean;
|
||||
}>;
|
||||
update(data: UpdateMapSettingsInput): Promise<{
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
createdBy: string | null;
|
||||
latitude: import("@prisma/client/runtime/library").Decimal | null;
|
||||
longitude: import("@prisma/client/runtime/library").Decimal | null;
|
||||
createdBy: string | null;
|
||||
zoom: number | null;
|
||||
walkSheetTitle: string | null;
|
||||
walkSheetSubtitle: string | null;
|
||||
@@ -35,6 +42,13 @@ export declare const mapSettingsService: {
|
||||
qrCode2Label: string | null;
|
||||
qrCode3Url: string | null;
|
||||
qrCode3Label: string | null;
|
||||
publicMapEnabled: boolean;
|
||||
publicShowLocations: boolean;
|
||||
publicShowSupportLevels: boolean;
|
||||
publicShowCuts: boolean;
|
||||
publicShowEvents: boolean;
|
||||
publicShowAddresses: boolean;
|
||||
publicShowSignInfo: boolean;
|
||||
}>;
|
||||
};
|
||||
//# sourceMappingURL=settings.service.d.ts.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"settings.service.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/settings/settings.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAEjE,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;iBAeV,sBAAsB;;;;;;;;;;;;;;;;;;CAiB1C,CAAC"}
|
||||
{"version":3,"file":"settings.service.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/settings/settings.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAEjE,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;iBAeV,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;CAiB1C,CAAC"}
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"shift-series.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/shifts/shift-series.routes.ts"],"names":[],"mappings":"AAQA,QAAA,MAAM,MAAM,4CAAW,CAAC;AA2DxB,eAAe,MAAM,CAAC"}
|
||||
{"version":3,"file":"shift-series.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/shifts/shift-series.routes.ts"],"names":[],"mappings":"AAQA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAqExB,eAAe,MAAM,CAAC"}
|
||||
@@ -4,12 +4,12 @@ const express_1 = require("express");
|
||||
const auth_middleware_1 = require("../../../middleware/auth.middleware");
|
||||
const rbac_middleware_1 = require("../../../middleware/rbac.middleware");
|
||||
const validate_1 = require("../../../middleware/validate");
|
||||
const client_1 = require("@prisma/client");
|
||||
const shift_series_service_1 = require("./shift-series.service");
|
||||
const shift_series_schemas_1 = require("./shift-series.schemas");
|
||||
const roles_1 = require("../../../utils/roles");
|
||||
const router = (0, express_1.Router)();
|
||||
// All routes require admin role
|
||||
router.use(auth_middleware_1.authenticate, (0, rbac_middleware_1.requireRole)(client_1.UserRole.SUPER_ADMIN, client_1.UserRole.MAP_ADMIN));
|
||||
router.use(auth_middleware_1.authenticate, (0, rbac_middleware_1.requireRole)(...roles_1.SCHEDULING_ROLES));
|
||||
// Create series
|
||||
router.post('/', (0, validate_1.validate)(shift_series_schemas_1.createShiftSeriesSchema), async (req, res, next) => {
|
||||
try {
|
||||
@@ -20,6 +20,16 @@ router.post('/', (0, validate_1.validate)(shift_series_schemas_1.createShiftSeri
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
// Get series shift count
|
||||
router.get('/:id/count', async (req, res, next) => {
|
||||
try {
|
||||
const count = await shift_series_service_1.ShiftSeriesService.getShiftCount(req.params.id);
|
||||
res.json({ count });
|
||||
}
|
||||
catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
// Get series
|
||||
router.get('/:id', async (req, res, next) => {
|
||||
try {
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"shift-series.routes.js","sourceRoot":"","sources":["../../../../src/modules/map/shifts/shift-series.routes.ts"],"names":[],"mappings":";;AAAA,qCAAiC;AACjC,yEAAmE;AACnE,yEAAkE;AAClE,2DAAwD;AACxD,2CAA0C;AAC1C,iEAA4D;AAC5D,iEAA0F;AAE1F,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAExB,gCAAgC;AAChC,MAAM,CAAC,GAAG,CAAC,8BAAY,EAAE,IAAA,6BAAW,EAAC,iBAAQ,CAAC,WAAW,EAAE,iBAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;AAEhF,gBAAgB;AAChB,MAAM,CAAC,IAAI,CACT,GAAG,EACH,IAAA,mBAAQ,EAAC,8CAAuB,CAAC,EACjC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACvB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,yCAAkB,CAAC,YAAY,CAClD,GAAG,CAAC,IAAI,EACR,GAAG,CAAC,IAAI,EAAE,EAAE,CACb,CAAC;QACF,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACnB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,KAAK,CAAC,CAAC;IACd,CAAC;AACH,CAAC,CACF,CAAC;AAEF,aAAa;AACb,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IAC1C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,yCAAkB,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAY,CAAC,CAAC;QAC3E,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,KAAK,CAAC,CAAC;IACd,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,gBAAgB;AAChB,MAAM,CAAC,GAAG,CACR,MAAM,EACN,IAAA,mBAAQ,EAAC,8CAAuB,CAAC,EACjC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACvB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,yCAAkB,CAAC,YAAY,CAClD,GAAG,CAAC,MAAM,CAAC,EAAY,EACvB,GAAG,CAAC,IAAI,CACT,CAAC;QACF,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,KAAK,CAAC,CAAC;IACd,CAAC;AACH,CAAC,CACF,CAAC;AAEF,gBAAgB;AAChB,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IAC7C,IAAI,CAAC;QACH,MAAM,yCAAkB,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,EAAY,CAAC,CAAC;QAC/D,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,KAAK,CAAC,CAAC;IACd,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,kBAAe,MAAM,CAAC"}
|
||||
{"version":3,"file":"shift-series.routes.js","sourceRoot":"","sources":["../../../../src/modules/map/shifts/shift-series.routes.ts"],"names":[],"mappings":"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"}
|
||||
@@ -25,8 +25,8 @@ export declare const createShiftSeriesSchema: z.ZodEffects<z.ZodObject<{
|
||||
maxVolunteers: number;
|
||||
frequency: "DAILY" | "WEEKLY" | "MONTHLY";
|
||||
location?: string | undefined;
|
||||
description?: string | undefined;
|
||||
endDate?: string | undefined;
|
||||
description?: string | undefined;
|
||||
cutId?: string | undefined;
|
||||
daysOfWeek?: number[] | undefined;
|
||||
}, {
|
||||
@@ -37,8 +37,8 @@ export declare const createShiftSeriesSchema: z.ZodEffects<z.ZodObject<{
|
||||
maxVolunteers: number;
|
||||
frequency: "DAILY" | "WEEKLY" | "MONTHLY";
|
||||
location?: string | undefined;
|
||||
description?: string | undefined;
|
||||
endDate?: string | undefined;
|
||||
description?: string | undefined;
|
||||
isPublic?: boolean | undefined;
|
||||
cutId?: string | undefined;
|
||||
daysOfWeek?: number[] | undefined;
|
||||
@@ -51,8 +51,8 @@ export declare const createShiftSeriesSchema: z.ZodEffects<z.ZodObject<{
|
||||
maxVolunteers: number;
|
||||
frequency: "DAILY" | "WEEKLY" | "MONTHLY";
|
||||
location?: string | undefined;
|
||||
description?: string | undefined;
|
||||
endDate?: string | undefined;
|
||||
description?: string | undefined;
|
||||
cutId?: string | undefined;
|
||||
daysOfWeek?: number[] | undefined;
|
||||
}, {
|
||||
@@ -63,8 +63,8 @@ export declare const createShiftSeriesSchema: z.ZodEffects<z.ZodObject<{
|
||||
maxVolunteers: number;
|
||||
frequency: "DAILY" | "WEEKLY" | "MONTHLY";
|
||||
location?: string | undefined;
|
||||
description?: string | undefined;
|
||||
endDate?: string | undefined;
|
||||
description?: string | undefined;
|
||||
isPublic?: boolean | undefined;
|
||||
cutId?: string | undefined;
|
||||
daysOfWeek?: number[] | undefined;
|
||||
|
||||
@@ -14,12 +14,12 @@ export declare class ShiftSeriesService {
|
||||
updatedAt: Date;
|
||||
location: string | null;
|
||||
title: string;
|
||||
description: string | null;
|
||||
startDate: Date;
|
||||
endDate: Date | null;
|
||||
description: string | null;
|
||||
createdBy: string | null;
|
||||
isPublic: boolean;
|
||||
cutId: string | null;
|
||||
createdBy: string | null;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
maxVolunteers: number;
|
||||
@@ -28,43 +28,26 @@ export declare class ShiftSeriesService {
|
||||
};
|
||||
generatedShiftsCount: number;
|
||||
}>;
|
||||
/**
|
||||
* Get count of non-exception shifts in a series
|
||||
*/
|
||||
static getShiftCount(seriesId: string): Promise<number>;
|
||||
/**
|
||||
* Get series with all its shifts
|
||||
*/
|
||||
static getSeries(seriesId: string): Promise<{
|
||||
cut: {
|
||||
id: string;
|
||||
name: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
category: import(".prisma/client").$Enums.CutCategory | null;
|
||||
description: string | null;
|
||||
createdByUserId: string | null;
|
||||
color: string;
|
||||
opacity: import("@prisma/client/runtime/library").Decimal;
|
||||
isPublic: boolean;
|
||||
isOfficial: boolean;
|
||||
geojson: string;
|
||||
bounds: string | null;
|
||||
showLocations: boolean;
|
||||
exportEnabled: boolean;
|
||||
assignedTo: string | null;
|
||||
filterSettings: import("@prisma/client/runtime/library").JsonValue | null;
|
||||
lastCanvassed: Date | null;
|
||||
completionPercentage: number;
|
||||
} | null;
|
||||
shifts: ({
|
||||
signups: {
|
||||
status: import(".prisma/client").$Enums.SignupStatus;
|
||||
id: string;
|
||||
userId: string | null;
|
||||
userEmail: string;
|
||||
shiftId: string;
|
||||
userName: string | null;
|
||||
shiftTitle: string | null;
|
||||
userPhone: string | null;
|
||||
signupDate: Date;
|
||||
signupSource: import(".prisma/client").$Enums.SignupSource;
|
||||
shiftId: string;
|
||||
}[];
|
||||
} & {
|
||||
status: import(".prisma/client").$Enums.ShiftStatus;
|
||||
@@ -75,28 +58,51 @@ export declare class ShiftSeriesService {
|
||||
location: string | null;
|
||||
title: string;
|
||||
description: string | null;
|
||||
createdBy: string | null;
|
||||
isPublic: boolean;
|
||||
cutId: string | null;
|
||||
createdBy: string | null;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
maxVolunteers: number;
|
||||
currentVolunteers: number;
|
||||
gancioEventId: number | null;
|
||||
meetingId: string | null;
|
||||
seriesId: string | null;
|
||||
isException: boolean;
|
||||
maxVolunteers: number;
|
||||
currentVolunteers: number;
|
||||
})[];
|
||||
cut: {
|
||||
id: string;
|
||||
name: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
category: import(".prisma/client").$Enums.CutCategory | null;
|
||||
description: string | null;
|
||||
isPublic: boolean;
|
||||
createdByUserId: string | null;
|
||||
color: string;
|
||||
opacity: import("@prisma/client/runtime/library").Decimal;
|
||||
isOfficial: boolean;
|
||||
geojson: string;
|
||||
bounds: string | null;
|
||||
showLocations: boolean;
|
||||
exportEnabled: boolean;
|
||||
assignedTo: string | null;
|
||||
filterSettings: import("@prisma/client/runtime/library").JsonValue | null;
|
||||
lastCanvassed: Date | null;
|
||||
completionPercentage: number;
|
||||
} | null;
|
||||
} & {
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
location: string | null;
|
||||
title: string;
|
||||
description: string | null;
|
||||
startDate: Date;
|
||||
endDate: Date | null;
|
||||
description: string | null;
|
||||
createdBy: string | null;
|
||||
isPublic: boolean;
|
||||
cutId: string | null;
|
||||
createdBy: string | null;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
maxVolunteers: number;
|
||||
@@ -107,39 +113,18 @@ export declare class ShiftSeriesService {
|
||||
* Update series shifts based on edit mode
|
||||
*/
|
||||
static updateSeries(seriesId: string, input: UpdateShiftSeriesInput): Promise<{
|
||||
cut: {
|
||||
id: string;
|
||||
name: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
category: import(".prisma/client").$Enums.CutCategory | null;
|
||||
description: string | null;
|
||||
createdByUserId: string | null;
|
||||
color: string;
|
||||
opacity: import("@prisma/client/runtime/library").Decimal;
|
||||
isPublic: boolean;
|
||||
isOfficial: boolean;
|
||||
geojson: string;
|
||||
bounds: string | null;
|
||||
showLocations: boolean;
|
||||
exportEnabled: boolean;
|
||||
assignedTo: string | null;
|
||||
filterSettings: import("@prisma/client/runtime/library").JsonValue | null;
|
||||
lastCanvassed: Date | null;
|
||||
completionPercentage: number;
|
||||
} | null;
|
||||
shifts: ({
|
||||
signups: {
|
||||
status: import(".prisma/client").$Enums.SignupStatus;
|
||||
id: string;
|
||||
userId: string | null;
|
||||
userEmail: string;
|
||||
shiftId: string;
|
||||
userName: string | null;
|
||||
shiftTitle: string | null;
|
||||
userPhone: string | null;
|
||||
signupDate: Date;
|
||||
signupSource: import(".prisma/client").$Enums.SignupSource;
|
||||
shiftId: string;
|
||||
}[];
|
||||
} & {
|
||||
status: import(".prisma/client").$Enums.ShiftStatus;
|
||||
@@ -150,28 +135,51 @@ export declare class ShiftSeriesService {
|
||||
location: string | null;
|
||||
title: string;
|
||||
description: string | null;
|
||||
createdBy: string | null;
|
||||
isPublic: boolean;
|
||||
cutId: string | null;
|
||||
createdBy: string | null;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
maxVolunteers: number;
|
||||
currentVolunteers: number;
|
||||
gancioEventId: number | null;
|
||||
meetingId: string | null;
|
||||
seriesId: string | null;
|
||||
isException: boolean;
|
||||
maxVolunteers: number;
|
||||
currentVolunteers: number;
|
||||
})[];
|
||||
cut: {
|
||||
id: string;
|
||||
name: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
category: import(".prisma/client").$Enums.CutCategory | null;
|
||||
description: string | null;
|
||||
isPublic: boolean;
|
||||
createdByUserId: string | null;
|
||||
color: string;
|
||||
opacity: import("@prisma/client/runtime/library").Decimal;
|
||||
isOfficial: boolean;
|
||||
geojson: string;
|
||||
bounds: string | null;
|
||||
showLocations: boolean;
|
||||
exportEnabled: boolean;
|
||||
assignedTo: string | null;
|
||||
filterSettings: import("@prisma/client/runtime/library").JsonValue | null;
|
||||
lastCanvassed: Date | null;
|
||||
completionPercentage: number;
|
||||
} | null;
|
||||
} & {
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
location: string | null;
|
||||
title: string;
|
||||
description: string | null;
|
||||
startDate: Date;
|
||||
endDate: Date | null;
|
||||
description: string | null;
|
||||
createdBy: string | null;
|
||||
isPublic: boolean;
|
||||
cutId: string | null;
|
||||
createdBy: string | null;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
maxVolunteers: number;
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"shift-series.service.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/shifts/shift-series.service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAK7F,qBAAa,kBAAkB;IAC7B;;OAEG;IACH,MAAM,CAAC,mBAAmB,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI,EAAE;IA8CjE;;OAEG;WACU,YAAY,CAAC,KAAK,EAAE,sBAAsB,EAAE,SAAS,CAAC,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;IA2D3E;;OAEG;WACU,SAAS,CAAC,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAmBvC;;OAEG;WACU,YAAY,CACvB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA0C/B;;OAEG;WACU,YAAY,CAAC,QAAQ,EAAE,MAAM;CAW3C"}
|
||||
{"version":3,"file":"shift-series.service.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/shifts/shift-series.service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAK7F,qBAAa,kBAAkB;IAC7B;;OAEG;IACH,MAAM,CAAC,mBAAmB,CAAC,KAAK,EAAE,sBAAsB,GAAG,IAAI,EAAE;IA8CjE;;OAEG;WACU,YAAY,CAAC,KAAK,EAAE,sBAAsB,EAAE,SAAS,CAAC,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;IA2D3E;;OAEG;WACU,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAM7D;;OAEG;WACU,SAAS,CAAC,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAmBvC;;OAEG;WACU,YAAY,CACvB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA0C/B;;OAEG;WACU,YAAY,CAAC,QAAQ,EAAE,MAAM;CAW3C"}
|
||||
@@ -104,6 +104,14 @@ class ShiftSeriesService {
|
||||
generatedShiftsCount: result.shifts.length,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Get count of non-exception shifts in a series
|
||||
*/
|
||||
static async getShiftCount(seriesId) {
|
||||
return database_1.prisma.shift.count({
|
||||
where: { seriesId, isException: false },
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Get series with all its shifts
|
||||
*/
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
{"version":3,"file":"shifts.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/shifts/shifts.routes.ts"],"names":[],"mappings":"AAkBA,QAAA,MAAM,WAAW,4CAAW,CAAC;AA2J7B,QAAA,MAAM,eAAe,4CAAW,CAAC;AA2DjC,QAAA,MAAM,YAAY,4CAAW,CAAC;AA+B9B,OAAO,EAAE,WAAW,IAAI,iBAAiB,EAAE,YAAY,IAAI,kBAAkB,EAAE,eAAe,IAAI,qBAAqB,EAAE,CAAC"}
|
||||
{"version":3,"file":"shifts.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/shifts/shifts.routes.ts"],"names":[],"mappings":"AAkBA,QAAA,MAAM,WAAW,4CAAW,CAAC;AAuL7B,QAAA,MAAM,eAAe,4CAAW,CAAC;AA2DjC,QAAA,MAAM,YAAY,4CAAW,CAAC;AA0E9B,OAAO,EAAE,WAAW,IAAI,iBAAiB,EAAE,YAAY,IAAI,kBAAkB,EAAE,eAAe,IAAI,qBAAqB,EAAE,CAAC"}
|
||||
72
api/dist/modules/map/shifts/shifts.routes.js
vendored
72
api/dist/modules/map/shifts/shifts.routes.js
vendored
@@ -2,19 +2,20 @@
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.shiftsVolunteerRouter = exports.shiftsPublicRouter = exports.shiftsAdminRouter = void 0;
|
||||
const express_1 = require("express");
|
||||
const client_1 = require("@prisma/client");
|
||||
const shifts_service_1 = require("./shifts.service");
|
||||
const shifts_schemas_1 = require("./shifts.schemas");
|
||||
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 MAP_ADMIN_ROLES = [client_1.UserRole.SUPER_ADMIN, client_1.UserRole.MAP_ADMIN];
|
||||
const database_1 = require("../../../config/database");
|
||||
const redis_1 = require("../../../config/redis");
|
||||
const roles_1 = require("../../../utils/roles");
|
||||
// --- Admin Router ---
|
||||
const adminRouter = (0, express_1.Router)();
|
||||
exports.shiftsAdminRouter = adminRouter;
|
||||
adminRouter.use(auth_middleware_1.authenticate);
|
||||
adminRouter.use((0, rbac_middleware_1.requireRole)(...MAP_ADMIN_ROLES));
|
||||
adminRouter.use((0, rbac_middleware_1.requireRole)(...roles_1.SCHEDULING_ROLES));
|
||||
// GET /api/map/shifts — list paginated
|
||||
adminRouter.get('/', (0, validate_1.validate)(shifts_schemas_1.listShiftsSchema, 'query'), async (req, res, next) => {
|
||||
try {
|
||||
@@ -114,6 +115,28 @@ adminRouter.delete('/:id/signups/:signupId', async (req, res, next) => {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
// POST /api/map/shifts/:id/meeting — create and link a video briefing meeting
|
||||
adminRouter.post('/:id/meeting', async (req, res, next) => {
|
||||
try {
|
||||
const id = req.params.id;
|
||||
const meeting = await shifts_service_1.shiftsService.createMeetingForShift(id, req.user.id);
|
||||
res.status(201).json(meeting);
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
// DELETE /api/map/shifts/:id/meeting — remove video briefing from shift
|
||||
adminRouter.delete('/:id/meeting', async (req, res, next) => {
|
||||
try {
|
||||
const id = req.params.id;
|
||||
await shifts_service_1.shiftsService.removeMeetingFromShift(id);
|
||||
res.status(204).send();
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
// POST /api/map/shifts/:id/email-details — email all volunteers
|
||||
adminRouter.post('/:id/email-details', async (req, res, next) => {
|
||||
try {
|
||||
@@ -195,4 +218,47 @@ publicRouter.post('/public/:id/signup', rate_limit_1.shiftSignupRateLimit, (0, v
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
// GET /api/map/shifts/public/related — related active campaigns
|
||||
publicRouter.get('/public/related', async (_req, res, next) => {
|
||||
try {
|
||||
const cacheKey = 'shifts:related:campaigns';
|
||||
try {
|
||||
const cached = await redis_1.redis.get(cacheKey);
|
||||
if (cached) {
|
||||
res.json(JSON.parse(cached));
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch { /* cache miss */ }
|
||||
const campaigns = await database_1.prisma.campaign.findMany({
|
||||
where: { status: 'ACTIVE' },
|
||||
select: {
|
||||
id: true,
|
||||
slug: true,
|
||||
title: true,
|
||||
description: true,
|
||||
_count: { select: { emails: true } },
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: 3,
|
||||
});
|
||||
const result = {
|
||||
campaigns: campaigns.map(c => ({
|
||||
id: c.id,
|
||||
slug: c.slug,
|
||||
title: c.title,
|
||||
description: c.description?.slice(0, 150) ?? null,
|
||||
emailCount: c._count.emails,
|
||||
})),
|
||||
};
|
||||
try {
|
||||
await redis_1.redis.setex(cacheKey, 300, JSON.stringify(result));
|
||||
}
|
||||
catch { /* non-critical */ }
|
||||
res.json(result);
|
||||
}
|
||||
catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
//# sourceMappingURL=shifts.routes.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -46,7 +46,7 @@ export declare const updateShiftSchema: z.ZodObject<{
|
||||
}>>;
|
||||
cutId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
||||
}, "strip", z.ZodTypeAny, {
|
||||
status?: "OPEN" | "FULL" | "CANCELLED" | undefined;
|
||||
status?: "CANCELLED" | "OPEN" | "FULL" | undefined;
|
||||
date?: string | undefined;
|
||||
location?: string | null | undefined;
|
||||
title?: string | undefined;
|
||||
@@ -57,7 +57,7 @@ export declare const updateShiftSchema: z.ZodObject<{
|
||||
endTime?: string | undefined;
|
||||
maxVolunteers?: number | undefined;
|
||||
}, {
|
||||
status?: "OPEN" | "FULL" | "CANCELLED" | undefined;
|
||||
status?: "CANCELLED" | "OPEN" | "FULL" | undefined;
|
||||
date?: string | undefined;
|
||||
location?: string | null | undefined;
|
||||
title?: string | undefined;
|
||||
@@ -85,11 +85,11 @@ export declare const listShiftsSchema: z.ZodObject<{
|
||||
page: number;
|
||||
sortBy: "date" | "createdAt" | "title";
|
||||
sortOrder: "asc" | "desc";
|
||||
status?: "OPEN" | "FULL" | "CANCELLED" | undefined;
|
||||
status?: "CANCELLED" | "OPEN" | "FULL" | undefined;
|
||||
search?: string | undefined;
|
||||
upcoming?: boolean | undefined;
|
||||
}, {
|
||||
status?: "OPEN" | "FULL" | "CANCELLED" | undefined;
|
||||
status?: "CANCELLED" | "OPEN" | "FULL" | undefined;
|
||||
search?: string | undefined;
|
||||
limit?: number | undefined;
|
||||
page?: number | undefined;
|
||||
|
||||
87
api/dist/modules/map/shifts/shifts.service.d.ts
vendored
87
api/dist/modules/map/shifts/shifts.service.d.ts
vendored
@@ -9,6 +9,13 @@ export declare const shiftsService: {
|
||||
id: string;
|
||||
name: string;
|
||||
} | null;
|
||||
meeting: {
|
||||
id: string;
|
||||
title: string;
|
||||
isActive: boolean;
|
||||
slug: string;
|
||||
jitsiRoom: string;
|
||||
} | null;
|
||||
} & {
|
||||
status: import(".prisma/client").$Enums.ShiftStatus;
|
||||
date: Date;
|
||||
@@ -18,15 +25,17 @@ export declare const shiftsService: {
|
||||
location: string | null;
|
||||
title: string;
|
||||
description: string | null;
|
||||
createdBy: string | null;
|
||||
isPublic: boolean;
|
||||
cutId: string | null;
|
||||
createdBy: string | null;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
maxVolunteers: number;
|
||||
currentVolunteers: number;
|
||||
gancioEventId: number | null;
|
||||
meetingId: string | null;
|
||||
seriesId: string | null;
|
||||
isException: boolean;
|
||||
maxVolunteers: number;
|
||||
currentVolunteers: number;
|
||||
})[];
|
||||
pagination: {
|
||||
page: number;
|
||||
@@ -43,6 +52,13 @@ export declare const shiftsService: {
|
||||
id: string;
|
||||
name: string;
|
||||
} | null;
|
||||
meeting: {
|
||||
id: string;
|
||||
title: string;
|
||||
isActive: boolean;
|
||||
slug: string;
|
||||
jitsiRoom: string;
|
||||
} | null;
|
||||
signups: ({
|
||||
user: {
|
||||
id: string;
|
||||
@@ -55,12 +71,12 @@ export declare const shiftsService: {
|
||||
id: string;
|
||||
userId: string | null;
|
||||
userEmail: string;
|
||||
shiftId: string;
|
||||
userName: string | null;
|
||||
shiftTitle: string | null;
|
||||
userPhone: string | null;
|
||||
signupDate: Date;
|
||||
signupSource: import(".prisma/client").$Enums.SignupSource;
|
||||
shiftId: string;
|
||||
})[];
|
||||
} & {
|
||||
status: import(".prisma/client").$Enums.ShiftStatus;
|
||||
@@ -71,15 +87,17 @@ export declare const shiftsService: {
|
||||
location: string | null;
|
||||
title: string;
|
||||
description: string | null;
|
||||
createdBy: string | null;
|
||||
isPublic: boolean;
|
||||
cutId: string | null;
|
||||
createdBy: string | null;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
maxVolunteers: number;
|
||||
currentVolunteers: number;
|
||||
gancioEventId: number | null;
|
||||
meetingId: string | null;
|
||||
seriesId: string | null;
|
||||
isException: boolean;
|
||||
maxVolunteers: number;
|
||||
currentVolunteers: number;
|
||||
}>;
|
||||
create(data: CreateShiftInput, userId: string): Promise<{
|
||||
status: import(".prisma/client").$Enums.ShiftStatus;
|
||||
@@ -90,15 +108,17 @@ export declare const shiftsService: {
|
||||
location: string | null;
|
||||
title: string;
|
||||
description: string | null;
|
||||
createdBy: string | null;
|
||||
isPublic: boolean;
|
||||
cutId: string | null;
|
||||
createdBy: string | null;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
maxVolunteers: number;
|
||||
currentVolunteers: number;
|
||||
gancioEventId: number | null;
|
||||
meetingId: string | null;
|
||||
seriesId: string | null;
|
||||
isException: boolean;
|
||||
maxVolunteers: number;
|
||||
currentVolunteers: number;
|
||||
}>;
|
||||
update(id: string, data: UpdateShiftInput): Promise<{
|
||||
status: import(".prisma/client").$Enums.ShiftStatus;
|
||||
@@ -109,17 +129,33 @@ export declare const shiftsService: {
|
||||
location: string | null;
|
||||
title: string;
|
||||
description: string | null;
|
||||
createdBy: string | null;
|
||||
isPublic: boolean;
|
||||
cutId: string | null;
|
||||
createdBy: string | null;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
maxVolunteers: number;
|
||||
currentVolunteers: number;
|
||||
gancioEventId: number | null;
|
||||
meetingId: string | null;
|
||||
seriesId: string | null;
|
||||
isException: boolean;
|
||||
maxVolunteers: number;
|
||||
currentVolunteers: number;
|
||||
}>;
|
||||
delete(id: string): Promise<void>;
|
||||
createMeetingForShift(shiftId: string, userId: string): Promise<{
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
title: string;
|
||||
isActive: boolean;
|
||||
description: string | null;
|
||||
slug: string;
|
||||
createdByUserId: string;
|
||||
jitsiRoom: string;
|
||||
startTime: Date | null;
|
||||
endTime: Date | null;
|
||||
}>;
|
||||
removeMeetingFromShift(shiftId: string): Promise<void>;
|
||||
getStats(): Promise<{
|
||||
total: number;
|
||||
open: number;
|
||||
@@ -140,24 +176,24 @@ export declare const shiftsService: {
|
||||
id: string;
|
||||
userId: string | null;
|
||||
userEmail: string;
|
||||
shiftId: string;
|
||||
userName: string | null;
|
||||
shiftTitle: string | null;
|
||||
userPhone: string | null;
|
||||
signupDate: Date;
|
||||
signupSource: import(".prisma/client").$Enums.SignupSource;
|
||||
shiftId: string;
|
||||
})[]>;
|
||||
addSignup(shiftId: string, data: AddSignupInput): Promise<{
|
||||
status: import(".prisma/client").$Enums.SignupStatus;
|
||||
id: string;
|
||||
userId: string | null;
|
||||
userEmail: string;
|
||||
shiftId: string;
|
||||
userName: string | null;
|
||||
shiftTitle: string | null;
|
||||
userPhone: string | null;
|
||||
signupDate: Date;
|
||||
signupSource: import(".prisma/client").$Enums.SignupSource;
|
||||
shiftId: string;
|
||||
}>;
|
||||
removeSignup(signupId: string): Promise<void>;
|
||||
publicSignup(shiftId: string, data: PublicSignupInput): Promise<{
|
||||
@@ -166,12 +202,12 @@ export declare const shiftsService: {
|
||||
id: string;
|
||||
userId: string | null;
|
||||
userEmail: string;
|
||||
shiftId: string;
|
||||
userName: string | null;
|
||||
shiftTitle: string | null;
|
||||
userPhone: string | null;
|
||||
signupDate: Date;
|
||||
signupSource: import(".prisma/client").$Enums.SignupSource;
|
||||
shiftId: string;
|
||||
};
|
||||
isNewUser: boolean;
|
||||
}>;
|
||||
@@ -184,6 +220,11 @@ export declare const shiftsService: {
|
||||
location: string | null;
|
||||
title: string;
|
||||
description: string | null;
|
||||
meeting: {
|
||||
id: string;
|
||||
isActive: boolean;
|
||||
slug: string;
|
||||
} | null;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
maxVolunteers: number;
|
||||
@@ -194,12 +235,12 @@ export declare const shiftsService: {
|
||||
id: string;
|
||||
userId: string | null;
|
||||
userEmail: string;
|
||||
shiftId: string;
|
||||
userName: string | null;
|
||||
shiftTitle: string | null;
|
||||
userPhone: string | null;
|
||||
signupDate: Date;
|
||||
signupSource: import(".prisma/client").$Enums.SignupSource;
|
||||
shiftId: string;
|
||||
}>;
|
||||
cancelVolunteerSignup(shiftId: string, userId: string): Promise<void>;
|
||||
getMySignups(userId: string): Promise<({
|
||||
@@ -210,6 +251,11 @@ export declare const shiftsService: {
|
||||
location: string | null;
|
||||
title: string;
|
||||
description: string | null;
|
||||
meeting: {
|
||||
id: string;
|
||||
isActive: boolean;
|
||||
slug: string;
|
||||
} | null;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
maxVolunteers: number;
|
||||
@@ -220,12 +266,12 @@ export declare const shiftsService: {
|
||||
id: string;
|
||||
userId: string | null;
|
||||
userEmail: string;
|
||||
shiftId: string;
|
||||
userName: string | null;
|
||||
shiftTitle: string | null;
|
||||
userPhone: string | null;
|
||||
signupDate: Date;
|
||||
signupSource: import(".prisma/client").$Enums.SignupSource;
|
||||
shiftId: string;
|
||||
})[]>;
|
||||
getPublicShifts(): Promise<{
|
||||
status: import(".prisma/client").$Enums.ShiftStatus;
|
||||
@@ -234,6 +280,11 @@ export declare const shiftsService: {
|
||||
location: string | null;
|
||||
title: string;
|
||||
description: string | null;
|
||||
meeting: {
|
||||
id: string;
|
||||
isActive: boolean;
|
||||
slug: string;
|
||||
} | null;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
maxVolunteers: number;
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"shifts.service.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/shifts/shifts.service.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,iBAAiB,EAClB,MAAM,kBAAkB,CAAC;AAY1B,eAAO,MAAM,aAAa;qBACD,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuDnB,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyBN,gBAAgB,UAAU,MAAM;;;;;;;;;;;;;;;;;;;eAmBlC,MAAM,QAAQ,gBAAgB;;;;;;;;;;;;;;;;;;;eA6B9B,MAAM;;;;;;;;;wBAsBG,MAAM;;;;;;;;;;;;;;;;;;;uBAaP,MAAM,QAAQ,cAAc;;;;;;;;;;;;2BA6DxB,MAAM;0BAyBP,MAAM,QAAQ,iBAAiB;;;;;;;;;;;;;;;gCAqJzB,MAAM,aAAa,MAAM;oCA4BrB,MAAM;;;;;;;;;;;;;6BA2Cb,MAAM,UAAU,MAAM;;;;;;;;;;;;mCAoGhB,MAAM,UAAU,MAAM;yBA0BhC,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BA4DA,MAAM;;;;+BAsEN,MAAM,WAAW,MAAM;;mBAiBf,MAAM;oBAAU,GAAG,EAAE;;;CAe/D,CAAC"}
|
||||
{"version":3,"file":"shifts.service.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/shifts/shifts.service.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,iBAAiB,EAClB,MAAM,kBAAkB,CAAC;AAoB1B,eAAO,MAAM,aAAa;qBACD,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwDnB,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA0BN,gBAAgB,UAAU,MAAM;;;;;;;;;;;;;;;;;;;;;eA2ClC,MAAM,QAAQ,gBAAgB;;;;;;;;;;;;;;;;;;;;;eA8C9B,MAAM;mCAwBc,MAAM,UAAU,MAAM;;;;;;;;;;;;;oCAyBrB,MAAM;;;;;;;;;wBA4BlB,MAAM;;;;;;;;;;;;;;;;;;;uBAaP,MAAM,QAAQ,cAAc;;;;;;;;;;;;2BA2ExB,MAAM;0BA4BP,MAAM,QAAQ,iBAAiB;;;;;;;;;;;;;;;gCAuQzB,MAAM,aAAa,MAAM;oCAuGrB,MAAM;;;;;;;;;;;;;;;;;;6BA4Cb,MAAM,UAAU,MAAM;;;;;;;;;;;;mCAsLhB,MAAM,UAAU,MAAM;yBAqGhC,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BA8DA,MAAM;;;;+BAuDN,MAAM,WAAW,MAAM;;mBAiBf,MAAM;oBAAU,GAAG,EAAE;;;CAe/D,CAAC"}
|
||||
589
api/dist/modules/map/shifts/shifts.service.js
vendored
589
api/dist/modules/map/shifts/shifts.service.js
vendored
@@ -9,10 +9,21 @@ const client_1 = require("@prisma/client");
|
||||
const database_1 = require("../../../config/database");
|
||||
const error_handler_1 = require("../../../middleware/error-handler");
|
||||
const email_service_1 = require("../../../services/email.service");
|
||||
const settings_service_1 = require("../../settings/settings.service");
|
||||
const notification_queue_service_1 = require("../../../services/notification-queue.service");
|
||||
const notification_helper_1 = require("../../../services/notification.helper");
|
||||
const env_1 = require("../../../config/env");
|
||||
const logger_1 = require("../../../utils/logger");
|
||||
const metrics_1 = require("../../../utils/metrics");
|
||||
const rocketchat_webhook_service_1 = require("../../../services/rocketchat-webhook.service");
|
||||
const listmonk_event_sync_service_1 = require("../../../services/listmonk-event-sync.service");
|
||||
const gancio_client_1 = require("../../../services/gancio.client");
|
||||
const unified_calendar_service_1 = require("../../events/unified-calendar.service");
|
||||
const group_service_1 = require("../../social/group.service");
|
||||
const achievements_service_1 = require("../../social/achievements.service");
|
||||
const slug_1 = require("../../../utils/slug");
|
||||
const settings_service_1 = require("../../settings/settings.service");
|
||||
const sms_notification_service_1 = require("../../../services/sms-notification.service");
|
||||
const crypto_1 = __importDefault(require("crypto"));
|
||||
const adjectives = ['Blue', 'Red', 'Green', 'Swift', 'Bright', 'Bold', 'Calm', 'Fair'];
|
||||
const nouns = ['Eagle', 'River', 'Mountain', 'Star', 'Forest', 'Lake', 'Wolf', 'Hawk'];
|
||||
function generateReadablePassword() {
|
||||
@@ -21,6 +32,13 @@ function generateReadablePassword() {
|
||||
const num = Math.floor(Math.random() * 90) + 10;
|
||||
return `${adj}${noun}${num}`;
|
||||
}
|
||||
const meetingSelect = {
|
||||
id: true,
|
||||
slug: true,
|
||||
title: true,
|
||||
isActive: true,
|
||||
jitsiRoom: true,
|
||||
};
|
||||
exports.shiftsService = {
|
||||
async findAll(filters) {
|
||||
const { page, limit, search, status, upcoming, sortBy, sortOrder } = filters;
|
||||
@@ -50,6 +68,7 @@ exports.shiftsService = {
|
||||
orderBy,
|
||||
include: {
|
||||
cut: { select: { id: true, name: true } },
|
||||
meeting: { select: meetingSelect },
|
||||
_count: {
|
||||
select: {
|
||||
signups: { where: { status: client_1.SignupStatus.CONFIRMED } },
|
||||
@@ -74,6 +93,7 @@ exports.shiftsService = {
|
||||
where: { id },
|
||||
include: {
|
||||
cut: { select: { id: true, name: true } },
|
||||
meeting: { select: meetingSelect },
|
||||
signups: {
|
||||
where: { status: client_1.SignupStatus.CONFIRMED },
|
||||
include: { user: { select: { id: true, email: true, name: true, phone: true } } },
|
||||
@@ -106,6 +126,28 @@ exports.shiftsService = {
|
||||
createdBy: userId,
|
||||
},
|
||||
});
|
||||
// Gancio event sync (fire-and-forget)
|
||||
if (gancio_client_1.gancioClient.enabled) {
|
||||
gancio_client_1.gancioClient.createEvent({
|
||||
title: shift.title,
|
||||
description: shift.description,
|
||||
location: shift.location,
|
||||
date: shift.date,
|
||||
startTime: shift.startTime,
|
||||
endTime: shift.endTime,
|
||||
}).then(async (eventId) => {
|
||||
if (eventId) {
|
||||
await database_1.prisma.shift.update({
|
||||
where: { id: shift.id },
|
||||
data: { gancioEventId: eventId },
|
||||
});
|
||||
}
|
||||
}).catch((err) => {
|
||||
logger_1.logger.warn('Gancio sync on shift create failed:', err);
|
||||
});
|
||||
}
|
||||
// Bust unified calendar cache
|
||||
unified_calendar_service_1.unifiedCalendarService.bustCache().catch(() => { });
|
||||
return shift;
|
||||
},
|
||||
async update(id, data) {
|
||||
@@ -130,6 +172,21 @@ exports.shiftsService = {
|
||||
where: { id },
|
||||
data: updateData,
|
||||
});
|
||||
// Gancio event sync (fire-and-forget)
|
||||
if (gancio_client_1.gancioClient.enabled && shift.gancioEventId) {
|
||||
gancio_client_1.gancioClient.updateEvent(shift.gancioEventId, {
|
||||
title: shift.title,
|
||||
description: shift.description,
|
||||
location: shift.location,
|
||||
date: shift.date,
|
||||
startTime: shift.startTime,
|
||||
endTime: shift.endTime,
|
||||
}).catch((err) => {
|
||||
logger_1.logger.warn('Gancio sync on shift update failed:', err);
|
||||
});
|
||||
}
|
||||
// Bust unified calendar cache
|
||||
unified_calendar_service_1.unifiedCalendarService.bustCache().catch(() => { });
|
||||
return shift;
|
||||
},
|
||||
async delete(id) {
|
||||
@@ -137,7 +194,56 @@ exports.shiftsService = {
|
||||
if (!existing) {
|
||||
throw new error_handler_1.AppError(404, 'Shift not found', 'SHIFT_NOT_FOUND');
|
||||
}
|
||||
// Delete Gancio event before deleting shift (fire-and-forget)
|
||||
if (gancio_client_1.gancioClient.enabled && existing.gancioEventId) {
|
||||
gancio_client_1.gancioClient.deleteEvent(existing.gancioEventId).catch((err) => {
|
||||
logger_1.logger.warn('Gancio sync on shift delete failed:', err);
|
||||
});
|
||||
}
|
||||
// Delete associated meeting if exists
|
||||
if (existing.meetingId) {
|
||||
await database_1.prisma.meeting.delete({ where: { id: existing.meetingId } }).catch(() => { });
|
||||
}
|
||||
await database_1.prisma.shift.delete({ where: { id } });
|
||||
// Bust unified calendar cache
|
||||
unified_calendar_service_1.unifiedCalendarService.bustCache().catch(() => { });
|
||||
},
|
||||
async createMeetingForShift(shiftId, userId) {
|
||||
const shift = await database_1.prisma.shift.findUnique({ where: { id: shiftId } });
|
||||
if (!shift)
|
||||
throw new error_handler_1.AppError(404, 'Shift not found', 'SHIFT_NOT_FOUND');
|
||||
if (shift.meetingId)
|
||||
throw new error_handler_1.AppError(400, 'Shift already has a meeting', 'MEETING_EXISTS');
|
||||
const settings = await settings_service_1.siteSettingsService.get();
|
||||
if (!settings.enableMeet)
|
||||
throw new error_handler_1.AppError(400, 'Video meetings are not enabled', 'MEET_DISABLED');
|
||||
const meeting = await database_1.prisma.meeting.create({
|
||||
data: {
|
||||
slug: (0, slug_1.generateSlug)(shift.title),
|
||||
title: `${shift.title} — Video Briefing`,
|
||||
jitsiRoom: crypto_1.default.randomUUID(),
|
||||
createdByUserId: userId,
|
||||
},
|
||||
});
|
||||
await database_1.prisma.shift.update({
|
||||
where: { id: shiftId },
|
||||
data: { meetingId: meeting.id },
|
||||
});
|
||||
return meeting;
|
||||
},
|
||||
async removeMeetingFromShift(shiftId) {
|
||||
const shift = await database_1.prisma.shift.findUnique({ where: { id: shiftId } });
|
||||
if (!shift)
|
||||
throw new error_handler_1.AppError(404, 'Shift not found', 'SHIFT_NOT_FOUND');
|
||||
if (!shift.meetingId)
|
||||
throw new error_handler_1.AppError(400, 'Shift has no meeting', 'NO_MEETING');
|
||||
const meetingId = shift.meetingId;
|
||||
await database_1.prisma.shift.update({
|
||||
where: { id: shiftId },
|
||||
data: { meetingId: null },
|
||||
});
|
||||
// Delete the meeting record
|
||||
await database_1.prisma.meeting.delete({ where: { id: meetingId } }).catch(() => { });
|
||||
},
|
||||
async getStats() {
|
||||
const [total, open, full, cancelled, upcoming, totalSignups] = await Promise.all([
|
||||
@@ -214,6 +320,18 @@ exports.shiftsService = {
|
||||
},
|
||||
}),
|
||||
]);
|
||||
// Listmonk event sync
|
||||
listmonk_event_sync_service_1.listmonkEventSyncService.onShiftSignup({
|
||||
email: data.userEmail,
|
||||
name: data.userName || data.userEmail,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: new Date(shift.date).toISOString().split('T')[0],
|
||||
}).catch(() => { });
|
||||
// Social group sync (fire-and-forget)
|
||||
group_service_1.groupService.syncShiftTeam(shiftId).catch(() => { });
|
||||
// Achievement check (fire-and-forget)
|
||||
if (user?.id)
|
||||
achievements_service_1.achievementsService.checkAndUnlock(user.id, ['shifts']).catch(() => { });
|
||||
return signup;
|
||||
},
|
||||
async removeSignup(signupId) {
|
||||
@@ -237,6 +355,8 @@ exports.shiftsService = {
|
||||
},
|
||||
}),
|
||||
]);
|
||||
// Social group sync (fire-and-forget)
|
||||
group_service_1.groupService.syncShiftTeam(signup.shiftId).catch(() => { });
|
||||
},
|
||||
async publicSignup(shiftId, data) {
|
||||
const shift = await database_1.prisma.shift.findUnique({ where: { id: shiftId } });
|
||||
@@ -279,6 +399,7 @@ exports.shiftsService = {
|
||||
name: data.name,
|
||||
phone: data.phone,
|
||||
role: 'TEMP',
|
||||
roles: JSON.parse(JSON.stringify(['TEMP'])),
|
||||
createdVia: 'PUBLIC_SHIFT_SIGNUP',
|
||||
expiresAt: shiftDate,
|
||||
},
|
||||
@@ -339,38 +460,142 @@ exports.shiftsService = {
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
});
|
||||
const htmlTemplate = email_service_1.emailService.loadTemplate('shift-signup-confirmation', 'html');
|
||||
const txtTemplate = email_service_1.emailService.loadTemplate('shift-signup-confirmation', 'txt');
|
||||
let orgName = 'Changemaker Lite';
|
||||
try {
|
||||
orgName = (await settings_service_1.siteSettingsService.get()).organizationName || orgName;
|
||||
}
|
||||
catch { /* use default */ }
|
||||
const vars = {
|
||||
USER_NAME: data.name,
|
||||
USER_EMAIL: data.email,
|
||||
SHIFT_TITLE: shift.title,
|
||||
SHIFT_DATE: dateStr,
|
||||
SHIFT_TIME: `${shift.startTime} — ${shift.endTime}`,
|
||||
SHIFT_LOCATION: shift.location || 'TBD',
|
||||
IS_NEW_USER: isNewUser ? 'true' : '',
|
||||
TEMP_PASSWORD: tempPassword || '',
|
||||
LOGIN_URL: `${env_1.env.CORS_ORIGINS.split(',')[0].trim()}/login`,
|
||||
ORGANIZATION_NAME: orgName,
|
||||
};
|
||||
const html = email_service_1.emailService.processTemplate(htmlTemplate, vars);
|
||||
const text = email_service_1.emailService.processTemplate(txtTemplate, vars);
|
||||
await email_service_1.emailService.sendEmail({
|
||||
to: data.email,
|
||||
subject: `Signup Confirmed — ${shift.title}`,
|
||||
html,
|
||||
text,
|
||||
await email_service_1.emailService.sendShiftSignupConfirmation({
|
||||
recipientEmail: data.email,
|
||||
recipientName: data.name,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: dateStr,
|
||||
shiftTime: `${shift.startTime} — ${shift.endTime}`,
|
||||
shiftLocation: shift.location || 'TBD',
|
||||
isNewUser,
|
||||
tempPassword,
|
||||
loginUrl: `${env_1.env.CORS_ORIGINS.split(',')[0].trim()}/login`,
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
logger_1.logger.error('Failed to send shift signup confirmation email:', err);
|
||||
}
|
||||
// SMS signup confirmation (fire-and-forget)
|
||||
if (data.phone) {
|
||||
const shiftDate = new Date(shift.date);
|
||||
const smsDateStr = shiftDate.toLocaleDateString('en-CA', {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
});
|
||||
sms_notification_service_1.smsNotificationService.sendShiftSignupConfirmation(data.phone, {
|
||||
name: data.name,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: smsDateStr,
|
||||
shiftTime: `${shift.startTime} — ${shift.endTime}`,
|
||||
}).catch(err => logger_1.logger.error('SMS signup confirmation failed:', err));
|
||||
}
|
||||
// Notify Rocket.Chat
|
||||
const shiftDateStr = new Date(shift.date).toLocaleDateString('en-CA', { month: 'short', day: 'numeric' });
|
||||
rocketchat_webhook_service_1.rocketchatWebhookService.onShiftSignup({
|
||||
userName: data.name || data.email,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: shiftDateStr,
|
||||
}).catch(() => { });
|
||||
// Notification: admin shift signup alert
|
||||
try {
|
||||
if (await (0, notification_helper_1.isNotificationEnabled)('notifyAdminShiftSignup')) {
|
||||
const adminEmails = await (0, notification_helper_1.getAdminEmailsByRole)([client_1.UserRole.SUPER_ADMIN, client_1.UserRole.MAP_ADMIN]);
|
||||
if (adminEmails.length > 0) {
|
||||
const adminUrl = `${env_1.env.ADMIN_URL || 'http://localhost:3000'}/app/map/shifts`;
|
||||
const shiftDate = new Date(shift.date);
|
||||
const dateStr = shiftDate.toLocaleDateString('en-CA', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
|
||||
await notification_queue_service_1.notificationQueueService.enqueue({
|
||||
type: 'admin-shift-signup',
|
||||
adminEmails,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: dateStr,
|
||||
volunteerName: data.name,
|
||||
volunteerEmail: data.email,
|
||||
signupSource: 'Public Form',
|
||||
adminUrl,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger_1.logger.error('Failed to enqueue admin shift signup notification:', err);
|
||||
}
|
||||
// Notification: schedule 24h pre-shift reminder
|
||||
try {
|
||||
if (await (0, notification_helper_1.isNotificationEnabled)('notifyVolunteerShiftReminder')) {
|
||||
const shiftDatetime = new Date(shift.date);
|
||||
const [startH, startM] = shift.startTime.split(':').map(Number);
|
||||
shiftDatetime.setHours(startH || 0, startM || 0, 0, 0);
|
||||
await notification_queue_service_1.notificationQueueService.scheduleShiftReminder({
|
||||
type: 'volunteer-shift-reminder',
|
||||
recipientEmail: data.email,
|
||||
recipientName: data.name,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: shiftDatetime.toLocaleDateString('en-CA', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }),
|
||||
shiftStartTime: shift.startTime,
|
||||
shiftEndTime: shift.endTime,
|
||||
shiftLocation: shift.location || 'TBD',
|
||||
shiftDescription: shift.description || '',
|
||||
currentVolunteers: shift.currentVolunteers + 1,
|
||||
maxVolunteers: shift.maxVolunteers,
|
||||
shiftStatus: shift.status,
|
||||
}, shiftDatetime);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger_1.logger.error('Failed to schedule shift reminder:', err);
|
||||
}
|
||||
// SMS shift reminder (fire-and-forget, delay calculated by notification service)
|
||||
if (data.phone) {
|
||||
const smsShiftDatetime = new Date(shift.date);
|
||||
const [smsH, smsM] = shift.startTime.split(':').map(Number);
|
||||
smsShiftDatetime.setHours(smsH || 0, smsM || 0, 0, 0);
|
||||
sms_notification_service_1.smsNotificationService.sendShiftReminder(data.phone, {
|
||||
name: data.name,
|
||||
shiftTitle: shift.title,
|
||||
shiftTime: shift.startTime,
|
||||
shiftLocation: shift.location || 'TBD',
|
||||
}, smsShiftDatetime).catch(err => logger_1.logger.error('SMS shift reminder failed:', err));
|
||||
}
|
||||
// Notification: schedule post-shift thank-you (2h after end)
|
||||
try {
|
||||
if (await (0, notification_helper_1.isNotificationEnabled)('notifyVolunteerShiftThankYou')) {
|
||||
const shiftEndDatetime = new Date(shift.date);
|
||||
const [endH, endM] = shift.endTime.split(':').map(Number);
|
||||
shiftEndDatetime.setHours(endH || 0, endM || 0, 0, 0);
|
||||
const shiftDate = new Date(shift.date);
|
||||
const dateStr = shiftDate.toLocaleDateString('en-CA', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
|
||||
const signupUrl = `${env_1.env.CORS_ORIGINS.split(',')[0].trim()}/shifts`;
|
||||
await notification_queue_service_1.notificationQueueService.scheduleShiftThankYou({
|
||||
type: 'volunteer-shift-thank-you',
|
||||
volunteerEmail: data.email,
|
||||
volunteerName: data.name,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: dateStr,
|
||||
shiftTime: `${shift.startTime} — ${shift.endTime}`,
|
||||
shiftLocation: shift.location || 'TBD',
|
||||
signupUrl,
|
||||
}, shiftEndDatetime);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger_1.logger.error('Failed to schedule shift thank-you:', err);
|
||||
}
|
||||
(0, metrics_1.recordShiftSignup)();
|
||||
// Listmonk event sync
|
||||
listmonk_event_sync_service_1.listmonkEventSyncService.onShiftSignup({
|
||||
email: data.email,
|
||||
name: data.name,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: new Date(shift.date).toISOString().split('T')[0],
|
||||
}).catch(() => { });
|
||||
// Social group sync (fire-and-forget)
|
||||
group_service_1.groupService.syncShiftTeam(shiftId).catch(() => { });
|
||||
// Achievement check (fire-and-forget)
|
||||
if (user?.id)
|
||||
achievements_service_1.achievementsService.checkAndUnlock(user.id, ['shifts']).catch(() => { });
|
||||
return { signup, isNewUser };
|
||||
},
|
||||
async cancelPublicSignup(shiftId, userEmail) {
|
||||
@@ -383,6 +608,7 @@ exports.shiftsService = {
|
||||
if (signup.status === client_1.SignupStatus.CANCELLED) {
|
||||
throw new error_handler_1.AppError(400, 'Signup already cancelled', 'ALREADY_CANCELLED');
|
||||
}
|
||||
const shift = await database_1.prisma.shift.findUnique({ where: { id: shiftId } });
|
||||
await database_1.prisma.$transaction([
|
||||
database_1.prisma.shiftSignup.update({
|
||||
where: { id: signup.id },
|
||||
@@ -396,6 +622,75 @@ exports.shiftsService = {
|
||||
},
|
||||
}),
|
||||
]);
|
||||
// Notification: cancellation acknowledgement + cancel reminder
|
||||
try {
|
||||
if (shift && await (0, notification_helper_1.isNotificationEnabled)('notifyVolunteerCancellation')) {
|
||||
const shiftDate = new Date(shift.date);
|
||||
const dateStr = shiftDate.toLocaleDateString('en-CA', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
|
||||
const signupUrl = `${env_1.env.CORS_ORIGINS.split(',')[0].trim()}/shifts`;
|
||||
await notification_queue_service_1.notificationQueueService.enqueue({
|
||||
type: 'volunteer-cancellation',
|
||||
volunteerEmail: userEmail,
|
||||
volunteerName: signup.userName || userEmail,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: dateStr,
|
||||
shiftTime: `${shift.startTime} — ${shift.endTime}`,
|
||||
signupUrl,
|
||||
});
|
||||
}
|
||||
// Cancel the pending shift reminder
|
||||
if (shift) {
|
||||
const shiftDatetime = new Date(shift.date);
|
||||
const [startH, startM] = shift.startTime.split(':').map(Number);
|
||||
shiftDatetime.setHours(startH || 0, startM || 0, 0, 0);
|
||||
await notification_queue_service_1.notificationQueueService.cancelShiftReminder(userEmail, shiftDatetime);
|
||||
}
|
||||
// Cancel the pending shift thank-you
|
||||
if (shift) {
|
||||
const shiftEndDatetime = new Date(shift.date);
|
||||
const [endH, endM] = shift.endTime.split(':').map(Number);
|
||||
shiftEndDatetime.setHours(endH || 0, endM || 0, 0, 0);
|
||||
await notification_queue_service_1.notificationQueueService.cancelShiftThankYou(userEmail, shiftEndDatetime);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger_1.logger.error('Failed to enqueue cancellation notification:', err);
|
||||
}
|
||||
// Notify Rocket.Chat of cancellation
|
||||
if (shift) {
|
||||
const shiftDateStr = new Date(shift.date).toLocaleDateString('en-CA', { month: 'short', day: 'numeric' });
|
||||
rocketchat_webhook_service_1.rocketchatWebhookService.onShiftCancellation({
|
||||
userName: signup.userName || userEmail,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: shiftDateStr,
|
||||
}).catch(() => { });
|
||||
}
|
||||
// Notification: admin shift cancellation alert
|
||||
try {
|
||||
if (shift && await (0, notification_helper_1.isNotificationEnabled)('notifyAdminShiftCancellation')) {
|
||||
const adminEmails = await (0, notification_helper_1.getAdminEmailsByRole)([client_1.UserRole.SUPER_ADMIN, client_1.UserRole.MAP_ADMIN]);
|
||||
if (adminEmails.length > 0) {
|
||||
const adminUrl = `${env_1.env.ADMIN_URL || 'http://localhost:3000'}/app/map/shifts`;
|
||||
const shiftDate = new Date(shift.date);
|
||||
const dateStr = shiftDate.toLocaleDateString('en-CA', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
|
||||
await notification_queue_service_1.notificationQueueService.enqueue({
|
||||
type: 'admin-shift-cancellation',
|
||||
adminEmails,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: dateStr,
|
||||
volunteerName: signup.userName || userEmail,
|
||||
volunteerEmail: userEmail,
|
||||
cancellationSource: 'Public Form',
|
||||
adminUrl,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger_1.logger.error('Failed to enqueue admin shift cancellation notification:', err);
|
||||
}
|
||||
// Social group sync (fire-and-forget)
|
||||
group_service_1.groupService.syncShiftTeam(shiftId).catch(() => { });
|
||||
},
|
||||
async getUpcomingForVolunteer(userId) {
|
||||
const user = await database_1.prisma.user.findUnique({ where: { id: userId }, select: { email: true } });
|
||||
@@ -418,6 +713,7 @@ exports.shiftsService = {
|
||||
maxVolunteers: true,
|
||||
currentVolunteers: true,
|
||||
status: true,
|
||||
meeting: { select: { id: true, slug: true, isActive: true } },
|
||||
},
|
||||
orderBy: [{ date: 'asc' }, { startTime: 'asc' }],
|
||||
});
|
||||
@@ -504,41 +800,115 @@ exports.shiftsService = {
|
||||
const dateStr = shiftDate.toLocaleDateString('en-CA', {
|
||||
weekday: 'long', year: 'numeric', month: 'long', day: 'numeric',
|
||||
});
|
||||
const htmlTemplate = email_service_1.emailService.loadTemplate('shift-signup-confirmation', 'html');
|
||||
const txtTemplate = email_service_1.emailService.loadTemplate('shift-signup-confirmation', 'txt');
|
||||
let orgName = 'Changemaker Lite';
|
||||
try {
|
||||
orgName = (await settings_service_1.siteSettingsService.get()).organizationName || orgName;
|
||||
}
|
||||
catch { /* default */ }
|
||||
const vars = {
|
||||
USER_NAME: user.name || user.email,
|
||||
USER_EMAIL: user.email,
|
||||
SHIFT_TITLE: shift.title,
|
||||
SHIFT_DATE: dateStr,
|
||||
SHIFT_TIME: `${shift.startTime} — ${shift.endTime}`,
|
||||
SHIFT_LOCATION: shift.location || 'TBD',
|
||||
IS_NEW_USER: '',
|
||||
TEMP_PASSWORD: '',
|
||||
LOGIN_URL: `${env_1.env.CORS_ORIGINS.split(',')[0].trim()}/login`,
|
||||
ORGANIZATION_NAME: orgName,
|
||||
};
|
||||
const html = email_service_1.emailService.processTemplate(htmlTemplate, vars);
|
||||
const text = email_service_1.emailService.processTemplate(txtTemplate, vars);
|
||||
await email_service_1.emailService.sendEmail({
|
||||
to: user.email,
|
||||
subject: `Signup Confirmed — ${shift.title}`,
|
||||
html,
|
||||
text,
|
||||
await email_service_1.emailService.sendShiftSignupConfirmation({
|
||||
recipientEmail: user.email,
|
||||
recipientName: user.name || user.email,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: dateStr,
|
||||
shiftTime: `${shift.startTime} — ${shift.endTime}`,
|
||||
shiftLocation: shift.location || 'TBD',
|
||||
isNewUser: false,
|
||||
loginUrl: `${env_1.env.CORS_ORIGINS.split(',')[0].trim()}/login`,
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
logger_1.logger.error('Failed to send volunteer shift signup confirmation email:', err);
|
||||
}
|
||||
// Notify Rocket.Chat
|
||||
const shiftDateStr = new Date(shift.date).toLocaleDateString('en-CA', { month: 'short', day: 'numeric' });
|
||||
rocketchat_webhook_service_1.rocketchatWebhookService.onShiftSignup({
|
||||
userName: user.name || user.email,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: shiftDateStr,
|
||||
}).catch(() => { });
|
||||
// Notification: admin shift signup alert
|
||||
try {
|
||||
if (await (0, notification_helper_1.isNotificationEnabled)('notifyAdminShiftSignup')) {
|
||||
const adminEmails = await (0, notification_helper_1.getAdminEmailsByRole)([client_1.UserRole.SUPER_ADMIN, client_1.UserRole.MAP_ADMIN]);
|
||||
if (adminEmails.length > 0) {
|
||||
const adminUrl = `${env_1.env.ADMIN_URL || 'http://localhost:3000'}/app/map/shifts`;
|
||||
const shiftDate = new Date(shift.date);
|
||||
const dateStr = shiftDate.toLocaleDateString('en-CA', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
|
||||
await notification_queue_service_1.notificationQueueService.enqueue({
|
||||
type: 'admin-shift-signup',
|
||||
adminEmails,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: dateStr,
|
||||
volunteerName: user.name || user.email,
|
||||
volunteerEmail: user.email,
|
||||
signupSource: 'Authenticated Volunteer',
|
||||
adminUrl,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger_1.logger.error('Failed to enqueue admin shift signup notification:', err);
|
||||
}
|
||||
// Notification: schedule 24h pre-shift reminder
|
||||
try {
|
||||
if (await (0, notification_helper_1.isNotificationEnabled)('notifyVolunteerShiftReminder')) {
|
||||
const shiftDatetime = new Date(shift.date);
|
||||
const [startH, startM] = shift.startTime.split(':').map(Number);
|
||||
shiftDatetime.setHours(startH || 0, startM || 0, 0, 0);
|
||||
await notification_queue_service_1.notificationQueueService.scheduleShiftReminder({
|
||||
type: 'volunteer-shift-reminder',
|
||||
recipientEmail: user.email,
|
||||
recipientName: user.name || user.email,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: shiftDatetime.toLocaleDateString('en-CA', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }),
|
||||
shiftStartTime: shift.startTime,
|
||||
shiftEndTime: shift.endTime,
|
||||
shiftLocation: shift.location || 'TBD',
|
||||
shiftDescription: shift.description || '',
|
||||
currentVolunteers: shift.currentVolunteers + 1,
|
||||
maxVolunteers: shift.maxVolunteers,
|
||||
shiftStatus: shift.status,
|
||||
}, shiftDatetime);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger_1.logger.error('Failed to schedule shift reminder:', err);
|
||||
}
|
||||
// Notification: schedule post-shift thank-you (2h after end)
|
||||
try {
|
||||
if (await (0, notification_helper_1.isNotificationEnabled)('notifyVolunteerShiftThankYou')) {
|
||||
const shiftEndDatetime = new Date(shift.date);
|
||||
const [endH, endM] = shift.endTime.split(':').map(Number);
|
||||
shiftEndDatetime.setHours(endH || 0, endM || 0, 0, 0);
|
||||
const shiftDate = new Date(shift.date);
|
||||
const dateStr = shiftDate.toLocaleDateString('en-CA', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
|
||||
const signupUrl = `${env_1.env.CORS_ORIGINS.split(',')[0].trim()}/shifts`;
|
||||
await notification_queue_service_1.notificationQueueService.scheduleShiftThankYou({
|
||||
type: 'volunteer-shift-thank-you',
|
||||
volunteerEmail: user.email,
|
||||
volunteerName: user.name || user.email,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: dateStr,
|
||||
shiftTime: `${shift.startTime} — ${shift.endTime}`,
|
||||
shiftLocation: shift.location || 'TBD',
|
||||
signupUrl,
|
||||
}, shiftEndDatetime);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger_1.logger.error('Failed to schedule shift thank-you:', err);
|
||||
}
|
||||
// Listmonk event sync
|
||||
listmonk_event_sync_service_1.listmonkEventSyncService.onShiftSignup({
|
||||
email: user.email,
|
||||
name: user.name || user.email,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: new Date(shift.date).toISOString().split('T')[0],
|
||||
}).catch(() => { });
|
||||
// Social group sync (fire-and-forget)
|
||||
group_service_1.groupService.syncShiftTeam(shiftId).catch(() => { });
|
||||
// Achievement check (fire-and-forget)
|
||||
achievements_service_1.achievementsService.checkAndUnlock(userId, ['shifts']).catch(() => { });
|
||||
return signup;
|
||||
},
|
||||
async cancelVolunteerSignup(shiftId, userId) {
|
||||
const user = await database_1.prisma.user.findUnique({ where: { id: userId }, select: { email: true } });
|
||||
const user = await database_1.prisma.user.findUnique({ where: { id: userId }, select: { email: true, name: true } });
|
||||
if (!user)
|
||||
throw new error_handler_1.AppError(404, 'User not found', 'USER_NOT_FOUND');
|
||||
const signup = await database_1.prisma.shiftSignup.findUnique({
|
||||
@@ -548,6 +918,7 @@ exports.shiftsService = {
|
||||
throw new error_handler_1.AppError(404, 'Signup not found', 'SIGNUP_NOT_FOUND');
|
||||
if (signup.status === client_1.SignupStatus.CANCELLED)
|
||||
throw new error_handler_1.AppError(400, 'Already cancelled', 'ALREADY_CANCELLED');
|
||||
const shift = await database_1.prisma.shift.findUnique({ where: { id: shiftId } });
|
||||
await database_1.prisma.$transaction([
|
||||
database_1.prisma.shiftSignup.update({
|
||||
where: { id: signup.id },
|
||||
@@ -561,6 +932,75 @@ exports.shiftsService = {
|
||||
},
|
||||
}),
|
||||
]);
|
||||
// Notification: cancellation acknowledgement + cancel reminder
|
||||
try {
|
||||
if (shift && await (0, notification_helper_1.isNotificationEnabled)('notifyVolunteerCancellation')) {
|
||||
const shiftDate = new Date(shift.date);
|
||||
const dateStr = shiftDate.toLocaleDateString('en-CA', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
|
||||
const signupUrl = `${env_1.env.CORS_ORIGINS.split(',')[0].trim()}/shifts`;
|
||||
await notification_queue_service_1.notificationQueueService.enqueue({
|
||||
type: 'volunteer-cancellation',
|
||||
volunteerEmail: user.email,
|
||||
volunteerName: user.name || user.email,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: dateStr,
|
||||
shiftTime: `${shift.startTime} — ${shift.endTime}`,
|
||||
signupUrl,
|
||||
});
|
||||
}
|
||||
// Cancel the pending shift reminder
|
||||
if (shift) {
|
||||
const shiftDatetime = new Date(shift.date);
|
||||
const [startH, startM] = shift.startTime.split(':').map(Number);
|
||||
shiftDatetime.setHours(startH || 0, startM || 0, 0, 0);
|
||||
await notification_queue_service_1.notificationQueueService.cancelShiftReminder(user.email, shiftDatetime);
|
||||
}
|
||||
// Cancel the pending shift thank-you
|
||||
if (shift) {
|
||||
const shiftEndDatetime = new Date(shift.date);
|
||||
const [endH, endM] = shift.endTime.split(':').map(Number);
|
||||
shiftEndDatetime.setHours(endH || 0, endM || 0, 0, 0);
|
||||
await notification_queue_service_1.notificationQueueService.cancelShiftThankYou(user.email, shiftEndDatetime);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger_1.logger.error('Failed to enqueue cancellation notification:', err);
|
||||
}
|
||||
// Notify Rocket.Chat of cancellation
|
||||
if (shift) {
|
||||
const shiftDateStr = new Date(shift.date).toLocaleDateString('en-CA', { month: 'short', day: 'numeric' });
|
||||
rocketchat_webhook_service_1.rocketchatWebhookService.onShiftCancellation({
|
||||
userName: user.name || user.email,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: shiftDateStr,
|
||||
}).catch(() => { });
|
||||
}
|
||||
// Notification: admin shift cancellation alert
|
||||
try {
|
||||
if (shift && await (0, notification_helper_1.isNotificationEnabled)('notifyAdminShiftCancellation')) {
|
||||
const adminEmails = await (0, notification_helper_1.getAdminEmailsByRole)([client_1.UserRole.SUPER_ADMIN, client_1.UserRole.MAP_ADMIN]);
|
||||
if (adminEmails.length > 0) {
|
||||
const adminUrl = `${env_1.env.ADMIN_URL || 'http://localhost:3000'}/app/map/shifts`;
|
||||
const shiftDate = new Date(shift.date);
|
||||
const dateStr = shiftDate.toLocaleDateString('en-CA', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
|
||||
await notification_queue_service_1.notificationQueueService.enqueue({
|
||||
type: 'admin-shift-cancellation',
|
||||
adminEmails,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: dateStr,
|
||||
volunteerName: user.name || user.email,
|
||||
volunteerEmail: user.email,
|
||||
cancellationSource: 'Volunteer Portal',
|
||||
adminUrl,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
logger_1.logger.error('Failed to enqueue admin shift cancellation notification:', err);
|
||||
}
|
||||
// Social group sync (fire-and-forget)
|
||||
group_service_1.groupService.syncShiftTeam(shiftId).catch(() => { });
|
||||
},
|
||||
async getMySignups(userId) {
|
||||
const user = await database_1.prisma.user.findUnique({ where: { id: userId }, select: { email: true } });
|
||||
@@ -588,6 +1028,7 @@ exports.shiftsService = {
|
||||
maxVolunteers: true,
|
||||
currentVolunteers: true,
|
||||
status: true,
|
||||
meeting: { select: { id: true, slug: true, isActive: true } },
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -613,6 +1054,7 @@ exports.shiftsService = {
|
||||
maxVolunteers: true,
|
||||
currentVolunteers: true,
|
||||
status: true,
|
||||
meeting: { select: { id: true, slug: true, isActive: true } },
|
||||
},
|
||||
orderBy: [{ date: 'asc' }, { startTime: 'asc' }],
|
||||
});
|
||||
@@ -637,37 +1079,22 @@ exports.shiftsService = {
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
});
|
||||
const htmlTemplate = email_service_1.emailService.loadTemplate('shift-details', 'html');
|
||||
const txtTemplate = email_service_1.emailService.loadTemplate('shift-details', 'txt');
|
||||
let orgName = 'Changemaker Lite';
|
||||
try {
|
||||
orgName = (await settings_service_1.siteSettingsService.get()).organizationName || orgName;
|
||||
}
|
||||
catch { /* use default */ }
|
||||
let sent = 0;
|
||||
let failed = 0;
|
||||
for (const signup of shift.signups) {
|
||||
try {
|
||||
const vars = {
|
||||
USER_NAME: signup.userName || signup.userEmail,
|
||||
SHIFT_TITLE: shift.title,
|
||||
SHIFT_DATE: dateStr,
|
||||
SHIFT_START_TIME: shift.startTime,
|
||||
SHIFT_END_TIME: shift.endTime,
|
||||
SHIFT_LOCATION: shift.location || 'TBD',
|
||||
SHIFT_DESCRIPTION: shift.description || '',
|
||||
CURRENT_VOLUNTEERS: shift.currentVolunteers.toString(),
|
||||
MAX_VOLUNTEERS: shift.maxVolunteers.toString(),
|
||||
SHIFT_STATUS: shift.status,
|
||||
ORGANIZATION_NAME: orgName,
|
||||
};
|
||||
const html = email_service_1.emailService.processTemplate(htmlTemplate, vars);
|
||||
const text = email_service_1.emailService.processTemplate(txtTemplate, vars);
|
||||
const result = await email_service_1.emailService.sendEmail({
|
||||
to: signup.userEmail,
|
||||
subject: `Shift Details — ${shift.title}`,
|
||||
html,
|
||||
text,
|
||||
const result = await email_service_1.emailService.sendShiftDetailsEmail({
|
||||
recipientEmail: signup.userEmail,
|
||||
recipientName: signup.userName || signup.userEmail,
|
||||
shiftTitle: shift.title,
|
||||
shiftDate: dateStr,
|
||||
shiftStartTime: shift.startTime,
|
||||
shiftEndTime: shift.endTime,
|
||||
shiftLocation: shift.location || 'TBD',
|
||||
shiftDescription: shift.description || '',
|
||||
currentVolunteers: shift.currentVolunteers,
|
||||
maxVolunteers: shift.maxVolunteers,
|
||||
shiftStatus: shift.status,
|
||||
});
|
||||
if (result.success) {
|
||||
sent++;
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
{"version":3,"file":"tracking.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/tracking/tracking.routes.ts"],"names":[],"mappings":"AAmBA,QAAA,MAAM,eAAe,4CAAW,CAAC;AAoHjC,QAAA,MAAM,WAAW,4CAAW,CAAC;AA8C7B,OAAO,EAAE,eAAe,IAAI,uBAAuB,EAAE,WAAW,IAAI,mBAAmB,EAAE,CAAC"}
|
||||
{"version":3,"file":"tracking.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/map/tracking/tracking.routes.ts"],"names":[],"mappings":"AAiBA,QAAA,MAAM,eAAe,4CAAW,CAAC;AAoHjC,QAAA,MAAM,WAAW,4CAAW,CAAC;AA8C7B,OAAO,EAAE,eAAe,IAAI,uBAAuB,EAAE,WAAW,IAAI,mBAAmB,EAAE,CAAC"}
|
||||
@@ -2,14 +2,13 @@
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.trackingAdminRouter = exports.trackingVolunteerRouter = void 0;
|
||||
const express_1 = require("express");
|
||||
const client_1 = require("@prisma/client");
|
||||
const tracking_service_1 = require("./tracking.service");
|
||||
const tracking_schemas_1 = require("./tracking.schemas");
|
||||
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 MAP_ADMIN_ROLES = [client_1.UserRole.SUPER_ADMIN, client_1.UserRole.MAP_ADMIN];
|
||||
const roles_1 = require("../../../utils/roles");
|
||||
// ─── Volunteer Router ────────────────────────────────────────────────
|
||||
const volunteerRouter = (0, express_1.Router)();
|
||||
exports.trackingVolunteerRouter = volunteerRouter;
|
||||
@@ -104,7 +103,7 @@ volunteerRouter.get('/my/sessions/:id/route', async (req, res, next) => {
|
||||
const adminRouter = (0, express_1.Router)();
|
||||
exports.trackingAdminRouter = adminRouter;
|
||||
adminRouter.use(auth_middleware_1.authenticate);
|
||||
adminRouter.use((0, rbac_middleware_1.requireRole)(...MAP_ADMIN_ROLES));
|
||||
adminRouter.use((0, rbac_middleware_1.requireRole)(...roles_1.MAP_ROLES));
|
||||
// GET /api/map/tracking/live — active volunteers with positions + recent trails
|
||||
adminRouter.get('/live', (0, validate_1.validate)(tracking_schemas_1.liveVolunteersQuerySchema, 'query'), async (req, res, next) => {
|
||||
try {
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"tracking.routes.js","sourceRoot":"","sources":["../../../../src/modules/map/tracking/tracking.routes.ts"],"names":[],"mappings":"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"}
|
||||
{"version":3,"file":"tracking.routes.js","sourceRoot":"","sources":["../../../../src/modules/map/tracking/tracking.routes.ts"],"names":[],"mappings":"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"}
|
||||
Reference in New Issue
Block a user