ActionCampaign service exposes admin CRUD plus a per-user composer (getActiveForUser) that fans out a per-step completion check against the existing per-user model for each ActionStepKind: VideoView for WATCH_VIDEO, CampaignEmail for SUBMIT_INFLUENCE, PetitionSignature for SIGN_PETITION (matched by signer email), Ticket for RSVP_EVENT, ShiftSignup for SIGNUP_SHIFT, ChallengeTeamMember for JOIN_CHALLENGE. CUSTOM and VISIT_LINK steps complete only via explicit self-report. An existing ActionStepCompletion row also short-circuits the check so manual marking and idempotency both work. Volunteer dashboard aggregator at GET /api/volunteer/dashboard composes the active campaign with the user's profile, referral info, upcoming featured event, training shifts (Shift.kind='TRAINING'), ticketed events the user holds, an engagement-counter point total (placeholder until the Redis engagement score is wired in), and resources tagged 'volunteer-resource' across Document/Video/Photo. Bunker Admin
23 lines
688 B
TypeScript
23 lines
688 B
TypeScript
import { Router, Request, Response, NextFunction } from 'express';
|
|
import { volunteerDashboardService } from './volunteer-dashboard.service';
|
|
import { authenticate } from '../../middleware/auth.middleware';
|
|
import { AppError } from '../../middleware/error-handler';
|
|
|
|
const router = Router();
|
|
|
|
router.get(
|
|
'/dashboard',
|
|
authenticate,
|
|
async (req: Request, res: Response, next: NextFunction) => {
|
|
try {
|
|
const data = await volunteerDashboardService.getDashboard(req.user!.id);
|
|
if (!data) throw new AppError(404, 'User not found', 'USER_NOT_FOUND');
|
|
res.json(data);
|
|
} catch (err) {
|
|
next(err);
|
|
}
|
|
},
|
|
);
|
|
|
|
export { router as volunteerDashboardRouter };
|