diff --git a/shatter-backend/docs/API_REFERENCE.md b/shatter-backend/docs/API_REFERENCE.md index 813e1cc..41d4dcd 100644 --- a/shatter-backend/docs/API_REFERENCE.md +++ b/shatter-backend/docs/API_REFERENCE.md @@ -463,6 +463,8 @@ Update a user's profile. Users can only update their own profile. | `bio` | string | | | `profilePhoto` | string | URL | | `socialLinks` | object | `{ linkedin?, github?, other? }` | +| `organization` | string | Where the user works/studies | +| `title` | string | Job title or role | **Success Response (200):** @@ -755,8 +757,10 @@ Join an event as a guest (no account required). | `socialLinks.linkedin` | string | No | LinkedIn URL | | `socialLinks.github` | string | No | GitHub URL | | `socialLinks.other` | string | No | Other URL | +| `organization` | string | No* | Where the guest works/studies | +| `title` | string | No | Job title or role | -\* At least one contact method is required: either `email` or at least one non-empty field in `socialLinks`. +\* At least one of the following is required: `email`, a non-empty field in `socialLinks`, or `organization`. **Success Response (200):** @@ -779,14 +783,14 @@ Join an event as a guest (no account required). | Status | Error | |--------|-------| | 400 | `"Missing fields: guest name and eventId are required"` | -| 400 | `"At least one contact method is required (email or a social link)"` | +| 400 | `"At least one contact method (email or social link) or organization is required"` | | 400 | `"Invalid email format"` | | 400 | `"Event is full"` | | 404 | `"Event not found"` | | 409 | `"A user with this email already exists"` | **Special Behavior:** -- Creates a guest User (`authProvider: 'guest'`) with the provided contact info (email and/or social links) +- Creates a guest User (`authProvider: 'guest'`) with the provided contact info (email, social links, and/or organization) - If the display name is already taken in the event, a `#XXX` suffix is automatically appended (e.g., `John` becomes `John#472`). The response `participant.name` reflects the final display name, and the guest User's name is updated to match. - Returns a JWT so the guest can make authenticated requests - Guest can later upgrade to a full account via `PUT /api/users/:userId` @@ -1259,7 +1263,8 @@ curl -X POST http://localhost:4000/api/events//join/user \ curl -X POST http://localhost:4000/api/events//join/guest \ -H "Content-Type: application/json" \ -d '{ - "name": "Guest User" + "name": "Guest User", + "email": "guest@example.com" }' ``` diff --git a/shatter-backend/docs/DATABASE_SCHEMA.md b/shatter-backend/docs/DATABASE_SCHEMA.md index a7ba2cd..21d9dc6 100644 --- a/shatter-backend/docs/DATABASE_SCHEMA.md +++ b/shatter-backend/docs/DATABASE_SCHEMA.md @@ -63,6 +63,8 @@ | `passwordHash` | String | No | — | `select: false` — excluded from queries by default | | `linkedinId` | String | No | — | Unique (sparse) | | `linkedinUrl` | String | No | — | Unique (sparse) | +| `organization` | String | No | — | Trimmed | +| `title` | String | No | — | Trimmed | | `bio` | String | No | — | Trimmed | | `profilePhoto` | String | No | — | | | `socialLinks` | Object | No | — | `{ linkedin?: String, github?: String, other?: String }` | diff --git a/shatter-backend/src/controllers/event_controller.ts b/shatter-backend/src/controllers/event_controller.ts index 0e7f7d3..1d9cf9c 100644 --- a/shatter-backend/src/controllers/event_controller.ts +++ b/shatter-backend/src/controllers/event_controller.ts @@ -272,10 +272,12 @@ export async function joinEventAsUser(req: Request, res: Response) { */ export async function joinEventAsGuest(req: Request, res: Response) { try { - const { name, email, socialLinks } = req.body as { + const { name, email, socialLinks, organization, title } = req.body as { name?: string; email?: string; socialLinks?: { linkedin?: string; github?: string; other?: string }; + organization?: string; + title?: string; }; const { eventId } = req.params; @@ -286,18 +288,19 @@ export async function joinEventAsGuest(req: Request, res: Response) { }); } - // Require at least one contact method + // Require at least one contact method or organization const hasEmail = email && email.trim(); const hasSocialLink = socialLinks && ( socialLinks.linkedin?.trim() || socialLinks.github?.trim() || socialLinks.other?.trim() ); + const hasOrganization = organization && organization.trim(); - if (!hasEmail && !hasSocialLink) { + if (!hasEmail && !hasSocialLink && !hasOrganization) { return res.status(400).json({ success: false, - msg: "At least one contact method is required (email or a social link)", + msg: "At least one contact method (email or social link) or organization is required", }); } @@ -325,6 +328,8 @@ export async function joinEventAsGuest(req: Request, res: Response) { authProvider: 'guest', ...(hasEmail && { email: email.toLowerCase().trim() }), ...(hasSocialLink && { socialLinks }), + ...(hasOrganization && { organization: organization.trim() }), + ...(title && title.trim() && { title: title.trim() }), }); const userId = user._id as Types.ObjectId; diff --git a/shatter-backend/src/controllers/user_controller.ts b/shatter-backend/src/controllers/user_controller.ts index a9a73fa..1c2115a 100644 --- a/shatter-backend/src/controllers/user_controller.ts +++ b/shatter-backend/src/controllers/user_controller.ts @@ -114,13 +114,15 @@ export const updateUser = async (req: Request, res: Response) => { return res.status(403).json({ success: false, error: "You can only update your own profile" }); } - const { name, email, password, bio, profilePhoto, socialLinks } = req.body as { + const { name, email, password, bio, profilePhoto, socialLinks, organization, title } = req.body as { name?: string; email?: string; password?: string; bio?: string; profilePhoto?: string; socialLinks?: { linkedin?: string; github?: string; other?: string }; + organization?: string; + title?: string; }; const updateFields: Record = {}; @@ -161,6 +163,8 @@ export const updateUser = async (req: Request, res: Response) => { if (bio !== undefined) updateFields.bio = bio; if (profilePhoto !== undefined) updateFields.profilePhoto = profilePhoto; if (socialLinks !== undefined) updateFields.socialLinks = socialLinks; + if (organization !== undefined) updateFields.organization = organization; + if (title !== undefined) updateFields.title = title; if (Object.keys(updateFields).length === 0) { return res.status(400).json({ success: false, error: "No fields to update" }); diff --git a/shatter-backend/src/models/user_model.ts b/shatter-backend/src/models/user_model.ts index 78e76c9..3d4bb1c 100644 --- a/shatter-backend/src/models/user_model.ts +++ b/shatter-backend/src/models/user_model.ts @@ -12,6 +12,8 @@ export interface IUser { passwordHash?: string; linkedinId?: string; linkedinUrl?: string; + organization?: string; + title?: string; bio?: string; profilePhoto?: string; socialLinks?: { @@ -65,6 +67,14 @@ const UserSchema = new Schema( unique: true, sparse: true, }, + organization: { + type: String, + trim: true, + }, + title: { + type: String, + trim: true, + }, bio: { type: String, trim: true,