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
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:
@@ -300,19 +300,17 @@ function EditProjectContent({ projectId }: { projectId: string }) {
|
||||
if (!project) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<Button variant="ghost" asChild className="-ml-4">
|
||||
<Link href="/admin/projects">
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
Back to Projects
|
||||
</Link>
|
||||
<Button variant="ghost" className="-ml-4" 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 text-center">
|
||||
<AlertCircle className="h-12 w-12 text-destructive/50" />
|
||||
<p className="mt-2 font-medium">Project Not Found</p>
|
||||
<Button asChild className="mt-4">
|
||||
<Link href="/admin/projects">Back to Projects</Link>
|
||||
<Button className="mt-4" onClick={() => router.back()}>
|
||||
Back
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -330,11 +328,9 @@ function EditProjectContent({ 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={`/admin/projects/${projectId}`}>
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
Back to Project
|
||||
</Link>
|
||||
<Button variant="ghost" className="-ml-4" onClick={() => router.back()}>
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
Back
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { Suspense, use, useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { trpc } from '@/lib/trpc/client'
|
||||
import { toast } from 'sonner'
|
||||
import {
|
||||
@@ -46,6 +46,7 @@ interface MentorSuggestion {
|
||||
}
|
||||
|
||||
function MentorAssignmentContent({ projectId }: { projectId: string }) {
|
||||
const router = useRouter()
|
||||
const [selectedMentorId, setSelectedMentorId] = useState<string | null>(null)
|
||||
|
||||
const utils = trpc.useUtils()
|
||||
@@ -128,11 +129,9 @@ function MentorAssignmentContent({ 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={`/admin/projects/${projectId}`}>
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
Back to Project
|
||||
</Link>
|
||||
<Button variant="ghost" className="-ml-4" onClick={() => router.back()}>
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
Back
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { Suspense, use, useState } 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,
|
||||
@@ -102,6 +103,7 @@ const evalStatusColors: Record<string, 'default' | 'secondary' | 'destructive' |
|
||||
}
|
||||
|
||||
function ProjectDetailContent({ projectId }: { projectId: string }) {
|
||||
const router = useRouter()
|
||||
// Fetch project + assignments + stats in a single combined query
|
||||
const { data: fullDetail, isLoading } = trpc.project.getFullDetail.useQuery(
|
||||
{ id: projectId },
|
||||
@@ -199,19 +201,17 @@ function ProjectDetailContent({ projectId }: { projectId: string }) {
|
||||
if (!project) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<Button variant="ghost" asChild className="-ml-4">
|
||||
<Link href="/admin/projects">
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
Back to Projects
|
||||
</Link>
|
||||
<Button variant="ghost" className="-ml-4" 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 text-center">
|
||||
<AlertCircle className="h-12 w-12 text-destructive/50" />
|
||||
<p className="mt-2 font-medium">Project Not Found</p>
|
||||
<Button asChild className="mt-4">
|
||||
<Link href="/admin/projects">Back to Projects</Link>
|
||||
<Button className="mt-4" onClick={() => router.back()}>
|
||||
Back
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -223,11 +223,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="/admin/projects">
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
Back to Projects
|
||||
</Link>
|
||||
<Button variant="ghost" className="-ml-4" onClick={() => router.back()}>
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
Back
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useCallback, useRef, useEffect, useMemo } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { trpc } from '@/lib/trpc/client'
|
||||
import { toast } from 'sonner'
|
||||
import {
|
||||
@@ -62,6 +62,7 @@ type UploadState = {
|
||||
type UploadMap = Record<string, UploadState>
|
||||
|
||||
export default function BulkUploadPage() {
|
||||
const router = useRouter()
|
||||
const [roundId, setRoundId] = useState('')
|
||||
const [search, setSearch] = useState('')
|
||||
const [debouncedSearch, setDebouncedSearch] = useState('')
|
||||
@@ -296,10 +297,8 @@ export default function BulkUploadPage() {
|
||||
<div className="space-y-6">
|
||||
{/* Header */}
|
||||
<div className="flex items-center gap-4">
|
||||
<Button variant="ghost" size="icon" asChild>
|
||||
<Link href="/admin/projects">
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
</Link>
|
||||
<Button variant="ghost" size="icon" onClick={() => router.back()}>
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold tracking-tight">Bulk Document Upload</h1>
|
||||
|
||||
@@ -59,11 +59,9 @@ function ImportPageContent() {
|
||||
<div className="space-y-6">
|
||||
{/* Header */}
|
||||
<div className="flex items-center gap-4">
|
||||
<Button variant="ghost" asChild className="-ml-4">
|
||||
<Link href="/admin/projects">
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
Back to Projects
|
||||
</Link>
|
||||
<Button variant="ghost" className="-ml-4" onClick={() => router.back()}>
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
Back
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -246,11 +246,9 @@ function NewProjectPageContent() {
|
||||
<div className="space-y-6">
|
||||
{/* Header */}
|
||||
<div className="flex items-center gap-4">
|
||||
<Button variant="ghost" asChild className="-ml-4">
|
||||
<Link href="/admin/projects">
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
Back to Projects
|
||||
</Link>
|
||||
<Button variant="ghost" className="-ml-4" onClick={() => router.back()}>
|
||||
<ArrowLeft className="mr-2 h-4 w-4" />
|
||||
Back
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user