feat: router.back() navigation, read-only evaluation view, auth audit logging
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m53s

- Convert all Back buttons platform-wide (38 files) to use router.back()
  for natural browser-back behavior regardless of entry point
- Add read-only view for submitted evaluations in closed rounds with
  blue banner, disabled inputs, and contextual back navigation
- Add auth audit logs: MAGIC_LINK_SENT, PASSWORD_RESET_LINK_CLICKED,
  PASSWORD_RESET_LINK_EXPIRED, PASSWORD_RESET_LINK_INVALID
- Learning Hub links navigate in same window for all roles
- Update settings descriptions to reflect all-user scope

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 14:25:56 +01:00
parent a556732b46
commit a1e758bc39
44 changed files with 398 additions and 384 deletions

View File

@@ -1,8 +1,7 @@
'use client'
import { Suspense, use, useState, useEffect } from 'react'
import Link from 'next/link'
import type { Route } from 'next'
import { useRouter } from 'next/navigation'
import { trpc } from '@/lib/trpc/client'
import {
Card,
@@ -75,6 +74,7 @@ const statusColors: Record<string, 'default' | 'secondary' | 'destructive' | 'ou
}
function ProjectDetailContent({ projectId }: { projectId: string }) {
const router = useRouter()
const { data: project, isLoading, error } = trpc.mentor.getProjectDetail.useQuery({
projectId,
})
@@ -106,11 +106,9 @@ function ProjectDetailContent({ projectId }: { projectId: string }) {
if (error || !project) {
return (
<div className="space-y-6">
<Button variant="ghost" asChild className="-ml-4">
<Link href={'/mentor' as Route}>
<ArrowLeft className="mr-2 h-4 w-4" />
Back to Dashboard
</Link>
<Button variant="ghost" className="-ml-4" onClick={() => router.back()}>
<ArrowLeft className="mr-2 h-4 w-4" />
Back
</Button>
<Card>
@@ -122,8 +120,8 @@ function ProjectDetailContent({ projectId }: { projectId: string }) {
<p className="text-sm text-muted-foreground mt-1">
You may not have access to view this project.
</p>
<Button asChild className="mt-4">
<Link href={'/mentor' as Route}>Back to Dashboard</Link>
<Button className="mt-4" onClick={() => router.back()}>
Back
</Button>
</CardContent>
</Card>
@@ -140,11 +138,9 @@ function ProjectDetailContent({ projectId }: { projectId: string }) {
<div className="space-y-6">
{/* Header */}
<div className="flex items-center gap-4">
<Button variant="ghost" asChild className="-ml-4">
<Link href={'/mentor' as Route}>
<ArrowLeft className="mr-2 h-4 w-4" />
Back to Dashboard
</Link>
<Button variant="ghost" className="-ml-4" onClick={() => router.back()}>
<ArrowLeft className="mr-2 h-4 w-4" />
Back
</Button>
</div>

View File

@@ -1,8 +1,7 @@
'use client'
import { useEffect } from 'react'
import { useParams } from 'next/navigation'
import Link from 'next/link'
import { useParams, useRouter } from 'next/navigation'
import dynamic from 'next/dynamic'
import { trpc } from '@/lib/trpc/client'
import { Button } from '@/components/ui/button'
@@ -27,6 +26,7 @@ const ResourceRenderer = dynamic(
export default function MentorResourceDetailPage() {
const params = useParams()
const router = useRouter()
const resourceId = params.id as string
const { data: resource, isLoading, error } = trpc.learningResource.get.useQuery({ id: resourceId })
@@ -73,11 +73,9 @@ export default function MentorResourceDetailPage() {
This resource may have been removed or you don&apos;t have access.
</AlertDescription>
</Alert>
<Button asChild>
<Link href="/mentor/resources">
<ArrowLeft className="mr-2 h-4 w-4" />
Back to Resources
</Link>
<Button onClick={() => router.back()}>
<ArrowLeft className="mr-2 h-4 w-4" />
Back
</Button>
</div>
)
@@ -87,11 +85,9 @@ export default function MentorResourceDetailPage() {
<div className="space-y-6">
{/* Header */}
<div className="flex items-center justify-between">
<Button variant="ghost" asChild className="-ml-4">
<Link href="/mentor/resources">
<ArrowLeft className="mr-2 h-4 w-4" />
Back to Resources
</Link>
<Button variant="ghost" className="-ml-4" onClick={() => router.back()}>
<ArrowLeft className="mr-2 h-4 w-4" />
Back
</Button>
<div className="flex items-center gap-2">
{resource.externalUrl && (

View File

@@ -1,8 +1,6 @@
'use client'
import { useParams } from 'next/navigation'
import Link from 'next/link'
import type { Route } from 'next'
import { useParams, useRouter } from 'next/navigation'
import { trpc } from '@/lib/trpc/client'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
@@ -16,6 +14,7 @@ import { toast } from 'sonner'
export default function MentorWorkspaceDetailPage() {
const params = useParams()
const router = useRouter()
const projectId = params.projectId as string
// Get mentor assignment for this project
@@ -39,11 +38,9 @@ export default function MentorWorkspaceDetailPage() {
if (!project) {
return (
<div className="space-y-6">
<Button variant="ghost" size="sm" asChild>
<Link href={'/mentor/workspace' as Route}>
<ArrowLeft className="mr-2 h-4 w-4" />
Back
</Link>
<Button variant="ghost" size="sm" onClick={() => router.back()}>
<ArrowLeft className="mr-2 h-4 w-4" />
Back
</Button>
<Card>
<CardContent className="flex flex-col items-center justify-center py-12">
@@ -58,11 +55,9 @@ export default function MentorWorkspaceDetailPage() {
return (
<div className="space-y-6">
<div className="flex items-center gap-4">
<Button variant="ghost" size="sm" asChild>
<Link href={'/mentor/workspace' as Route}>
<ArrowLeft className="mr-2 h-4 w-4" />
Back
</Link>
<Button variant="ghost" size="sm" onClick={() => router.back()}>
<ArrowLeft className="mr-2 h-4 w-4" />
Back
</Button>
<div className="flex-1">
<div className="flex items-center gap-3 flex-wrap">

View File

@@ -2,6 +2,7 @@
import Link from 'next/link'
import type { Route } from 'next'
import { useRouter } from 'next/navigation'
import { trpc } from '@/lib/trpc/client'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
@@ -20,6 +21,7 @@ const statusColors: Record<string, 'default' | 'secondary' | 'destructive' | 'ou
}
export default function MentorWorkspacePage() {
const router = useRouter()
const { data: assignments, isLoading } = trpc.mentor.getMyProjects.useQuery()
if (isLoading) {
@@ -46,11 +48,9 @@ export default function MentorWorkspacePage() {
Collaborate with your assigned mentee projects
</p>
</div>
<Button variant="ghost" size="sm" asChild>
<Link href={'/mentor' as Route}>
<ArrowLeft className="mr-2 h-4 w-4" />
Back to Dashboard
</Link>
<Button variant="ghost" size="sm" onClick={() => router.back()}>
<ArrowLeft className="mr-2 h-4 w-4" />
Back
</Button>
</div>