500 lines
23 KiB
Markdown
500 lines
23 KiB
Markdown
|
|
# Round: Mentoring (Finalist Collaboration Layer)
|
|||
|
|
|
|||
|
|
## 1. Purpose & Position in Flow
|
|||
|
|
|
|||
|
|
The MENTORING round is **not a judging stage** — it is a collaboration layer that activates between Jury 2 finalist selection and the Live Finals. It provides finalist teams who requested mentoring with a private workspace to refine their submissions with guidance from an assigned mentor.
|
|||
|
|
|
|||
|
|
| Aspect | Detail |
|
|||
|
|
|--------|--------|
|
|||
|
|
| Position | Round 6 (after Jury 2, before Live Finals) |
|
|||
|
|
| Participants | Finalist teams + assigned mentors |
|
|||
|
|
| Duration | Configurable (typically 2-4 weeks) |
|
|||
|
|
| Output | Better-prepared finalist submissions; some mentoring files promoted to official submissions |
|
|||
|
|
|
|||
|
|
### Who Gets Mentoring
|
|||
|
|
|
|||
|
|
- Only projects that have `Project.wantsMentorship = true` AND have advanced to finalist status (ProjectRoundState PASSED in the Jury 2 round)
|
|||
|
|
- Admin can override: assign mentoring to projects that didn't request it, or skip projects that did
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. Data Model
|
|||
|
|
|
|||
|
|
### Round Record
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Round {
|
|||
|
|
id: "round-mentoring"
|
|||
|
|
competitionId: "comp-2026"
|
|||
|
|
name: "Finalist Mentoring"
|
|||
|
|
roundType: MENTORING
|
|||
|
|
status: ROUND_DRAFT → ROUND_ACTIVE → ROUND_CLOSED
|
|||
|
|
sortOrder: 5
|
|||
|
|
windowOpenAt: "2026-06-01" // Mentoring period start
|
|||
|
|
windowCloseAt: "2026-06-30" // Mentoring period end
|
|||
|
|
juryGroupId: null // No jury for mentoring
|
|||
|
|
submissionWindowId: null // Mentoring doesn't collect formal submissions
|
|||
|
|
configJson: { ...MentoringConfig }
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### MentoringConfig
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
type MentoringConfig = {
|
|||
|
|
// Who gets mentoring
|
|||
|
|
eligibility: "all_advancing" | "requested_only"
|
|||
|
|
// all_advancing: Every finalist gets a mentor
|
|||
|
|
// requested_only: Only projects with wantsMentorship=true
|
|||
|
|
|
|||
|
|
// Workspace features
|
|||
|
|
chatEnabled: boolean // Bidirectional messaging (default: true)
|
|||
|
|
fileUploadEnabled: boolean // Mentor + team can upload files (default: true)
|
|||
|
|
fileCommentsEnabled: boolean // Threaded comments on files (default: true)
|
|||
|
|
filePromotionEnabled: boolean // Promote workspace file to official submission (default: true)
|
|||
|
|
|
|||
|
|
// Promotion target
|
|||
|
|
promotionTargetWindowId: string | null
|
|||
|
|
// Which SubmissionWindow promoted files go to
|
|||
|
|
// Usually the most recent window (Round 2 docs)
|
|||
|
|
// If null, promotion creates files without a window (admin must assign)
|
|||
|
|
|
|||
|
|
// Auto-assignment
|
|||
|
|
autoAssignMentors: boolean // Use AI/algorithm to assign (default: false)
|
|||
|
|
maxProjectsPerMentor: number // Mentor workload cap (default: 3)
|
|||
|
|
|
|||
|
|
// Notifications
|
|||
|
|
notifyTeamsOnOpen: boolean // Email teams when mentoring opens (default: true)
|
|||
|
|
notifyMentorsOnAssign: boolean // Email mentors when assigned (default: true)
|
|||
|
|
reminderBeforeClose: number[] // Days before close to remind (default: [7, 3, 1])
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Related Models
|
|||
|
|
|
|||
|
|
| Model | Purpose |
|
|||
|
|
|-------|---------|
|
|||
|
|
| `MentorAssignment` | Links mentor to project (existing, enhanced) |
|
|||
|
|
| `MentorMessage` | Chat messages between mentor and team (existing) |
|
|||
|
|
| `MentorNote` | Mentor's private notes (existing) |
|
|||
|
|
| `MentorFile` | **NEW** — Files uploaded in workspace |
|
|||
|
|
| `MentorFileComment` | **NEW** — Threaded comments on files |
|
|||
|
|
| `ProjectFile` | Target for file promotion |
|
|||
|
|
| `SubmissionFileRequirement` | Requirement slot that promoted file fills |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. Mentor Assignment
|
|||
|
|
|
|||
|
|
### 3.1 Assignment Methods
|
|||
|
|
|
|||
|
|
| Method | Description |
|
|||
|
|
|--------|-------------|
|
|||
|
|
| `MANUAL` | Admin picks mentor for each project |
|
|||
|
|
| `AI_SUGGESTED` | AI recommends matches, admin approves |
|
|||
|
|
| `AI_AUTO` | AI auto-assigns, admin can override |
|
|||
|
|
| `ALGORITHM` | Round-robin or expertise-matching algorithm |
|
|||
|
|
|
|||
|
|
### 3.2 Assignment Criteria
|
|||
|
|
|
|||
|
|
The existing `mentor-matching.ts` service evaluates:
|
|||
|
|
- **Expertise overlap** — mentor's tags vs project's tags/category
|
|||
|
|
- **Country/region diversity** — avoid same-country bias
|
|||
|
|
- **Workload balance** — distribute evenly across mentors
|
|||
|
|
- **Language** — match if language preferences exist
|
|||
|
|
|
|||
|
|
### 3.3 Assignment Flow
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
1. MENTORING round opens (status → ROUND_ACTIVE)
|
|||
|
|
2. System identifies eligible projects:
|
|||
|
|
- All finalists (if eligibility = "all_advancing")
|
|||
|
|
- Only finalists with wantsMentorship (if "requested_only")
|
|||
|
|
3. For each eligible project without a mentor:
|
|||
|
|
a. If autoAssignMentors: Run AI/algorithm assignment
|
|||
|
|
b. Else: Flag as "needs mentor" in admin dashboard
|
|||
|
|
4. Admin reviews assignments, can:
|
|||
|
|
- Accept suggestions
|
|||
|
|
- Reassign mentors
|
|||
|
|
- Skip projects (no mentoring needed)
|
|||
|
|
5. Assigned mentors receive email notification
|
|||
|
|
6. Workspace becomes active for mentor+team
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.4 Workspace Activation
|
|||
|
|
|
|||
|
|
When a mentor is assigned and the MENTORING round is ROUND_ACTIVE:
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// MentorAssignment is updated:
|
|||
|
|
{
|
|||
|
|
workspaceEnabled: true,
|
|||
|
|
workspaceOpenAt: round.windowOpenAt,
|
|||
|
|
workspaceCloseAt: round.windowCloseAt,
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The workspace is accessible from:
|
|||
|
|
- **Mentor dashboard** → "My Projects" → select project → Workspace tab
|
|||
|
|
- **Applicant dashboard** → "Mentor" section → Workspace tab
|
|||
|
|
- **Admin** → can view any workspace at any time
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. Workspace Features
|
|||
|
|
|
|||
|
|
### 4.1 Messaging (Chat)
|
|||
|
|
|
|||
|
|
Bidirectional chat between mentor and team members:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌────────────────────────────────────────────────┐
|
|||
|
|
│ Mentor Workspace — OceanClean AI │
|
|||
|
|
│ ──────────────────────────────────────────── │
|
|||
|
|
│ [💬 Chat] [📁 Files] [📋 Milestones] │
|
|||
|
|
│ │
|
|||
|
|
│ ┌────────────────────────────────────────┐ │
|
|||
|
|
│ │ Dr. Martin (Mentor) Apr 5, 10:30│ │
|
|||
|
|
│ │ Welcome! I've reviewed your business │ │
|
|||
|
|
│ │ plan. Let's work on the financial │ │
|
|||
|
|
│ │ projections section. │ │
|
|||
|
|
│ │ │ │
|
|||
|
|
│ │ Sarah (Team Lead) Apr 5, 14:15│ │
|
|||
|
|
│ │ Thank you! We've uploaded a revised │ │
|
|||
|
|
│ │ version. See the Files tab. │ │
|
|||
|
|
│ │ │ │
|
|||
|
|
│ │ Dr. Martin (Mentor) Apr 6, 09:00│ │
|
|||
|
|
│ │ Great improvement! I've left comments │ │
|
|||
|
|
│ │ on the file. One more round should do. │ │
|
|||
|
|
│ └────────────────────────────────────────┘ │
|
|||
|
|
│ │
|
|||
|
|
│ [Type a message... ] [Send] │
|
|||
|
|
└────────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Implementation:**
|
|||
|
|
- Uses existing `MentorMessage` model
|
|||
|
|
- Messages auto-marked as read when the chat is viewed
|
|||
|
|
- Real-time updates via polling (every 10s) or WebSocket if available
|
|||
|
|
- Both mentor and any team member can send messages
|
|||
|
|
|
|||
|
|
### 4.2 File Upload & Comments
|
|||
|
|
|
|||
|
|
The core new feature: a private file space with threaded discussion.
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌────────────────────────────────────────────────┐
|
|||
|
|
│ [💬 Chat] [📁 Files] [📋 Milestones] │
|
|||
|
|
│ │
|
|||
|
|
│ ┌── Workspace Files ───────────────────────┐ │
|
|||
|
|
│ │ │ │
|
|||
|
|
│ │ 📄 Business Plan v2.pdf │ │
|
|||
|
|
│ │ Uploaded by Sarah (Team) · Apr 5 │ │
|
|||
|
|
│ │ 💬 3 comments │ │
|
|||
|
|
│ │ [Download] [Comment] [Promote →] │ │
|
|||
|
|
│ │ │ │
|
|||
|
|
│ │ 📄 Financial Model.xlsx │ │
|
|||
|
|
│ │ Uploaded by Dr. Martin (Mentor) · Apr 6│ │
|
|||
|
|
│ │ 💬 1 comment │ │
|
|||
|
|
│ │ [Download] [Comment] │ │
|
|||
|
|
│ │ │ │
|
|||
|
|
│ │ 📄 Pitch Deck Draft.pptx │ │
|
|||
|
|
│ │ Uploaded by Sarah (Team) · Apr 8 │ │
|
|||
|
|
│ │ ✅ Promoted → "Presentation" slot │ │
|
|||
|
|
│ │ [Download] [View Comments] │ │
|
|||
|
|
│ │ │ │
|
|||
|
|
│ └──────────────────────────────────────────┘ │
|
|||
|
|
│ │
|
|||
|
|
│ [📤 Upload File] │
|
|||
|
|
└────────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**File Upload Flow:**
|
|||
|
|
1. User (mentor or team member) clicks "Upload File"
|
|||
|
|
2. Client calls `mentor.getWorkspaceUploadUrl(mentorAssignmentId, fileName, mimeType)`
|
|||
|
|
3. Server generates MinIO pre-signed PUT URL
|
|||
|
|
4. Client uploads directly to MinIO
|
|||
|
|
5. Client calls `mentor.saveWorkspaceFile(mentorAssignmentId, fileName, mimeType, size, bucket, objectKey, description)`
|
|||
|
|
6. Server creates `MentorFile` record
|
|||
|
|
|
|||
|
|
**File Comments:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌── Comments on: Business Plan v2.pdf ──────────┐
|
|||
|
|
│ │
|
|||
|
|
│ Dr. Martin (Mentor) · Apr 5, 16:00 │
|
|||
|
|
│ Section 3.2 needs stronger market analysis. │
|
|||
|
|
│ Consider adding competitor comparisons. │
|
|||
|
|
│ └─ Sarah (Team) · Apr 5, 18:30 │
|
|||
|
|
│ Good point — we'll add a competitive │
|
|||
|
|
│ landscape section. See updated version. │
|
|||
|
|
│ │
|
|||
|
|
│ Dr. Martin (Mentor) · Apr 6, 10:00 │
|
|||
|
|
│ Revenue projections look much better now. │
|
|||
|
|
│ Ready for promotion to official submission? │
|
|||
|
|
│ └─ Sarah (Team) · Apr 6, 11:00 │
|
|||
|
|
│ Yes, let's promote it! │
|
|||
|
|
│ │
|
|||
|
|
│ [Add comment... ] [Post] │
|
|||
|
|
└────────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Implementation:**
|
|||
|
|
- `MentorFileComment` with `parentCommentId` for threading
|
|||
|
|
- Both mentor and team members can comment
|
|||
|
|
- Admin can view all comments
|
|||
|
|
- Comments are timestamped and attributed
|
|||
|
|
|
|||
|
|
### 4.3 File Promotion to Official Submission
|
|||
|
|
|
|||
|
|
The key feature: converting a private mentoring file into an official submission document.
|
|||
|
|
|
|||
|
|
**Promotion Flow:**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
1. Team member (or admin) clicks "Promote →" on a workspace file
|
|||
|
|
2. Dialog appears:
|
|||
|
|
┌────────────────────────────────────────┐
|
|||
|
|
│ Promote File to Official Submission │
|
|||
|
|
│ │
|
|||
|
|
│ File: Business Plan v2.pdf │
|
|||
|
|
│ │
|
|||
|
|
│ Target submission window: │
|
|||
|
|
│ [Round 2 Docs ▾] │
|
|||
|
|
│ │
|
|||
|
|
│ Replaces requirement: │
|
|||
|
|
│ [Business Plan ▾] │
|
|||
|
|
│ │
|
|||
|
|
│ ⚠ This will replace the current │
|
|||
|
|
│ "Business Plan" file for this project. │
|
|||
|
|
│ │
|
|||
|
|
│ [Cancel] [Promote & Replace] │
|
|||
|
|
└────────────────────────────────────────┘
|
|||
|
|
|
|||
|
|
3. On confirmation:
|
|||
|
|
a. System creates a new ProjectFile record:
|
|||
|
|
- projectId: project's ID
|
|||
|
|
- submissionWindowId: selected window
|
|||
|
|
- requirementId: selected requirement slot
|
|||
|
|
- fileName, mimeType, size: copied from MentorFile
|
|||
|
|
- bucket, objectKey: SAME as MentorFile (no file duplication)
|
|||
|
|
- version: incremented from previous file in slot
|
|||
|
|
b. Previous file in that slot gets `replacedById` set to new file
|
|||
|
|
c. MentorFile updated:
|
|||
|
|
- isPromoted: true
|
|||
|
|
- promotedToFileId: new ProjectFile ID
|
|||
|
|
- promotedAt: now
|
|||
|
|
- promotedByUserId: actor ID
|
|||
|
|
d. Audit log entry created:
|
|||
|
|
- action: "MENTOR_FILE_PROMOTED"
|
|||
|
|
- details: { mentorFileId, projectFileId, submissionWindowId, requirementId, replacedFileId }
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Key Rules:**
|
|||
|
|
- Only files in **active** mentoring workspaces can be promoted
|
|||
|
|
- Promotion **replaces** the existing file for that requirement slot (per user's decision)
|
|||
|
|
- The MinIO object is **not duplicated** — both MentorFile and ProjectFile point to the same objectKey
|
|||
|
|
- Once promoted, the MentorFile shows a "Promoted" badge and the promote button is disabled
|
|||
|
|
- Admin can un-promote (revert) if needed, which deletes the ProjectFile and resets MentorFile flags
|
|||
|
|
- Promotion is audited with full provenance chain
|
|||
|
|
|
|||
|
|
**Who Can Promote:**
|
|||
|
|
- Team lead (Project.submittedByUserId or TeamMember.role = LEAD)
|
|||
|
|
- Admin (always)
|
|||
|
|
- Mentor (only if `MentoringConfig.mentorCanPromote` is true — default false for safety)
|
|||
|
|
|
|||
|
|
### 4.4 Privacy Model
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Visibility Matrix:
|
|||
|
|
┌──────────────────┬────────┬──────────┬───────┬──────┐
|
|||
|
|
│ Content │ Mentor │ Team │ Admin │ Jury │
|
|||
|
|
├──────────────────┼────────┼──────────┼───────┼──────┤
|
|||
|
|
│ Chat messages │ ✅ │ ✅ │ ✅ │ ❌ │
|
|||
|
|
│ Workspace files │ ✅ │ ✅ │ ✅ │ ❌ │
|
|||
|
|
│ File comments │ ✅ │ ✅ │ ✅ │ ❌ │
|
|||
|
|
│ Mentor notes │ ✅ │ ❌ │ ✅* │ ❌ │
|
|||
|
|
│ Promoted files │ ✅ │ ✅ │ ✅ │ ✅** │
|
|||
|
|
└──────────────────┴────────┴──────────┴───────┴──────┘
|
|||
|
|
|
|||
|
|
* Only if MentorNote.isVisibleToAdmin = true
|
|||
|
|
** Promoted files become official submissions visible to jury
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. Mentor Dashboard
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌──────────────────────────────────────────────────────────┐
|
|||
|
|
│ Mentor Dashboard │
|
|||
|
|
│ ─────────────────────────────────────────────────────── │
|
|||
|
|
│ │
|
|||
|
|
│ Mentoring Period: June 1 – June 30 │
|
|||
|
|
│ ⏱ 18 days remaining │
|
|||
|
|
│ │
|
|||
|
|
│ ┌─────────┐ ┌─────────┐ ┌──────────┐ │
|
|||
|
|
│ │ 3 │ │ 12 │ │ 5 │ │
|
|||
|
|
│ │ Teams │ │ Messages│ │ Files │ │
|
|||
|
|
│ └─────────┘ └─────────┘ └──────────┘ │
|
|||
|
|
│ │
|
|||
|
|
│ My Assigned Teams │
|
|||
|
|
│ ┌────────────────────────────────────────────────────┐ │
|
|||
|
|
│ │ OceanClean AI (Startup) │ │
|
|||
|
|
│ │ 💬 2 unread messages · 📁 3 files · Last: Apr 6 │ │
|
|||
|
|
│ │ [Open Workspace] │ │
|
|||
|
|
│ ├────────────────────────────────────────────────────┤ │
|
|||
|
|
│ │ Blue Carbon Hub (Concept) │ │
|
|||
|
|
│ │ 💬 0 unread · 📁 1 file · Last: Apr 4 │ │
|
|||
|
|
│ │ [Open Workspace] │ │
|
|||
|
|
│ ├────────────────────────────────────────────────────┤ │
|
|||
|
|
│ │ SeaWatch Monitor (Startup) │ │
|
|||
|
|
│ │ ⚠ No activity yet │ │
|
|||
|
|
│ │ [Open Workspace] │ │
|
|||
|
|
│ └────────────────────────────────────────────────────┘ │
|
|||
|
|
│ │
|
|||
|
|
│ Milestones │
|
|||
|
|
│ ┌────────────────────────────────────────────────────┐ │
|
|||
|
|
│ │ ☑ Initial review (3/3 teams) │ │
|
|||
|
|
│ │ ☐ Business plan feedback (1/3 teams) │ │
|
|||
|
|
│ │ ☐ Pitch deck review (0/3 teams) │ │
|
|||
|
|
│ └────────────────────────────────────────────────────┘ │
|
|||
|
|
└──────────────────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. Applicant Experience
|
|||
|
|
|
|||
|
|
On the applicant dashboard, a "Mentoring" section appears when mentoring is active:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌────────────────────────────────────────────────┐
|
|||
|
|
│ Your Mentor: Dr. Martin Duval │
|
|||
|
|
│ Expertise: Marine Biology, Sustainability │
|
|||
|
|
│ │
|
|||
|
|
│ Mentoring Period: June 1 – June 30 │
|
|||
|
|
│ ⏱ 18 days remaining │
|
|||
|
|
│ │
|
|||
|
|
│ [💬 Messages (2 unread)] │
|
|||
|
|
│ [📁 Workspace Files (3)] │
|
|||
|
|
│ [📋 Milestones] │
|
|||
|
|
└────────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Clicking "Workspace Files" opens the same workspace view as the mentor (with appropriate permissions).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. Admin Experience
|
|||
|
|
|
|||
|
|
Admin can:
|
|||
|
|
- **Assign/reassign mentors** via bulk or individual assignment
|
|||
|
|
- **View any workspace** (read-only or with full edit access)
|
|||
|
|
- **Promote files** on behalf of teams
|
|||
|
|
- **Track activity** — dashboard showing mentor engagement:
|
|||
|
|
- Messages sent per mentor
|
|||
|
|
- Files uploaded
|
|||
|
|
- Milestones completed
|
|||
|
|
- Last activity timestamp
|
|||
|
|
- **Extend/close mentoring window** per team or globally
|
|||
|
|
- **Export workspace data** for audit purposes
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 8. API — New and Modified Procedures
|
|||
|
|
|
|||
|
|
### New Procedures (mentor-workspace router)
|
|||
|
|
|
|||
|
|
| Procedure | Auth | Purpose |
|
|||
|
|
|-----------|------|---------|
|
|||
|
|
| `mentorWorkspace.getUploadUrl` | Mentor or Team | Get MinIO pre-signed URL for workspace upload |
|
|||
|
|
| `mentorWorkspace.saveFile` | Mentor or Team | Create MentorFile record after upload |
|
|||
|
|
| `mentorWorkspace.listFiles` | Mentor, Team, Admin | List workspace files with comment counts |
|
|||
|
|
| `mentorWorkspace.deleteFile` | Uploader or Admin | Delete workspace file |
|
|||
|
|
| `mentorWorkspace.getFileDownloadUrl` | Mentor, Team, Admin | Get MinIO pre-signed URL for download |
|
|||
|
|
| `mentorWorkspace.addComment` | Mentor, Team, Admin | Add comment to file (with optional parentCommentId) |
|
|||
|
|
| `mentorWorkspace.listComments` | Mentor, Team, Admin | Get threaded comments for a file |
|
|||
|
|
| `mentorWorkspace.deleteComment` | Author or Admin | Delete a comment |
|
|||
|
|
| `mentorWorkspace.promoteFile` | Team Lead or Admin | Promote workspace file to official submission |
|
|||
|
|
| `mentorWorkspace.unpromoteFile` | Admin only | Revert a promotion |
|
|||
|
|
| `mentorWorkspace.getWorkspaceStatus` | Any participant | Get workspace summary (file count, message count, etc.) |
|
|||
|
|
|
|||
|
|
### Modified Existing Procedures
|
|||
|
|
|
|||
|
|
| Procedure | Change |
|
|||
|
|
|-----------|--------|
|
|||
|
|
| `mentor.getMyProjects` | Include workspace status (file count, unread messages) |
|
|||
|
|
| `mentor.getProjectDetail` | Include MentorFile[] with comment counts |
|
|||
|
|
| `applicant.getMyDashboard` | Include mentor workspace summary if mentoring active |
|
|||
|
|
| `file.listByProjectForRound` | Promoted files visible to jury (via ProjectFile record) |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 9. Service: `mentor-workspace.ts`
|
|||
|
|
|
|||
|
|
### Key Functions
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// Upload handling
|
|||
|
|
async function getWorkspaceUploadUrl(
|
|||
|
|
mentorAssignmentId: string,
|
|||
|
|
fileName: string,
|
|||
|
|
mimeType: string,
|
|||
|
|
actorId: string,
|
|||
|
|
prisma: PrismaClient
|
|||
|
|
): Promise<{ uploadUrl: string; objectKey: string }>
|
|||
|
|
|
|||
|
|
// Save file metadata after upload
|
|||
|
|
async function saveWorkspaceFile(
|
|||
|
|
mentorAssignmentId: string,
|
|||
|
|
uploadedByUserId: string,
|
|||
|
|
file: { fileName, mimeType, size, bucket, objectKey },
|
|||
|
|
description: string | null,
|
|||
|
|
prisma: PrismaClient
|
|||
|
|
): Promise<MentorFile>
|
|||
|
|
|
|||
|
|
// Promote file to official submission
|
|||
|
|
async function promoteFileToSubmission(
|
|||
|
|
mentorFileId: string,
|
|||
|
|
submissionWindowId: string,
|
|||
|
|
requirementId: string | null,
|
|||
|
|
actorId: string,
|
|||
|
|
prisma: PrismaClient
|
|||
|
|
): Promise<{ mentorFile: MentorFile; projectFile: ProjectFile }>
|
|||
|
|
// Steps:
|
|||
|
|
// 1. Validate mentorFile exists, is not already promoted, workspace is active
|
|||
|
|
// 2. If requirementId: find existing ProjectFile for that requirement, set replacedById
|
|||
|
|
// 3. Create new ProjectFile (reusing same bucket/objectKey — no MinIO duplication)
|
|||
|
|
// 4. Update MentorFile: isPromoted=true, promotedToFileId, promotedAt, promotedByUserId
|
|||
|
|
// 5. Audit log with full provenance
|
|||
|
|
|
|||
|
|
// Revert promotion
|
|||
|
|
async function unpromoteFile(
|
|||
|
|
mentorFileId: string,
|
|||
|
|
actorId: string,
|
|||
|
|
prisma: PrismaClient
|
|||
|
|
): Promise<void>
|
|||
|
|
// Steps:
|
|||
|
|
// 1. Find the ProjectFile created by promotion
|
|||
|
|
// 2. If it replaced a previous file, restore that file's replacedById=null
|
|||
|
|
// 3. Delete the promoted ProjectFile
|
|||
|
|
// 4. Reset MentorFile flags
|
|||
|
|
// 5. Audit log
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 10. Edge Cases
|
|||
|
|
|
|||
|
|
| Scenario | Handling |
|
|||
|
|
|----------|----------|
|
|||
|
|
| Team doesn't want mentoring but admin assigns anyway | Assignment created; team sees mentor in dashboard |
|
|||
|
|
| Mentor goes inactive during period | Admin can reassign; previous workspace preserved |
|
|||
|
|
| File promoted then mentor period closes | Promoted file remains as official submission |
|
|||
|
|
| Team tries to promote file for a requirement that doesn't exist | Error — must select valid requirement or leave requirementId null |
|
|||
|
|
| Two files promoted to the same requirement slot | Second promotion replaces first (versioning) |
|
|||
|
|
| Mentoring file is larger than requirement maxSizeMB | Warning shown but promotion allowed (admin override implicit) |
|
|||
|
|
| Workspace closed but team needs one more upload | Admin can extend via round window or grant grace |
|
|||
|
|
| Promoted file deleted from workspace | ProjectFile remains (separate record); audit shows provenance |
|