Spaces:
Sleeping
Sleeping
File size: 10,951 Bytes
1ae6d1e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 | import {
Controller,
Get,
Post,
Put,
Delete,
Body,
Param,
Query,
Req,
UseGuards,
ParseIntPipe,
HttpCode,
HttpStatus,
} from '@nestjs/common';
import {
ApiTags,
ApiOperation,
ApiParam,
ApiBody,
ApiResponse,
ApiBearerAuth,
} from '@nestjs/swagger';
import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard';
import { RolesGuard } from '../../auth/guards/roles.guard';
import { Roles } from '../../auth/roles.decorator';
import { RoleName } from '../../auth/entities/role.entity';
import { CampusEventsService } from '../services/campus-events.service';
import {
CreateCampusEventDto,
UpdateCampusEventDto,
QueryCampusEventDto,
RegisterCampusEventDto,
} from '../dto';
@ApiTags('π Campus Events')
@ApiBearerAuth('JWT-auth')
@Controller('api/campus-events')
@UseGuards(JwtAuthGuard, RolesGuard)
export class CampusEventsController {
constructor(private readonly eventsService: CampusEventsService) {}
@Get()
@ApiOperation({
summary: 'List campus events',
description: `
## List Campus Events
Returns paginated list of campus events filtered by visibility and query parameters.
### Access Control
- **Authentication Required**: β
Yes (Bearer Token)
- **Roles**: ALL
### Visibility Rules
- **University-wide events**: Visible to ALL users
- **Department events**: Visible to users in that department
- **Campus events**: Visible to users on that campus
- **Program events**: Visible to users in that program
- **Admins/IT_Admins**: See all events regardless of status
- **Others**: Only see published events
### Query Parameters
- \`eventType\`: Filter by event type
- \`scopeId\`: Filter by scope ID
- \`status\`: Filter by status
- \`fromDate\`: Events from this date
- \`toDate\`: Events until this date
- \`tag\`: Filter by tag
- \`search\`: Search in title or description
- \`page\`: Page number (default: 1)
- \`limit\`: Items per page (default: 10)
`,
})
@ApiResponse({ status: 200, description: 'Paginated list of campus events' })
@ApiResponse({ status: 401, description: 'Unauthorized' })
async findAll(@Query() query: QueryCampusEventDto, @Req() req: any) {
const userId = req.user.userId || req.user.id;
const roles = this.extractRoles(req.user);
return this.eventsService.findAll(query, userId, roles);
}
@Get('my')
@ApiOperation({
summary: 'Get my campus events',
description: `
## Get My Campus Events
Returns campus events visible to the current user (published events from today onwards).
### Access Control
- **Authentication Required**: β
Yes (Bearer Token)
- **Roles**: ALL
`,
})
@ApiResponse({ status: 200, description: 'List of events visible to user' })
@ApiResponse({ status: 401, description: 'Unauthorized' })
async findMyEvents(@Req() req: any) {
const userId = req.user.userId || req.user.id;
const roles = this.extractRoles(req.user);
return this.eventsService.findMyEvents(userId, roles);
}
@Get(':id')
@ApiOperation({
summary: 'Get campus event by ID',
description: `
## Get Campus Event Details
Returns detailed information about a specific campus event including registration status.
### Access Control
- **Authentication Required**: β
Yes (Bearer Token)
- **Roles**: ALL
### Response Includes
- Event details
- User's registration status (if applicable)
- Current registration count
- Spots remaining (if max attendees set)
`,
})
@ApiParam({ name: 'id', description: 'Campus event ID', type: Number, example: 1 })
@ApiResponse({ status: 200, description: 'Campus event details' })
@ApiResponse({ status: 404, description: 'Event not found' })
async findById(@Param('id', ParseIntPipe) id: number, @Req() req: any) {
const userId = req.user.userId || req.user.id;
const roles = this.extractRoles(req.user);
return this.eventsService.findById(id, userId, roles);
}
@Post()
@Roles(RoleName.ADMIN, RoleName.IT_ADMIN, RoleName.DEPARTMENT_HEAD)
@HttpCode(HttpStatus.CREATED)
@ApiOperation({
summary: 'Create campus event',
description: `
## Create New Campus Event
Creates a new campus event.
### Access Control
- **Authentication Required**: β
Yes (Bearer Token)
- **Roles**: ADMIN, IT_ADMIN, DEPARTMENT_HEAD
### Permissions
- **Admins/IT_Admins**: Can create any type of event
- **Department Heads**: Can only create department-level events for their department
### Required Fields
- \`title\`: Event name
- \`eventType\`: Type of event (university_wide, department, campus, program)
- \`startDatetime\`: Start date and time
- \`endDatetime\`: End date and time
### Optional Fields
- \`description\`: Event description
- \`scopeId\`: Department/campus/program ID (required for non-university_wide events)
- \`location\`, \`building\`, \`room\`: Location details
- \`isMandatory\`: Is attendance mandatory
- \`registrationRequired\`: Does event require registration
- \`maxAttendees\`: Maximum number of attendees
- \`color\`: Hex color for calendar display
- \`status\`: Event status (default: draft)
- \`tags\`: Array of tags
`,
})
@ApiBody({ type: CreateCampusEventDto })
@ApiResponse({ status: 201, description: 'Campus event created successfully' })
@ApiResponse({ status: 400, description: 'Invalid input data' })
@ApiResponse({ status: 403, description: 'Forbidden - Insufficient permissions' })
async create(@Body() dto: CreateCampusEventDto, @Req() req: any) {
const userId = req.user.userId || req.user.id;
const roles = this.extractRoles(req.user);
return this.eventsService.create(dto, userId, roles);
}
@Put(':id')
@Roles(RoleName.ADMIN, RoleName.IT_ADMIN, RoleName.DEPARTMENT_HEAD)
@ApiOperation({
summary: 'Update campus event',
description: `
## Update Campus Event
Updates an existing campus event.
### Access Control
- **Authentication Required**: β
Yes (Bearer Token)
- **Roles**: ADMIN, IT_ADMIN, DEPARTMENT_HEAD, Event Organizer
### Permissions
- **Admins/IT_Admins**: Can update any event
- **Department Heads**: Can update department events
- **Event Organizer**: Can update own events
`,
})
@ApiParam({ name: 'id', description: 'Campus event ID', type: Number, example: 1 })
@ApiBody({ type: UpdateCampusEventDto })
@ApiResponse({ status: 200, description: 'Campus event updated successfully' })
@ApiResponse({ status: 404, description: 'Event not found' })
@ApiResponse({ status: 403, description: 'Forbidden' })
async update(
@Param('id', ParseIntPipe) id: number,
@Body() dto: UpdateCampusEventDto,
@Req() req: any,
) {
const userId = req.user.userId || req.user.id;
const roles = this.extractRoles(req.user);
return this.eventsService.update(id, dto, userId, roles);
}
@Delete(':id')
@Roles(RoleName.ADMIN, RoleName.IT_ADMIN, RoleName.DEPARTMENT_HEAD)
@HttpCode(HttpStatus.OK)
@ApiOperation({
summary: 'Delete campus event',
description: `
## Delete Campus Event
Deletes a campus event from the system.
### Access Control
- **Authentication Required**: β
Yes (Bearer Token)
- **Roles**: ADMIN, IT_ADMIN, Event Organizer
### Permissions
- **Admins/IT_Admins**: Can delete any event
- **Event Organizer**: Can delete own events
`,
})
@ApiParam({ name: 'id', description: 'Campus event ID', type: Number, example: 1 })
@ApiResponse({ status: 200, description: 'Campus event deleted successfully' })
@ApiResponse({ status: 404, description: 'Event not found' })
@ApiResponse({ status: 403, description: 'Forbidden' })
async delete(@Param('id', ParseIntPipe) id: number, @Req() req: any) {
const userId = req.user.userId || req.user.id;
const roles = this.extractRoles(req.user);
return this.eventsService.delete(id, userId, roles);
}
@Post(':id/register')
@HttpCode(HttpStatus.OK)
@ApiOperation({
summary: 'Register for campus event',
description: `
## Register for Campus Event
Registers the current user for an event that requires registration.
### Access Control
- **Authentication Required**: β
Yes (Bearer Token)
- **Roles**: ALL
### Validation
- Event must require registration
- Event must be published
- User cannot be already registered
- Event must have available capacity (if max attendees set)
`,
})
@ApiParam({ name: 'id', description: 'Campus event ID', type: Number, example: 1 })
@ApiBody({ type: RegisterCampusEventDto })
@ApiResponse({ status: 200, description: 'Successfully registered for event' })
@ApiResponse({ status: 400, description: 'Bad request - Already registered or event full' })
@ApiResponse({ status: 404, description: 'Event not found' })
async register(
@Param('id', ParseIntPipe) id: number,
@Body() dto: RegisterCampusEventDto,
@Req() req: any,
) {
const userId = req.user.userId || req.user.id;
return this.eventsService.register(id, dto, userId);
}
@Delete(':id/register')
@HttpCode(HttpStatus.OK)
@ApiOperation({
summary: 'Unregister from campus event',
description: `
## Unregister from Campus Event
Cancels the current user's registration for an event.
### Access Control
- **Authentication Required**: β
Yes (Bearer Token)
- **Roles**: ALL
`,
})
@ApiParam({ name: 'id', description: 'Campus event ID', type: Number, example: 1 })
@ApiResponse({ status: 200, description: 'Successfully unregistered from event' })
@ApiResponse({ status: 404, description: 'Registration not found' })
@ApiResponse({ status: 400, description: 'Registration already cancelled' })
async unregister(@Param('id', ParseIntPipe) id: number, @Req() req: any) {
const userId = req.user.userId || req.user.id;
return this.eventsService.unregister(id, userId);
}
@Get(':id/registrations')
@Roles(RoleName.ADMIN, RoleName.IT_ADMIN, RoleName.DEPARTMENT_HEAD)
@ApiOperation({
summary: 'Get event registrations',
description: `
## Get Event Registrations
Returns list of registrations for an event.
### Access Control
- **Authentication Required**: β
Yes (Bearer Token)
- **Roles**: ADMIN, IT_ADMIN, Event Organizer
### Response Includes
- Event details
- List of registrations with user info
- Summary statistics (registered, attended, cancelled, no_show)
`,
})
@ApiParam({ name: 'id', description: 'Campus event ID', type: Number, example: 1 })
@ApiResponse({ status: 200, description: 'List of registrations' })
@ApiResponse({ status: 404, description: 'Event not found' })
@ApiResponse({ status: 403, description: 'Forbidden' })
async getRegistrations(@Param('id', ParseIntPipe) id: number, @Req() req: any) {
const userId = req.user.userId || req.user.id;
const roles = this.extractRoles(req.user);
return this.eventsService.getRegistrations(id, userId, roles);
}
private extractRoles(user: any): string[] {
if (Array.isArray(user.roles)) {
return user.roles.map((r: any) => (typeof r === 'string' ? r : r.name || r.roleName));
}
return [];
}
}
|