Add equipment management to events

- Add equipmentProvided and requiredItems fields to Event model
- Update event creation form with equipment section
- Allow organizers to specify if equipment is provided or BYO
- Display required items list in event details
- Update database schema and run migration
This commit is contained in:
2025-11-18 19:43:22 +00:00
parent c1f8c00728
commit 4f06b1fe50
7 changed files with 202 additions and 3 deletions

View File

@@ -0,0 +1,3 @@
-- Add equipment fields to events table
ALTER TABLE `events` ADD COLUMN `equipment_provided` BOOLEAN DEFAULT false AFTER `photos`;
ALTER TABLE `events` ADD COLUMN `required_items` JSON AFTER `equipment_provided`;

View File

@@ -83,6 +83,8 @@ model Event {
participantCount Int? @default(0) @map("participant_count")
litterCollected Decimal? @map("litter_collected") @db.Decimal(10, 2)
photos Json?
equipmentProvided Boolean? @default(false) @map("equipment_provided")
requiredItems Json? @map("required_items")
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
updatedAt DateTime? @default(now()) @updatedAt @map("updated_at") @db.Timestamp(0)
registrations EventRegistration[]

View File

@@ -109,7 +109,7 @@ export const eventController = {
// Find or create user in database
const userId = await findOrCreateUser(clerkId);
const { title, description, latitude, longitude, locationName, scheduledDate, duration } = req.body;
const { title, description, latitude, longitude, locationName, scheduledDate, duration, equipmentProvided, requiredItems } = req.body;
// Validation
if (!title || !description || !latitude || !longitude || !locationName || !scheduledDate || !duration) {
@@ -129,6 +129,8 @@ export const eventController = {
locationName,
scheduledDate: new Date(scheduledDate),
duration: parseInt(duration),
equipmentProvided: equipmentProvided !== undefined ? Boolean(equipmentProvided) : false,
requiredItems: Array.isArray(requiredItems) ? requiredItems : [],
};
const event = await eventService.createEvent(eventData);

View File

@@ -21,6 +21,8 @@ interface CreateEventData {
locationName: string;
scheduledDate: Date;
duration: number;
equipmentProvided?: boolean;
requiredItems?: string[];
}
interface UpdateEventData {
@@ -31,6 +33,8 @@ interface UpdateEventData {
status?: 'upcoming' | 'completed' | 'cancelled';
litterCollected?: number;
photos?: string[];
equipmentProvided?: boolean;
requiredItems?: string[];
}
export const eventService = {
@@ -93,6 +97,8 @@ export const eventService = {
participantCount: event._count.registrations,
litterCollected: event.litterCollected ? parseFloat(event.litterCollected.toString()) : undefined,
photos: event.photos ? (Array.isArray(event.photos) ? event.photos : []) : undefined,
equipmentProvided: (event as any).equipmentProvided || false,
requiredItems: (event as any).requiredItems ? (Array.isArray((event as any).requiredItems) ? (event as any).requiredItems : []) : [],
createdAt: event.createdAt,
updatedAt: event.updatedAt,
}));
@@ -137,6 +143,8 @@ export const eventService = {
participantCount: event._count.registrations,
litterCollected: event.litterCollected ? parseFloat(event.litterCollected.toString()) : undefined,
photos: event.photos ? (Array.isArray(event.photos) ? event.photos : []) : undefined,
equipmentProvided: (event as any).equipmentProvided || false,
requiredItems: (event as any).requiredItems ? (Array.isArray((event as any).requiredItems) ? (event as any).requiredItems : []) : [],
createdAt: event.createdAt,
updatedAt: event.updatedAt,
};
@@ -145,16 +153,18 @@ export const eventService = {
async createEvent(data: CreateEventData) {
// Create point geometry for spatial queries
const locationPoint = `POINT(${data.longitude} ${data.latitude})`;
const equipmentProvided = data.equipmentProvided !== undefined ? data.equipmentProvided : false;
const requiredItems = data.requiredItems && data.requiredItems.length > 0 ? JSON.stringify(data.requiredItems) : null;
const event = await prisma.$executeRaw`
INSERT INTO events (
id, organizer_id, title, description, location, latitude, longitude,
location_name, scheduled_date, duration, status, participant_count
location_name, scheduled_date, duration, status, participant_count, equipment_provided, required_items
) VALUES (
UUID(), ${data.organizerId}, ${data.title}, ${data.description},
ST_GeomFromText(${locationPoint}, 4326),
${data.latitude}, ${data.longitude}, ${data.locationName},
${data.scheduledDate}, ${data.duration}, 'upcoming', 0
${data.scheduledDate}, ${data.duration}, 'upcoming', 0, ${equipmentProvided}, ${requiredItems}
)
`;
@@ -195,6 +205,8 @@ export const eventService = {
participantCount: createdEvent._count.registrations,
litterCollected: createdEvent.litterCollected ? parseFloat(createdEvent.litterCollected.toString()) : undefined,
photos: createdEvent.photos ? (Array.isArray(createdEvent.photos) ? createdEvent.photos : []) : undefined,
equipmentProvided: (createdEvent as any).equipmentProvided || false,
requiredItems: (createdEvent as any).requiredItems ? (Array.isArray((createdEvent as any).requiredItems) ? (createdEvent as any).requiredItems : []) : [],
createdAt: createdEvent.createdAt,
updatedAt: createdEvent.updatedAt,
};