Backend: - Add EventParticipant model to track user-event participation - Create database migration for event_participants table - Record participation when user joins event chat via Socket.IO - Update GET /api/events to include isJoined flag for current user - Sort events: joined events first, then by start date - Add authenticate middleware to GET /api/events Frontend: - Replace mock events with real API data from backend - Add loading and error states to EventsPage - Display "Joined" badge on events user has joined - Highlight joined events with colored border - Show "Open chat" vs "Join chat" button text - Auto-refresh events list when navigating back When users join an event chat, this is now recorded in the database. Joined events appear at the top of the list with visual indicators.
175 lines
6.2 KiB
Plaintext
175 lines
6.2 KiB
Plaintext
// Prisma schema for spotlight.cam
|
|
// Database: PostgreSQL 15
|
|
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
binaryTargets = ["native", "linux-musl-openssl-3.0.x"]
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
// Users table
|
|
model User {
|
|
id Int @id @default(autoincrement())
|
|
username String @unique @db.VarChar(50)
|
|
email String @unique @db.VarChar(255)
|
|
passwordHash String @map("password_hash") @db.VarChar(255)
|
|
|
|
// WSDC Integration (Phase 1.5)
|
|
firstName String? @map("first_name") @db.VarChar(100)
|
|
lastName String? @map("last_name") @db.VarChar(100)
|
|
wsdcId String? @unique @map("wsdc_id") @db.VarChar(20)
|
|
|
|
// Social Media Links
|
|
youtubeUrl String? @map("youtube_url") @db.VarChar(255)
|
|
instagramUrl String? @map("instagram_url") @db.VarChar(255)
|
|
facebookUrl String? @map("facebook_url") @db.VarChar(255)
|
|
tiktokUrl String? @map("tiktok_url") @db.VarChar(255)
|
|
|
|
// Location
|
|
country String? @db.VarChar(100)
|
|
city String? @db.VarChar(100)
|
|
|
|
// Email Verification (Phase 1.5)
|
|
emailVerified Boolean @default(false) @map("email_verified")
|
|
verificationToken String? @unique @map("verification_token") @db.VarChar(255)
|
|
verificationCode String? @map("verification_code") @db.VarChar(6)
|
|
verificationTokenExpiry DateTime? @map("verification_token_expiry")
|
|
|
|
// Password Reset (Phase 1.5)
|
|
resetToken String? @unique @map("reset_token") @db.VarChar(255)
|
|
resetTokenExpiry DateTime? @map("reset_token_expiry")
|
|
|
|
avatar String? @db.VarChar(255)
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
// Relations
|
|
messages Message[]
|
|
matchesAsUser1 Match[] @relation("MatchUser1")
|
|
matchesAsUser2 Match[] @relation("MatchUser2")
|
|
ratingsGiven Rating[] @relation("RaterRatings")
|
|
ratingsReceived Rating[] @relation("RatedRatings")
|
|
eventParticipants EventParticipant[]
|
|
|
|
@@map("users")
|
|
}
|
|
|
|
// Events table (dance events from worldsdc.com)
|
|
model Event {
|
|
id Int @id @default(autoincrement())
|
|
name String @db.VarChar(255)
|
|
location String @db.VarChar(255)
|
|
startDate DateTime @map("start_date") @db.Date
|
|
endDate DateTime @map("end_date") @db.Date
|
|
worldsdcId String? @unique @map("worldsdc_id") @db.VarChar(100)
|
|
participantsCount Int @default(0) @map("participants_count")
|
|
description String? @db.Text
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
// Relations
|
|
chatRooms ChatRoom[]
|
|
matches Match[]
|
|
participants EventParticipant[]
|
|
|
|
@@map("events")
|
|
}
|
|
|
|
// Chat rooms (event chat and private 1:1 chat)
|
|
model ChatRoom {
|
|
id Int @id @default(autoincrement())
|
|
eventId Int? @map("event_id")
|
|
type String @db.VarChar(20) // 'event' or 'private'
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
// Relations
|
|
event Event? @relation(fields: [eventId], references: [id])
|
|
messages Message[]
|
|
matches Match[]
|
|
|
|
@@map("chat_rooms")
|
|
}
|
|
|
|
// Messages (text messages and video links)
|
|
model Message {
|
|
id Int @id @default(autoincrement())
|
|
roomId Int @map("room_id")
|
|
userId Int @map("user_id")
|
|
content String @db.Text
|
|
type String @db.VarChar(20) // 'text', 'link', 'video'
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
// Relations
|
|
room ChatRoom @relation(fields: [roomId], references: [id], onDelete: Cascade)
|
|
user User @relation(fields: [userId], references: [id])
|
|
|
|
@@index([roomId])
|
|
@@index([createdAt])
|
|
@@map("messages")
|
|
}
|
|
|
|
// Matches (pairs of users for collaboration)
|
|
model Match {
|
|
id Int @id @default(autoincrement())
|
|
user1Id Int @map("user1_id")
|
|
user2Id Int @map("user2_id")
|
|
eventId Int @map("event_id")
|
|
roomId Int? @map("room_id")
|
|
status String @default("pending") @db.VarChar(20) // 'pending', 'accepted', 'completed'
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
// Relations
|
|
user1 User @relation("MatchUser1", fields: [user1Id], references: [id])
|
|
user2 User @relation("MatchUser2", fields: [user2Id], references: [id])
|
|
event Event @relation(fields: [eventId], references: [id])
|
|
room ChatRoom? @relation(fields: [roomId], references: [id])
|
|
ratings Rating[]
|
|
|
|
@@unique([user1Id, user2Id, eventId])
|
|
@@index([user1Id])
|
|
@@index([user2Id])
|
|
@@index([eventId])
|
|
@@map("matches")
|
|
}
|
|
|
|
// Ratings (user ratings after collaboration)
|
|
model Rating {
|
|
id Int @id @default(autoincrement())
|
|
matchId Int @map("match_id")
|
|
raterId Int @map("rater_id")
|
|
ratedId Int @map("rated_id")
|
|
score Int // 1-5
|
|
comment String? @db.Text
|
|
wouldCollaborateAgain Boolean @default(false) @map("would_collaborate_again")
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
// Relations
|
|
match Match @relation(fields: [matchId], references: [id])
|
|
rater User @relation("RaterRatings", fields: [raterId], references: [id])
|
|
rated User @relation("RatedRatings", fields: [ratedId], references: [id])
|
|
|
|
@@unique([matchId, raterId, ratedId])
|
|
@@index([ratedId])
|
|
@@map("ratings")
|
|
}
|
|
|
|
// Event participants (tracks which users joined which events)
|
|
model EventParticipant {
|
|
id Int @id @default(autoincrement())
|
|
userId Int @map("user_id")
|
|
eventId Int @map("event_id")
|
|
joinedAt DateTime @default(now()) @map("joined_at")
|
|
|
|
// Relations
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
event Event @relation(fields: [eventId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([userId, eventId])
|
|
@@index([userId])
|
|
@@index([eventId])
|
|
@@map("event_participants")
|
|
}
|