feat(final-docs): Final Documents panel on team + mentor views
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@ import { Button } from '@/components/ui/button'
|
|||||||
import { Skeleton } from '@/components/ui/skeleton'
|
import { Skeleton } from '@/components/ui/skeleton'
|
||||||
import { MentorChat } from '@/components/shared/mentor-chat'
|
import { MentorChat } from '@/components/shared/mentor-chat'
|
||||||
import { WorkspaceFilesPanel } from '@/components/mentor/workspace-files-panel'
|
import { WorkspaceFilesPanel } from '@/components/mentor/workspace-files-panel'
|
||||||
|
import { FinalDocumentsPanel } from '@/components/applicant/final-documents-panel'
|
||||||
import { RequestChangeDialog } from './request-change-dialog'
|
import { RequestChangeDialog } from './request-change-dialog'
|
||||||
import {
|
import {
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
@@ -216,6 +217,9 @@ export default function ApplicantMentorPage() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Final Documents (self-hides when not a finalist) */}
|
||||||
|
<FinalDocumentsPanel variant="team" />
|
||||||
|
|
||||||
{/* Request change dialog */}
|
{/* Request change dialog */}
|
||||||
{projectId && (
|
{projectId && (
|
||||||
<RequestChangeDialog
|
<RequestChangeDialog
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
import { WorkspaceChat } from '@/components/mentor/workspace-chat'
|
import { WorkspaceChat } from '@/components/mentor/workspace-chat'
|
||||||
import { FilePromotionPanel } from '@/components/mentor/file-promotion-panel'
|
import { FilePromotionPanel } from '@/components/mentor/file-promotion-panel'
|
||||||
import { WorkspaceFilesPanel } from '@/components/mentor/workspace-files-panel'
|
import { WorkspaceFilesPanel } from '@/components/mentor/workspace-files-panel'
|
||||||
|
import { FinalDocumentsPanel } from '@/components/applicant/final-documents-panel'
|
||||||
import { ArrowLeft, MessageSquare, FileText, Upload, Users } from 'lucide-react'
|
import { ArrowLeft, MessageSquare, FileText, Upload, Users } from 'lucide-react'
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
|
|
||||||
@@ -186,6 +187,9 @@ export default function MentorWorkspaceDetailPage() {
|
|||||||
)}
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
|
{/* Final Documents (self-hides when not a finalist) */}
|
||||||
|
<FinalDocumentsPanel variant="mentor" projectId={projectId} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
59
src/components/applicant/final-documents-panel.tsx
Normal file
59
src/components/applicant/final-documents-panel.tsx
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { trpc } from '@/lib/trpc/client'
|
||||||
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
import { FileCheck2, Clock, Upload, CheckCircle2, Circle } from 'lucide-react'
|
||||||
|
|
||||||
|
type Props =
|
||||||
|
| { variant: 'team' }
|
||||||
|
| { variant: 'mentor'; projectId: string }
|
||||||
|
|
||||||
|
export function FinalDocumentsPanel(props: Props) {
|
||||||
|
const teamQuery = trpc.applicant.getFinalDocumentStatus.useQuery(undefined, { enabled: props.variant === 'team' })
|
||||||
|
const mentorQuery = trpc.mentor.getProjectFinalDocuments.useQuery(
|
||||||
|
{ projectId: props.variant === 'mentor' ? props.projectId : '' },
|
||||||
|
{ enabled: props.variant === 'mentor' },
|
||||||
|
)
|
||||||
|
const status = props.variant === 'team' ? teamQuery.data : mentorQuery.data
|
||||||
|
if (!status) return null
|
||||||
|
|
||||||
|
const fmt = new Intl.DateTimeFormat(undefined, { dateStyle: 'long', timeStyle: 'short' })
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<div className="flex items-center justify-between flex-wrap gap-2">
|
||||||
|
<CardTitle className="flex items-center gap-2 text-lg"><FileCheck2 className="h-5 w-5" /> Final Documents</CardTitle>
|
||||||
|
{status.allRequiredUploaded
|
||||||
|
? <Badge className="bg-emerald-50 text-emerald-700 border-emerald-200">Submitted</Badge>
|
||||||
|
: status.deadline && (
|
||||||
|
<span className={`flex items-center gap-1.5 text-sm ${status.deadlinePassed ? 'text-destructive' : 'text-muted-foreground'}`}>
|
||||||
|
<Clock className="h-4 w-4" /> Due {fmt.format(new Date(status.deadline))}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<CardDescription>
|
||||||
|
{props.variant === 'team' ? 'Your final deliverables for the Grand Finale.' : 'This team\'s final deliverables for the Grand Finale.'}
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-2">
|
||||||
|
{status.requirements.map((r) => (
|
||||||
|
<div key={r.id} className="flex items-center justify-between rounded-lg border p-3">
|
||||||
|
<span className="flex items-center gap-2 text-sm">
|
||||||
|
{r.uploaded ? <CheckCircle2 className="h-4 w-4 text-emerald-600" /> : <Circle className="h-4 w-4 text-muted-foreground/50" />}
|
||||||
|
{r.name}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs text-muted-foreground truncate max-w-[50%]">{r.file?.fileName ?? 'Not yet uploaded'}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{props.variant === 'team' && !status.allRequiredUploaded && (
|
||||||
|
<Button asChild size="sm" className="mt-2 bg-brand-blue hover:bg-brand-blue-light">
|
||||||
|
<Link href="/applicant/documents"><Upload className="mr-2 h-4 w-4" /> Upload documents</Link>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user