MOPC-App/docs/claude-architecture-redesign/18-mentor-ui.md

78 KiB
Raw Blame History

Mentor UI Redesign (Complete Workspace Experience)

1. Overview

Mentors are a first-class role in the MOPC platform. They guide finalist teams through the mentoring round (Round 6) — a collaboration layer between Jury 2 finalist selection and the Live Finals. The redesigned mentor experience provides a dedicated dashboard and full workspace for file collaboration, threaded comments, and file promotion to official submissions.

What Changes

Area Current State Redesigned State
Dashboard Basic list of assigned projects Rich dashboard with activity feed, status cards, metrics
Messaging Simple chat via MentorMessage model Enhanced messaging integrated into workspace
File Sharing None — teams upload to official submission only Full private workspace with file upload/download
Comments None Threaded file comments with @mentions
File Promotion None Promote workspace files to official submission slots
Milestones Basic MentorMilestone tracking Interactive milestone progress UI
Navigation /mentor and /mentor/projects Full IA with team-specific workspaces

Design Principles

  1. Privacy-first — Workspace is private (mentor + team + admin only; jury can't see it)
  2. No duplication — Promoted files reuse MinIO objects (no storage duplication)
  3. Provenance tracking — Full audit trail from workspace upload → promotion → submission
  4. Mobile-friendly — File review on tablets is a primary use case
  5. Integrated — Workspace is tightly integrated with existing submission system

2. Current Mentor Experience (Baseline)

Existing Components

File Purpose
src/components/layouts/mentor-nav.tsx Top navigation (Dashboard, My Projects, Resources)
src/components/shared/mentor-chat.tsx Chat component for mentor-team messaging
src/server/routers/mentor.ts tRPC procedures (assign, getSuggestions, etc.)
src/server/services/mentor-matching.ts AI-based mentor-project matching

Current Data Models

MentorAssignment {
  id, projectId, mentorId, method, assignedAt, assignedBy
  aiConfidenceScore, expertiseMatchScore, aiReasoning
  completionStatus, lastViewedAt
}

MentorMessage {
  id, mentorAssignmentId, senderId, message, createdAt, isRead
}

MentorNote {
  id, mentorAssignmentId, note, isVisibleToAdmin, createdAt
}

MentorMilestone {
  id, competitionId, name, description, sortOrder
}

MentorMilestoneCompletion {
  id, mentorAssignmentId, milestoneId, completedAt, notes
}

Current Mentor Procedures (src/server/routers/mentor.ts)

Procedure Purpose
getSuggestions Get AI-suggested mentors for a project
assign Manually assign mentor to project
unassign Remove mentor assignment
getMyAssignments List mentor's assigned projects
getAssignmentDetail Get detailed view of a single assignment
sendMessage Send message to team
getMessages Retrieve chat history
addNote Add private mentor note
getNotes Retrieve mentor's notes
completeMilestone Mark a milestone as complete
getMilestones Get milestone list + completion status

What's Missing

  • No file workspace
  • No threaded comments
  • No file promotion mechanism
  • No visual file preview
  • No mentor dashboard metrics
  • No admin monitoring of mentor activity
  • No file versioning in workspace
  • No mobile-optimized file review

3. Mentor Dashboard (Landing Page)

The mentor dashboard is the entry point for all mentoring activities. It provides an at-a-glance view of assigned teams, workspace activity, and upcoming deadlines.

Route

/mentor (dashboard)

ASCII Mockup

┌──────────────────────────────────────────────────────────────────────────┐
│ MOPC 2026 — Mentor Dashboard                           [🔔 3] [Profile ▾]│
├──────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│ Mentoring Period: June 1  June 30, 2026                                │
│ ⏱ 18 days remaining                                                     │
│                                                                          │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐               │
│ │ 3              │ │ 12             │ │ 5              │               │
│ │ Assigned Teams │ │ Unread Messages│ │ Files Reviewed │               │
│ └────────────────┘ └────────────────┘ └────────────────┘               │
│                                                                          │
│ ┌─ My Assigned Teams ───────────────────────────────────────────────┐   │
│ │                                                                    │   │
│ │ ┌────────────────────────────────────────────────────────────┐    │   │
│ │ │ 🚀 OceanClean AI — Startup                                │    │   │
│ │ │ Team Lead: Sarah Johnson · sjohnson@oceanclean.ai          │    │   │
│ │ │                                                            │    │   │
│ │ │ 💬 2 unread messages · 📁 3 files uploaded                │    │   │
│ │ │ 🔄 Last activity: 2 hours ago                              │    │   │
│ │ │                                                            │    │   │
│ │ │ Status: ✅ Active    Milestones: 2/4 completed            │    │   │
│ │ │                                                            │    │   │
│ │ │ [Open Workspace]                           [View Details]  │    │   │
│ │ └────────────────────────────────────────────────────────────┘    │   │
│ │                                                                    │   │
│ │ ┌────────────────────────────────────────────────────────────┐    │   │
│ │ │ 💡 Blue Carbon Hub — Business Concept                     │    │   │
│ │ │ Team Lead: Dr. Maria Santos · msantos@bluecarbonhub.org    │    │   │
│ │ │                                                            │    │   │
│ │ │ 💬 0 unread · 📁 1 file uploaded                          │    │   │
│ │ │ 🔄 Last activity: 2 days ago                               │    │   │
│ │ │                                                            │    │   │
│ │ │ Status: ⚠ Needs Attention    Milestones: 1/4 completed   │    │   │
│ │ │                                                            │    │   │
│ │ │ [Open Workspace]                           [View Details]  │    │   │
│ │ └────────────────────────────────────────────────────────────┘    │   │
│ │                                                                    │   │
│ │ ┌────────────────────────────────────────────────────────────┐    │   │
│ │ │ 🌊 SeaWatch Monitor — Startup                             │    │   │
│ │ │ Team Lead: John Chen · jchen@seawatch.io                   │    │   │
│ │ │                                                            │    │   │
│ │ │ 💬 0 unread · 📁 0 files                                  │    │   │
│ │ │ 🔄 Last activity: Never                                    │    │   │
│ │ │                                                            │    │   │
│ │ │ Status: ⚠ No Activity Yet    Milestones: 0/4 completed   │    │   │
│ │ │                                                            │    │   │
│ │ │ [Open Workspace]                           [View Details]  │    │   │
│ │ └────────────────────────────────────────────────────────────┘    │   │
│ │                                                                    │   │
│ └────────────────────────────────────────────────────────────────────┘   │
│                                                                          │
│ ┌─ Milestone Progress ───────────────────────────────────────────┐      │
│ │                                                                 │      │
│ │ ☑ Initial Review (3/3 teams completed)                         │      │
│ │ ├─ OceanClean AI: ✅ Jun 2                                     │      │
│ │ ├─ Blue Carbon Hub: ✅ Jun 3                                   │      │
│ │ └─ SeaWatch Monitor: ✅ Jun 4                                  │      │
│ │                                                                 │      │
│ │ ☐ Business Plan Feedback (1/3 teams completed)                 │      │
│ │ ├─ OceanClean AI: ✅ Jun 5                                     │      │
│ │ ├─ Blue Carbon Hub: ⏳ In progress                             │      │
│ │ └─ SeaWatch Monitor: ⏳ Not started                            │      │
│ │                                                                 │      │
│ │ ☐ Pitch Deck Review (0/3 teams)                                │      │
│ │ ☐ Final Submission Review (0/3 teams)                          │      │
│ │                                                                 │      │
│ └─────────────────────────────────────────────────────────────────┘      │
│                                                                          │
│ ┌─ Recent Activity ────────────────────────────────────────────┐         │
│ │                                                              │         │
│ │ 2 hours ago                                                  │         │
│ │ 📁 Sarah Johnson uploaded "Business Plan v3.pdf"             │         │
│ │    in OceanClean AI workspace                                │         │
│ │                                                              │         │
│ │ 5 hours ago                                                  │         │
│ │ 💬 You sent a message to Blue Carbon Hub                     │         │
│ │                                                              │         │
│ │ Yesterday                                                    │         │
│ │ ✅ You completed "Initial Review" for SeaWatch Monitor       │         │
│ │                                                              │         │
│ │ 2 days ago                                                   │         │
│ │ 📁 You uploaded "Financial Model Template.xlsx"              │         │
│ │    in OceanClean AI workspace                                │         │
│ │                                                              │         │
│ └──────────────────────────────────────────────────────────────┘         │
│                                                                          │
└──────────────────────────────────────────────────────────────────────────┘

Data Requirements

Team Cards:

// Query: mentor.getMyDashboard
{
  teams: [
    {
      projectId: string,
      projectTitle: string,
      projectCategory: "STARTUP" | "BUSINESS_CONCEPT",
      teamLead: { name, email },
      mentorAssignmentId: string,
      status: "active" | "needs_attention" | "no_activity" | "completed",
      unreadMessageCount: number,
      fileCount: number,
      lastActivity: Date | null,
      milestonesCompleted: number,
      milestonesTotal: number,
    }
  ],
  stats: {
    totalTeams: number,
    unreadMessages: number,
    filesReviewed: number,
    filesPromoted: number,
  },
  mentoring: {
    windowOpenAt: Date,
    windowCloseAt: Date,
    daysRemaining: number,
  }
}

Status Logic:

  • no_activity — lastActivity is null (team hasn't engaged)
  • needs_attention — lastActivity > 3 days ago OR unreadMessageCount > 0
  • active — Regular engagement
  • completed — All milestones done

Activity Feed:

// Query: mentor.getRecentActivity (limit: 10)
{
  activities: [
    {
      id: string,
      type: "FILE_UPLOADED" | "MESSAGE_SENT" | "MILESTONE_COMPLETED" | "FILE_PROMOTED",
      actor: { id, name },
      projectTitle: string,
      timestamp: Date,
      metadata: { fileName?, milestoneId? }
    }
  ]
}

Component Breakdown

MentorDashboard (page)
├─ MentorHeader (period info, countdown, stats cards)
├─ TeamCardList
│  └─ TeamCard (reusable, shows status + quick actions)
├─ MilestoneProgressSection
│  └─ MilestoneExpandableList (shows per-team breakdown)
└─ RecentActivityFeed
   └─ ActivityItem (icon + text + timestamp)

4. Workspace (Per-Team Private Collaboration Space)

The workspace is the heart of the mentor experience. It's a split-pane interface with files on the left and detail view on the right.

Route

/mentor/team/[projectId]
/mentor/team/[projectId]/files
/mentor/team/[projectId]/file/[fileId]
/mentor/team/[projectId]/chat
/mentor/team/[projectId]/milestones

ASCII Mockup — File List View

┌──────────────────────────────────────────────────────────────────────────┐
│ ← Back to Dashboard                                     [🔔 3] [Profile ▾]│
├──────────────────────────────────────────────────────────────────────────┤
│ OceanClean AI — Workspace                                                │
│ Team Lead: Sarah Johnson · sjohnson@oceanclean.ai                        │
│ Mentoring Period: June 1  June 30 · ⏱ 18 days remaining                │
├──────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│ [📁 Files] [💬 Chat (2)] [📋 Milestones (2/4)]                          │
│                                                                          │
│ ┌─────────────────────┬────────────────────────────────────────────────┐│
│ │ Files (5)           │ File Detail                                    ││
│ │ [📤 Upload File]    │                                                ││
│ │                     │ ┌────────────────────────────────────────────┐ ││
│ │ ───────────────────│ │ 📄 Business Plan v3.pdf                   │ ││
│ │                     │ │ Uploaded by Sarah Johnson (Team)          │ ││
│ │ 📄 Business Plan..  │ │ Jun 8, 2026 · 2.4 MB                       │ ││
│ │ Sarah · Jun 8       │ │                                            │ ││
│ │ 💬 4 comments       │ │ [📥 Download]  [💬 Comments (4)]           │ ││
│ │ [Open →]            │ │ [🚀 Promote to Submission]                 │ ││
│ │                     │ │                                            │ ││
│ │ 📊 Financial Mod..  │ │ ───────────────────────────────────────── │ ││
│ │ You · Jun 6         │ │                                            │ ││
│ │ 💬 2 comments       │ │ 💬 Comments (4)                            │ ││
│ │ [Open →]            │ │                                            │ ││
│ │                     │ │ Dr. Martin Duval (Mentor) · Jun 8, 14:30  │ ││
│ │ 📄 Market Analys..  │ │ Section 3.2 needs stronger market         │ ││
│ │ Sarah · Jun 5       │ │ analysis. Consider adding competitor      │ ││
│ │ 💬 1 comment        │ │ comparisons and market size estimates.    │ ││
│ │ [Open →]            │ │                                            │ ││
│ │                     │ │   └─ Sarah Johnson (Team) · Jun 8, 16:00  │ ││
│ │ 📄 Pitch Deck Dr..  │ │      Good point! We'll add a competitive  │ ││
│ │ Sarah · Jun 4       │ │      landscape section in the next ver.   │ ││
│ │ ✅ PROMOTED         │ │                                            │ ││
│ │ [View]              │ │ Dr. Martin Duval (Mentor) · Jun 9, 10:00  │ ││
│ │                     │ │ Financial projections look much better.   │ ││
│ │ 🎥 Video Pitch      │ │ Revenue model is solid. Ready to promote? │ ││
│ │ Sarah · Jun 3       │ │                                            │ ││
│ │ 💬 0 comments       │ │   └─ Sarah Johnson (Team) · Jun 9, 11:30  │ ││
│ │ [Open →]            │ │      Yes! Please promote to the Business  │ ││
│ │                     │ │      Plan requirement slot.                │ ││
│ │                     │ │                                            │ ││
│ │                     │ │ [Add comment...                         ] │ ││
│ │                     │ │ [Post Comment]                             │ ││
│ │                     │ └────────────────────────────────────────────┘ ││
│ │                     │                                                ││
│ └─────────────────────┴────────────────────────────────────────────────┘│
│                                                                          │
└──────────────────────────────────────────────────────────────────────────┘

Split-Pane Behavior

State Left Pane Right Pane
Default File list File upload instructions + quick stats
File selected File list (selected file highlighted) File detail (preview, comments, actions)
Mobile Stack vertically — file list on top, detail below when file selected

File List Item Structure

interface WorkspaceFile {
  id: string
  fileName: string
  uploadedBy: { id, name, role: "MENTOR" | "TEAM" }
  uploadedAt: Date
  size: number
  mimeType: string
  commentCount: number
  isPromoted: boolean
  promotedToSlot?: { requirementName: string, submissionWindowName: string }
}

File Actions

Action Visibility Behavior
Download Always Get MinIO pre-signed URL, trigger browser download
Comment Always Open comment thread in right pane
Promote Only if not already promoted Show promotion dialog (see section 5)
Delete Uploader or admin only Soft delete (mark as deleted, don't remove from MinIO)

5. File Review & Comments (Threaded Discussion)

Comment Thread UI

┌──────────────────────────────────────────────────────────────┐
│ 💬 Comments on: Business Plan v3.pdf                          │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│ Dr. Martin Duval (Mentor)                     Jun 8, 14:30  │
│ Section 3.2 needs stronger market analysis. Consider        │
│ adding competitor comparisons and market size estimates.     │
│                                                              │
│   └─ Sarah Johnson (Team)                    Jun 8, 16:00   │
│      Good point! We'll add a competitive landscape           │
│      section in the next version. @Dr. Martin would you      │
│      prefer a SWOT matrix or Porter's 5 Forces analysis?     │
│                                                              │
│        └─ Dr. Martin Duval (Mentor)          Jun 8, 17:00   │
│           Porter's 5 Forces would be stronger for this       │
│           type of technology play. Focus on threat of        │
│           substitutes and competitive rivalry.                │
│                                                              │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│ Dr. Martin Duval (Mentor)                     Jun 9, 10:00  │
│ Financial projections look much better. Revenue model is     │
│ solid. Ready to promote this to official submission?         │
│                                                              │
│   └─ Sarah Johnson (Team)                    Jun 9, 11:30   │
│      Yes! Please promote to the Business Plan slot.          │
│                                                              │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│ [Add comment...                                            ] │
│                                                              │
│ [@mention team member]                       [Post Comment] │
└──────────────────────────────────────────────────────────────┘

Comment Features

Threading:

  • Top-level comments (parentCommentId = null)
  • Nested replies (parentCommentId = parent's ID)
  • Maximum nesting depth: 3 levels (prevents deep threading hell)

@Mentions:

  • Typing @ triggers autocomplete of team members + mentor
  • Mentioned users get in-app notification
  • Mention is rendered as a link to user profile

Formatting:

  • Plain text only (no rich text editor — keep it simple)
  • Auto-linkify URLs
  • Preserve line breaks

Deletion:

  • Author can delete own comments within 1 hour of posting
  • Admin can delete any comment
  • Deleted comments show "[Comment deleted]" placeholder (preserve thread structure)

Comment API

Procedure Purpose
mentorWorkspace.addComment(fileId, content, parentCommentId?) Add comment
mentorWorkspace.listComments(fileId) Get threaded comments
mentorWorkspace.deleteComment(commentId) Delete comment

Comment Data Structure

interface MentorFileComment {
  id: string
  mentorFileId: string
  authorId: string
  author: { id, name, role }
  content: string
  parentCommentId: string | null
  replies: MentorFileComment[]  // Nested replies
  createdAt: Date
  updatedAt: Date
}

6. File Promotion (Workspace → Official Submission)

File promotion is the key value add of the mentor workspace. It allows refined documents to become official submissions without manual re-upload.

Promotion Flow

┌────────────────────────────────────────────────────────────────┐
│ Step 1: Mentor/Team clicks "Promote →" on a workspace file    │
└────────────────────────────────────────────────────────────────┘
                            ↓
┌────────────────────────────────────────────────────────────────┐
│ Step 2: Promotion Dialog Appears                               │
│                                                                │
│ ┌────────────────────────────────────────────────────────┐    │
│ │ Promote File to Official Submission                    │    │
│ │                                                        │    │
│ │ File: Business Plan v3.pdf                             │    │
│ │ Size: 2.4 MB                                           │    │
│ │                                                        │    │
│ │ Target Submission Window:                              │    │
│ │ ┌────────────────────────────────────────────────┐     │    │
│ │ │ Round 2 Docs (Semi-finalist Submissions) ▾    │     │    │
│ │ └────────────────────────────────────────────────┘     │    │
│ │                                                        │    │
│ │ Replaces Requirement Slot:                             │    │
│ │ ┌────────────────────────────────────────────────┐     │    │
│ │ │ Business Plan ▾                                │     │    │
│ │ └────────────────────────────────────────────────┘     │    │
│ │                                                        │    │
│ │ Current file in this slot:                             │    │
│ │ "Business Plan v2.pdf" uploaded Jun 3, 2026            │    │
│ │                                                        │    │
│ │ ⚠ This will replace the existing file. The old file   │    │
│ │ will remain in version history but won't be shown      │    │
│ │ to jurors.                                             │    │
│ │                                                        │    │
│ │ [Cancel]                    [Promote & Replace File]   │    │
│ └────────────────────────────────────────────────────────┘    │
└────────────────────────────────────────────────────────────────┘
                            ↓
┌────────────────────────────────────────────────────────────────┐
│ Step 3: Server Executes Promotion                              │
│                                                                │
│ 1. Validate:                                                   │
│    ✓ Workspace is active                                       │
│    ✓ File not already promoted                                │
│    ✓ Submission window exists and is accessible               │
│    ✓ User has permission (team lead or admin)                 │
│                                                                │
│ 2. Find existing ProjectFile for that requirement slot:        │
│    ProjectFile.where(projectId, requirementId)                 │
│                                                                │
│ 3. Create new ProjectFile:                                     │
│    {                                                           │
│      projectId: project.id,                                    │
│      submissionWindowId: selected window,                      │
│      requirementId: selected requirement,                      │
│      fileName: "Business Plan v3.pdf",                         │
│      mimeType: "application/pdf",                              │
│      size: 2.4MB,                                              │
│      bucket: same as MentorFile,                               │
│      objectKey: same as MentorFile,  // NO DUPLICATION        │
│      version: 3,  // increment from previous                  │
│    }                                                           │
│                                                                │
│ 4. Update old file (if exists):                                │
│    oldFile.replacedById = newFile.id                           │
│                                                                │
│ 5. Update MentorFile:                                          │
│    {                                                           │
│      isPromoted: true,                                         │
│      promotedToFileId: newFile.id,                             │
│      promotedAt: now,                                          │
│      promotedByUserId: actor.id,                               │
│    }                                                           │
│                                                                │
│ 6. Audit log entry:                                            │
│    {                                                           │
│      action: "MENTOR_FILE_PROMOTED",                           │
│      actorId: actor.id,                                        │
│      targetType: "PROJECT_FILE",                               │
│      targetId: newFile.id,                                     │
│      metadata: {                                               │
│        mentorFileId: mentorFile.id,                            │
│        submissionWindowId: window.id,                          │
│        requirementId: requirement.id,                          │
│        replacedFileId: oldFile?.id,                            │
│      }                                                         │
│    }                                                           │
└────────────────────────────────────────────────────────────────┘
                            ↓
┌────────────────────────────────────────────────────────────────┐
│ Step 4: UI Updates                                             │
│                                                                │
│ - MentorFile shows "✅ PROMOTED" badge                         │
│ - Promote button disabled                                      │
│ - Toast: "File promoted to Business Plan slot"                │
│ - ProjectFile now visible to jury in evaluation round          │
└────────────────────────────────────────────────────────────────┘

Promotion Dialog Component

// components/mentor/file-promotion-dialog.tsx

interface FilePromotionDialogProps {
  mentorFile: WorkspaceFile
  availableWindows: SubmissionWindow[]
  currentWindowId: string  // Default selection
  onPromote: (windowId: string, requirementId: string | null) => Promise<void>
}

// Logic:
// 1. User selects target submission window
// 2. Load requirements for that window (via tRPC query)
// 3. Show dropdown of available requirement slots
// 4. Show warning if slot already has a file
// 5. Confirm and execute promotion

Promotion Rules

Rule Enforcement
Only unpromoted files isPromoted = false
Workspace must be active workspaceOpenAt <= now <= workspaceCloseAt
User must have permission Team lead OR admin OR (mentor if config allows)
Target window must exist SubmissionWindow.id valid
No file duplication ProjectFile.objectKey = MentorFile.objectKey (same MinIO object)
Versioning New file increments version number
Audit trail Full provenance logged

Unpromote (Revert)

Admin can revert a promotion:

1. Find ProjectFile created by promotion (via MentorFile.promotedToFileId)
2. If it replaced a previous file, restore that file (set replacedById = null)
3. Delete the promoted ProjectFile
4. Reset MentorFile flags:
   - isPromoted = false
   - promotedToFileId = null
   - promotedAt = null
   - promotedByUserId = null
5. Audit log: "MENTOR_FILE_UNPROMOTED"

API:

  • mentorWorkspace.unpromoteFile(mentorFileId) — admin only

7. Messaging (Team Communication)

The workspace includes integrated chat for real-time communication between mentor and team.

Chat Tab UI

┌──────────────────────────────────────────────────────────────┐
│ [📁 Files] [💬 Chat (2)] [📋 Milestones]                     │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│ ┌────────────────────────────────────────────────────────┐  │
│ │ Dr. Martin Duval (Mentor)             Jun 8, 10:30 AM  │  │
│ │ Welcome to the mentoring workspace! I've reviewed      │  │
│ │ your initial application. Let's focus on strengthening │  │
│ │ the financial projections first. I'll upload a         │  │
│ │ template to the Files tab.                             │  │
│ └────────────────────────────────────────────────────────┘  │
│                                                              │
│ ┌────────────────────────────────────────────────────────┐  │
│ │                             Jun 8, 2:15 PM             │  │
│ │                       Sarah Johnson (Team)             │  │
│ │                    Thank you! We've uploaded the       │  │
│ │                    revised business plan to the        │  │
│ │                    Files section. Looking forward      │  │
│ │                    to your feedback.                   │  │
│ └────────────────────────────────────────────────────────┘  │
│                                                              │
│ ┌────────────────────────────────────────────────────────┐  │
│ │ Dr. Martin Duval (Mentor)             Jun 9, 9:00 AM   │  │
│ │ Excellent work on the revisions! I've left detailed    │  │
│ │ comments on the file. The market analysis is much      │  │
│ │ stronger now. One more iteration should get us to      │  │
│ │ a promotion-ready version.                             │  │
│ └────────────────────────────────────────────────────────┘  │
│                                                              │
│ ─────────────────────────────────────────────────────────── │
│                                                              │
│ [Type a message...                                        ] │
│                                                              │
│ [📎 Attach File]                              [Send]        │
└──────────────────────────────────────────────────────────────┘

Message Features

Real-time Updates:

  • Polling every 10 seconds for new messages
  • WebSocket support if available
  • Unread count badge on tab

Message Types:

Type Description
text Plain text message
file_reference Link to a workspace file ("I've uploaded X — see Files tab")
milestone Milestone completion notification

Notifications:

  • In-app notification on new message
  • Email notification if recipient hasn't viewed workspace in 24 hours
  • Push notification if enabled

Read Receipts:

  • Messages marked as read when chat tab is viewed
  • No "typing..." indicator (keep it simple)

8. Milestones (Progress Tracking)

Milestones are optional admin-defined checkpoints for mentoring progress. They're displayed in the workspace and on the mentor dashboard.

Milestones Tab UI

┌──────────────────────────────────────────────────────────────┐
│ [📁 Files] [💬 Chat] [📋 Milestones (2/4)]                   │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│ Mentoring Progress                                           │
│                                                              │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ✅ Initial Review                                       │ │
│ │ Completed: Jun 2, 2026 by Dr. Martin Duval              │ │
│ │ Notes: Reviewed application materials. Strong concept   │ │
│ │ but needs financial model refinement.                   │ │
│ └─────────────────────────────────────────────────────────┘ │
│                                                              │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ✅ Business Plan Feedback                               │ │
│ │ Completed: Jun 5, 2026 by Dr. Martin Duval              │ │
│ │ Notes: Provided detailed comments on v2. Team responded │ │
│ │ with v3 incorporating all feedback.                     │ │
│ └─────────────────────────────────────────────────────────┘ │
│                                                              │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ⏳ Pitch Deck Review                                    │ │
│ │                                                         │ │
│ │ [Mark as Complete]                                      │ │
│ │                                                         │ │
│ │ Optional Notes:                                         │ │
│ │ [Reviewed draft deck. Suggested stronger opening...  ] │ │
│ │                                                         │ │
│ └─────────────────────────────────────────────────────────┘ │
│                                                              │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ⏳ Final Submission Review                              │ │
│ │                                                         │ │
│ │ [Mark as Complete]                                      │ │
│ └─────────────────────────────────────────────────────────┘ │
│                                                              │
└──────────────────────────────────────────────────────────────┘

Milestone Data Model

model MentorMilestone {
  id              String @id @default(cuid())
  competitionId   String
  name            String       // "Initial Review", "Business Plan Feedback"
  description     String?      @db.Text
  sortOrder       Int @default(0)
  isRequired      Boolean @default(false)  // If true, must be completed before workspace closes

  competition     Competition @relation(...)
  completions     MentorMilestoneCompletion[]
}

model MentorMilestoneCompletion {
  id                  String @id @default(cuid())
  mentorAssignmentId  String
  milestoneId         String
  completedAt         DateTime @default(now())
  notes               String? @db.Text

  mentorAssignment MentorAssignment @relation(...)
  milestone        MentorMilestone @relation(...)

  @@unique([mentorAssignmentId, milestoneId])
}

Milestone API

Procedure Purpose
mentor.getMilestones(mentorAssignmentId) Get milestones + completion status for a team
mentor.completeMilestone(assignmentId, milestoneId, notes) Mark milestone complete
mentor.uncompleteMilestone(completionId) Revert completion (admin only)

9. Navigation & Information Architecture

Route Structure

/mentor/                                    → Dashboard (landing page)
/mentor/team/[projectId]                    → Workspace (redirects to /files)
/mentor/team/[projectId]/files              → File list + detail split-pane
/mentor/team/[projectId]/file/[fileId]      → Full-screen file detail (mobile)
/mentor/team/[projectId]/chat               → Chat tab
/mentor/team/[projectId]/milestones         → Milestones tab
/mentor/notifications                       → Notification center
/mentor/settings                            → Mentor profile/preferences

Navigation Component

// components/layouts/mentor-workspace-nav.tsx

interface MentorWorkspaceNavProps {
  projectId: string
  currentTab: "files" | "chat" | "milestones"
  unreadMessageCount: number
  milestonesCompleted: number
  milestonesTotal: number
}

// Renders:
// [📁 Files] [💬 Chat (2)] [📋 Milestones (2/4)]

Breadcrumb

Dashboard > My Projects > OceanClean AI > Files

10. Admin View of Mentoring (Monitoring & Override)

Admin can monitor all mentoring activity and intervene when needed.

Admin Dashboard — Mentoring Overview

┌──────────────────────────────────────────────────────────────────────────┐
│ Admin Dashboard > Mentoring Overview                                     │
├──────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│ Mentoring Period: June 1  June 30, 2026 · ⏱ 18 days remaining          │
│                                                                          │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐               │
│ │ 12             │ │ 8              │ │ 4              │               │
│ │ Active Pairs   │ │ Files Promoted │ │ Inactive Teams │               │
│ └────────────────┘ └────────────────┘ └────────────────┘               │
│                                                                          │
│ ┌─ Mentor-Team Pairings ────────────────────────────────────────────┐   │
│ │                                                                    │   │
│ │ Search: [Filter by mentor, project, or status...]                 │   │
│ │                                                                    │   │
│ │ Mentor              | Project          | Files | Msgs | Last Act  │   │
│ │ ────────────────────┼──────────────────┼───────┼──────┼────────── │   │
│ │ Dr. Martin Duval    | OceanClean AI    | 5     | 8    | 2 hrs ago │   │
│ │                     | Blue Carbon Hub  | 1     | 3    | 2 days    │   │
│ │                     | SeaWatch Monitor | 0     | 0    | Never     │   │
│ │                     |                  |       |      | [Reassign]│   │
│ │                                                                    │   │
│ │ Dr. Lisa Chen       | WaveGen Pro      | 3     | 12   | 5 hrs ago │   │
│ │                     | Reef Restore AI  | 2     | 6    | 1 day     │   │
│ │                                                                    │   │
│ │ Prof. Ahmed Hassan  | PlasticTrack     | 4     | 10   | 3 hrs ago │   │
│ │                     | CoralSense       | 1     | 2    | 4 days    │   │
│ │                     |                  |       |      | ⚠ Flag    │   │
│ │                                                                    │   │
│ └────────────────────────────────────────────────────────────────────┘   │
│                                                                          │
│ ┌─ Alerts ──────────────────────────────────────────────────────────┐   │
│ │                                                                    │   │
│ │ ⚠ 4 teams have no activity in the last 5 days                     │   │
│ │ ⚠ 2 mentors are at max capacity (3 teams each)                    │   │
│ │ ⚠ 1 team has unread messages for 48+ hours                        │   │
│ │                                                                    │   │
│ └────────────────────────────────────────────────────────────────────┘   │
│                                                                          │
│ [Export Mentoring Report]  [Bulk Reassign]  [Extend Deadline]           │
└──────────────────────────────────────────────────────────────────────────┘

Admin Actions

Action Purpose
View any workspace Full read/write access to all workspaces
Reassign mentor Change mentor for a project mid-stream
Promote files on behalf of team Force promotion if team is unresponsive
Extend deadline for specific team Grant extra time
Flag inactive pairs Mark for follow-up
Export workspace data Download all files, messages, comments for audit

Admin Workspace Access

Admin sees an additional "Admin Actions" section in any workspace:

┌─ Admin Actions ─────────────────────────────────────────┐
│                                                         │
│ [Reassign Mentor]  [Extend Deadline]  [Export Data]    │
│ [Promote File (Override)]  [Close Workspace]           │
│                                                         │
└─────────────────────────────────────────────────────────┘

11. Mobile Experience (Tablet-Optimized)

The workspace is designed to work well on tablets (iPad, Android tablets) for file review on the go.

Mobile Layout

Portrait Mode:

┌─────────────────────────────┐
│ ← Back    OceanClean AI     │
├─────────────────────────────┤
│ [Files] [Chat] [Milestones] │
├─────────────────────────────┤
│                             │
│ Files (5)                   │
│                             │
│ 📄 Business Plan v3.pdf     │
│ Sarah · Jun 8 · 💬 4        │
│ [Open]                      │
│                             │
│ 📊 Financial Model.xlsx     │
│ You · Jun 6 · 💬 2          │
│ [Open]                      │
│                             │
│ 📄 Market Analysis.pdf      │
│ Sarah · Jun 5 · 💬 1        │
│ [Open]                      │
│                             │
│ ...                         │
│                             │
└─────────────────────────────┘

Tap a file →

┌─────────────────────────────┐
│ ← Back to Files             │
├─────────────────────────────┤
│ 📄 Business Plan v3.pdf     │
│ Sarah Johnson (Team)        │
│ Jun 8, 2026 · 2.4 MB        │
├─────────────────────────────┤
│                             │
│ [📥 Download]               │
│ [🚀 Promote]                │
│                             │
│ ───────────────────────     │
│                             │
│ 💬 Comments (4)             │
│                             │
│ Dr. Martin · Jun 8, 14:30   │
│ Section 3.2 needs stronger  │
│ market analysis...          │
│                             │
│   └─ Sarah · Jun 8, 16:00   │
│      Good point! We'll add  │
│      a competitive section. │
│                             │
│ [Add comment...]            │
│                             │
└─────────────────────────────┘

Landscape Mode:

┌───────────────────────────────────────────────────────────┐
│ ← Back to Dashboard      OceanClean AI    [🔔 3] [Menu ▾]│
├──────────────┬────────────────────────────────────────────┤
│ Files (5)    │ 📄 Business Plan v3.pdf                   │
│              │ Sarah Johnson · Jun 8 · 2.4 MB            │
│ 📄 Business..│ [📥 Download] [🚀 Promote]                │
│ 💬 4         │                                            │
│              │ ──────────────────────────────────────     │
│ 📊 Financia..│ 💬 Comments (4)                           │
│ 💬 2         │                                            │
│              │ Dr. Martin · Jun 8, 14:30                 │
│ 📄 Market A..│ Section 3.2 needs stronger...             │
│ 💬 1         │   └─ Sarah · Jun 8, 16:00                 │
│              │      Good point! We'll add...              │
│              │                                            │
└──────────────┴────────────────────────────────────────────┘

Mobile-Specific Features

  • Swipe gestures — Swipe left on file to reveal delete/promote actions
  • Pull to refresh — Refresh file list and comments
  • Touch-friendly targets — Minimum 44px tap targets
  • Offline mode — Download files for offline review, sync comments when back online
  • File preview — In-app PDF preview (no external app required)

12. Accessibility

WCAG 2.1 AA Compliance

Feature Implementation
Keyboard navigation Full keyboard access to all workspace features
Screen reader support ARIA labels on all interactive elements
Color contrast 4.5:1 minimum for all text
Focus indicators Visible focus ring on all focusable elements
Alt text File type icons have descriptive labels
Error messaging Clear, descriptive error messages on validation failures

File Preview Accessibility

For PDF file previews:

  • Use <iframe> with PDF.js (accessible)
  • Provide "Download" button as alternative to in-browser preview
  • Alt text: "PDF preview of [filename]"

For image files:

  • Show full-size image with alt text from file description
  • Zoom controls for low vision users

For other file types (Excel, PowerPoint):

  • No in-browser preview — download only
  • Clear file type indicator and size

13. Component Library

New Components

Component Path Purpose
MentorDashboard src/app/(mentor)/mentor/page.tsx Landing page
TeamCard src/components/mentor/team-card.tsx Reusable team summary card
WorkspaceLayout src/app/(mentor)/mentor/team/[projectId]/layout.tsx Workspace shell with tabs
FileList src/components/mentor/file-list.tsx Left pane file list
FileDetail src/components/mentor/file-detail.tsx Right pane file detail
FilePromotionDialog src/components/mentor/file-promotion-dialog.tsx Promotion confirmation dialog
FileUploadZone src/components/mentor/file-upload-zone.tsx Drag-and-drop upload area
CommentThread src/components/mentor/comment-thread.tsx Threaded comment display
CommentInput src/components/mentor/comment-input.tsx Comment textarea with @mentions
MilestoneList src/components/mentor/milestone-list.tsx Milestone progress view
MentorActivityFeed src/components/mentor/activity-feed.tsx Recent activity stream
WorkspaceStatusBadge src/components/mentor/workspace-status-badge.tsx Status indicator (active, needs attention, etc.)

Modified Existing Components

Component Changes
MentorChat Integrate into workspace tabs, add file reference support
MentorNav Add "Workspaces" navigation item

14. API Reference (New tRPC Procedures)

Router: mentorWorkspace

All procedures require mentor, team lead, or admin authentication.

// src/server/routers/mentor-workspace.ts

export const mentorWorkspaceRouter = router({
  // File Upload
  getUploadUrl: mentorOrTeamProcedure
    .input(z.object({
      mentorAssignmentId: z.string(),
      fileName: z.string(),
      mimeType: z.string(),
    }))
    .mutation(async ({ ctx, input }) => {
      // Generate MinIO pre-signed PUT URL
      return { uploadUrl, objectKey }
    }),

  saveFile: mentorOrTeamProcedure
    .input(z.object({
      mentorAssignmentId: z.string(),
      fileName: z.string(),
      mimeType: z.string(),
      size: z.number(),
      bucket: z.string(),
      objectKey: z.string(),
      description: z.string().optional(),
    }))
    .mutation(async ({ ctx, input }) => {
      // Create MentorFile record
      return mentorFile
    }),

  // File Management
  listFiles: mentorOrTeamProcedure
    .input(z.object({
      mentorAssignmentId: z.string(),
    }))
    .query(async ({ ctx, input }) => {
      // Return MentorFile[] with comment counts
      return { files }
    }),

  getFileDownloadUrl: mentorOrTeamProcedure
    .input(z.object({
      mentorFileId: z.string(),
    }))
    .query(async ({ ctx, input }) => {
      // Generate MinIO pre-signed GET URL
      return { downloadUrl, expiresAt }
    }),

  deleteFile: mentorOrTeamProcedure
    .input(z.object({
      mentorFileId: z.string(),
    }))
    .mutation(async ({ ctx, input }) => {
      // Soft delete (mark as deleted)
      // Only uploader or admin can delete
      return { success: true }
    }),

  // Comments
  addComment: mentorOrTeamProcedure
    .input(z.object({
      mentorFileId: z.string(),
      content: z.string().min(1),
      parentCommentId: z.string().optional(),
    }))
    .mutation(async ({ ctx, input }) => {
      // Create MentorFileComment
      // Parse @mentions and create notifications
      return comment
    }),

  listComments: mentorOrTeamProcedure
    .input(z.object({
      mentorFileId: z.string(),
    }))
    .query(async ({ ctx, input }) => {
      // Return threaded comments (with nested replies)
      return { comments }
    }),

  deleteComment: mentorOrTeamProcedure
    .input(z.object({
      commentId: z.string(),
    }))
    .mutation(async ({ ctx, input }) => {
      // Only author or admin can delete
      // Replace with "[Comment deleted]" placeholder
      return { success: true }
    }),

  // File Promotion
  promoteFile: protectedProcedure
    .input(z.object({
      mentorFileId: z.string(),
      submissionWindowId: z.string(),
      requirementId: z.string().optional(),
    }))
    .mutation(async ({ ctx, input }) => {
      // Validate permissions (team lead or admin)
      // Execute promotion (create ProjectFile, update MentorFile)
      // Audit log
      return { mentorFile, projectFile }
    }),

  unpromoteFile: adminProcedure
    .input(z.object({
      mentorFileId: z.string(),
    }))
    .mutation(async ({ ctx, input }) => {
      // Revert promotion (admin only)
      return { success: true }
    }),

  // Workspace Status
  getWorkspaceStatus: mentorOrTeamProcedure
    .input(z.object({
      mentorAssignmentId: z.string(),
    }))
    .query(async ({ ctx, input }) => {
      // Summary stats for workspace
      return {
        fileCount,
        unreadMessageCount,
        milestonesCompleted,
        milestonesTotal,
        lastActivity,
      }
    }),
})

Authorization Helper

// Check if user can access workspace
async function canAccessWorkspace(
  userId: string,
  mentorAssignmentId: string,
  prisma: PrismaClient
): Promise<boolean> {
  const assignment = await prisma.mentorAssignment.findUnique({
    where: { id: mentorAssignmentId },
    include: {
      mentor: true,
      project: {
        include: {
          teamMembers: true,
        },
      },
    },
  })

  if (!assignment) return false

  // Mentor
  if (assignment.mentorId === userId) return true

  // Team lead
  const teamLead = assignment.project.teamMembers.find(
    (tm) => tm.role === 'LEAD'
  )
  if (teamLead?.userId === userId) return true

  // Any team member (for read access)
  const isTeamMember = assignment.project.teamMembers.some(
    (tm) => tm.userId === userId
  )
  if (isTeamMember) return true

  // Admin (checked in middleware)
  return false
}

15. Service Layer

New Service: mentor-workspace.ts

// src/server/services/mentor-workspace.ts

import { PrismaClient, MentorFile, ProjectFile } from '@prisma/client'
import { generatePresignedUrl } from './minio-client'
import { logAudit } from '../utils/audit'

/**
 * Generate MinIO pre-signed PUT URL for workspace file upload
 */
export async function getWorkspaceUploadUrl(
  mentorAssignmentId: string,
  fileName: string,
  mimeType: string,
  actorId: string,
  prisma: PrismaClient
): Promise<{ uploadUrl: string; objectKey: string }> {
  // Validate workspace is active
  const assignment = await prisma.mentorAssignment.findUniqueOrThrow({
    where: { id: mentorAssignmentId },
  })

  if (!assignment.workspaceEnabled) {
    throw new Error('Workspace is not enabled for this assignment')
  }

  const now = new Date()
  if (
    assignment.workspaceOpenAt &&
    assignment.workspaceCloseAt &&
    (now < assignment.workspaceOpenAt || now > assignment.workspaceCloseAt)
  ) {
    throw new Error('Workspace is not currently open')
  }

  // Generate object key
  const objectKey = `mentor-workspace/${mentorAssignmentId}/${Date.now()}-${fileName}`

  // Generate pre-signed PUT URL (15-minute expiry)
  const uploadUrl = await generatePresignedUrl(
    process.env.MINIO_BUCKET!,
    objectKey,
    'PUT',
    15 * 60,
    mimeType
  )

  return { uploadUrl, objectKey }
}

/**
 * Save file metadata after upload
 */
export async function saveWorkspaceFile(
  mentorAssignmentId: string,
  uploadedByUserId: string,
  file: {
    fileName: string
    mimeType: string
    size: number
    bucket: string
    objectKey: string
  },
  description: string | null,
  prisma: PrismaClient
): Promise<MentorFile> {
  const mentorFile = await prisma.mentorFile.create({
    data: {
      mentorAssignmentId,
      uploadedByUserId,
      fileName: file.fileName,
      mimeType: file.mimeType,
      size: file.size,
      bucket: file.bucket,
      objectKey: file.objectKey,
      description,
    },
  })

  // Audit log
  await logAudit(prisma, {
    action: 'MENTOR_FILE_UPLOADED',
    actorId: uploadedByUserId,
    targetType: 'MENTOR_FILE',
    targetId: mentorFile.id,
    metadata: {
      fileName: file.fileName,
      size: file.size,
      mentorAssignmentId,
    },
  })

  return mentorFile
}

/**
 * Promote workspace file to official submission
 */
export async function promoteFileToSubmission(
  mentorFileId: string,
  submissionWindowId: string,
  requirementId: string | null,
  actorId: string,
  prisma: PrismaClient
): Promise<{ mentorFile: MentorFile; projectFile: ProjectFile }> {
  // 1. Validate mentor file
  const mentorFile = await prisma.mentorFile.findUniqueOrThrow({
    where: { id: mentorFileId },
    include: {
      mentorAssignment: {
        include: {
          project: true,
        },
      },
    },
  })

  if (mentorFile.isPromoted) {
    throw new Error('This file has already been promoted')
  }

  // 2. Validate workspace is active
  const { mentorAssignment } = mentorFile
  const now = new Date()
  if (
    !mentorAssignment.workspaceEnabled ||
    (mentorAssignment.workspaceCloseAt && now > mentorAssignment.workspaceCloseAt)
  ) {
    throw new Error('Workspace is closed — cannot promote files')
  }

  // 3. Find existing file in target slot (if requirementId provided)
  let replacedFile: ProjectFile | null = null
  if (requirementId) {
    replacedFile = await prisma.projectFile.findFirst({
      where: {
        projectId: mentorAssignment.projectId,
        requirementId,
        replacedById: null,
      },
    })
  }

  // 4. Determine version number
  const existingVersions = await prisma.projectFile.findMany({
    where: {
      projectId: mentorAssignment.projectId,
      submissionWindowId,
      requirementId,
    },
    orderBy: { version: 'desc' },
    take: 1,
  })
  const nextVersion = existingVersions.length > 0 ? existingVersions[0].version + 1 : 1

  // 5. Create new ProjectFile (reusing same MinIO object)
  const projectFile = await prisma.projectFile.create({
    data: {
      projectId: mentorAssignment.projectId,
      submissionWindowId,
      requirementId,
      fileType: 'SUBMISSION',
      fileName: mentorFile.fileName,
      mimeType: mentorFile.mimeType,
      size: mentorFile.size,
      bucket: mentorFile.bucket,
      objectKey: mentorFile.objectKey,  // NO DUPLICATION
      version: nextVersion,
      isLate: false,
    },
  })

  // 6. Update old file if it exists
  if (replacedFile) {
    await prisma.projectFile.update({
      where: { id: replacedFile.id },
      data: { replacedById: projectFile.id },
    })
  }

  // 7. Update MentorFile
  const updatedMentorFile = await prisma.mentorFile.update({
    where: { id: mentorFileId },
    data: {
      isPromoted: true,
      promotedToFileId: projectFile.id,
      promotedAt: now,
      promotedByUserId: actorId,
    },
  })

  // 8. Audit log
  await logAudit(prisma, {
    action: 'MENTOR_FILE_PROMOTED',
    actorId,
    targetType: 'PROJECT_FILE',
    targetId: projectFile.id,
    metadata: {
      mentorFileId,
      submissionWindowId,
      requirementId,
      replacedFileId: replacedFile?.id,
      version: nextVersion,
    },
  })

  return { mentorFile: updatedMentorFile, projectFile }
}

/**
 * Revert promotion (admin only)
 */
export async function unpromoteFile(
  mentorFileId: string,
  actorId: string,
  prisma: PrismaClient
): Promise<void> {
  const mentorFile = await prisma.mentorFile.findUniqueOrThrow({
    where: { id: mentorFileId },
    include: { promotedFile: true },
  })

  if (!mentorFile.isPromoted || !mentorFile.promotedFile) {
    throw new Error('File is not promoted')
  }

  const promotedFile = mentorFile.promotedFile

  // 1. If promoted file replaced a previous file, restore that file
  if (promotedFile.replacedById) {
    const previousFile = await prisma.projectFile.findUnique({
      where: { replacedById: promotedFile.id },
    })
    if (previousFile) {
      await prisma.projectFile.update({
        where: { id: previousFile.id },
        data: { replacedById: null },
      })
    }
  }

  // 2. Delete the promoted ProjectFile
  await prisma.projectFile.delete({
    where: { id: promotedFile.id },
  })

  // 3. Reset MentorFile flags
  await prisma.mentorFile.update({
    where: { id: mentorFileId },
    data: {
      isPromoted: false,
      promotedToFileId: null,
      promotedAt: null,
      promotedByUserId: null,
    },
  })

  // 4. Audit log
  await logAudit(prisma, {
    action: 'MENTOR_FILE_UNPROMOTED',
    actorId,
    targetType: 'MENTOR_FILE',
    targetId: mentorFileId,
    metadata: {
      deletedProjectFileId: promotedFile.id,
    },
  })
}

16. Edge Cases & Error Handling

Scenario Handling
Team doesn't want mentoring but admin assigns anyway Assignment created; team sees mentor in dashboard with explanation
Mentor goes inactive during period Admin can reassign; previous workspace preserved (read-only for old mentor)
File promoted then mentor period closes Promoted file remains as official submission; MentorFile remains in workspace
Team tries to promote file for requirement that doesn't exist Error — must select valid requirement or leave requirementId null
Two files promoted to same requirement slot Second promotion replaces first (versioning applies)
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 adjustment or grant grace period
Promoted file deleted from workspace ProjectFile remains (separate record); MentorFile keeps audit trail
Mentor uploads file then leaves program Files remain accessible in workspace; admin can reassign mentor
File upload fails mid-way MinIO object exists but no MentorFile record → cleanup job deletes orphaned objects weekly
Comment with @mention for user not in workspace @mention rendered as plain text, no notification sent
Workspace accessed after mentoring round ends Read-only mode — can view files and messages, can't upload or comment

17. Performance Considerations

File Upload Optimization

  • Direct MinIO upload — Files never touch the Next.js server
  • Pre-signed URLs — 15-minute expiry, client uploads directly
  • Chunked upload — For files > 5 MB, use multipart upload
  • Progress feedback — Real-time upload progress bar

File List Performance

  • Pagination — Load 20 files at a time (infinite scroll)
  • Comment count caching — Denormalized comment count on MentorFile
  • Thumbnail generation — For images, generate thumbnails on upload

Real-time Updates

  • Polling strategy — Poll every 10s for new messages, 30s for file list
  • WebSocket upgrade — If available, use WebSocket for real-time updates
  • Optimistic UI — Show uploaded file immediately, sync in background

18. Security

Access Control

// Every workspace procedure checks:
1. User is authenticated
2. User is mentor OR team member OR admin
3. Workspace is active (or admin override)

File Storage Security

  • Private bucket — All workspace files in private MinIO bucket
  • Pre-signed URLs — Expire after 15 minutes
  • No directory listing — Can't enumerate workspace files without database access
  • Audit trail — Every file upload, download, promotion logged

XSS Prevention

  • Sanitize filenames — Strip HTML, escape special chars
  • Sanitize comments — Plain text only, no HTML tags
  • Validate MIME types — Only allow whitelisted file types

19. Testing Strategy

Unit Tests

// Test file promotion logic
describe('promoteFileToSubmission', () => {
  it('should create ProjectFile with same objectKey', async () => {
    const { mentorFile, projectFile } = await promoteFileToSubmission(...)
    expect(projectFile.objectKey).toBe(mentorFile.objectKey)
    expect(mentorFile.isPromoted).toBe(true)
  })

  it('should increment version number', async () => {
    // Upload file v1
    // Promote to create ProjectFile v1
    // Upload file v2
    // Promote to create ProjectFile v2
    expect(projectFile.version).toBe(2)
  })

  it('should replace previous file in slot', async () => {
    // Promote file1 to slot A
    // Promote file2 to slot A
    expect(file1.replacedById).toBe(file2.id)
  })
})

Integration Tests

// Test full workspace flow
describe('Workspace Integration', () => {
  it('should allow mentor to upload, team to comment, and promote', async () => {
    // 1. Mentor uploads file
    const uploadUrl = await trpc.mentorWorkspace.getUploadUrl(...)
    // Upload to MinIO
    const file = await trpc.mentorWorkspace.saveFile(...)

    // 2. Team adds comment
    const comment = await trpc.mentorWorkspace.addComment(...)
    expect(comment.authorId).toBe(teamMemberId)

    // 3. Promote file
    const { projectFile } = await trpc.mentorWorkspace.promoteFile(...)
    expect(projectFile.projectId).toBe(project.id)
  })
})

E2E Tests (Playwright)

// Test workspace UI
test('Mentor can upload file and view comments', async ({ page }) => {
  await page.goto('/mentor/team/project-123/files')

  // Upload file
  await page.click('text=Upload File')
  await page.setInputFiles('input[type=file]', 'test-file.pdf')
  await page.click('text=Upload')

  // Wait for file to appear
  await page.waitForSelector('text=test-file.pdf')

  // Open file detail
  await page.click('text=test-file.pdf')

  // Add comment
  await page.fill('textarea[placeholder="Add comment..."]', 'Looks good!')
  await page.click('text=Post Comment')

  // Verify comment appears
  await page.waitForSelector('text=Looks good!')
})

20. Rollout Plan

Phase 1: Foundation (Week 1-2)

  • Create data models (MentorFile, MentorFileComment)
  • Migration script
  • Basic tRPC procedures (upload, list, download)
  • Service layer (mentor-workspace.ts)

Phase 2: Core UI (Week 3-4)

  • Mentor dashboard
  • Workspace layout (split-pane)
  • File list component
  • File detail component
  • File upload zone

Phase 3: Comments & Chat (Week 5)

  • Comment thread component
  • @mention support
  • Enhanced chat integration

Phase 4: File Promotion (Week 6)

  • Promotion dialog
  • Promotion service logic
  • Unpromote (admin override)
  • Audit trail

Phase 5: Admin Tools (Week 7)

  • Admin monitoring dashboard
  • Reassignment flow
  • Workspace export
  • Activity alerts

Phase 6: Polish & Mobile (Week 8)

  • Mobile responsive design
  • Accessibility audit
  • Performance optimization
  • E2E tests

21. Success Metrics

Metric Target
Mentor engagement 80%+ of mentors upload at least 1 file
Team engagement 90%+ of finalist teams engage in workspace
File promotion rate 50%+ of workspace files promoted to submission
Comment activity Average 3+ comments per promoted file
Milestone completion 70%+ of mentors complete all milestones
Mobile usage 30%+ of workspace access from mobile/tablet
Time to first file upload < 24 hours after mentoring round opens
Admin intervention rate < 10% of assignments require admin reassignment

22. Future Enhancements (Post-MVP)

Video Integration

  • Loom/Zoom integration for mentor video feedback
  • Record screen share reviews of files

Real-time Collaboration

  • Live document editing (Google Docs-style)
  • Shared whiteboard for brainstorming

AI-Powered Features

  • AI-generated file summaries
  • Suggested comments based on file content
  • Auto-tagging files by topic

Analytics Dashboard

  • Mentor performance metrics
  • Team engagement heatmaps
  • File promotion success rates

Templates Library

  • Mentor-created templates (financial models, pitch decks)
  • Template marketplace

Document Complete

This document provides a comprehensive blueprint for the Mentor UI redesign. It covers:

  • Current baseline (existing mentor features)
  • Dashboard design (landing page with team cards, metrics, activity feed)
  • Workspace architecture (split-pane file management)
  • File review & comments (threaded discussion)
  • File promotion (workspace → official submission)
  • Messaging integration
  • Milestones tracking
  • Navigation & IA
  • Admin monitoring
  • Mobile experience
  • Accessibility
  • Component library
  • API reference
  • Service layer
  • Edge cases & security
  • Testing strategy
  • Rollout plan
  • Success metrics

The redesigned mentor experience transforms mentoring from basic messaging into a full collaboration workspace with file management, threaded comments, and seamless integration with the submission system.