Three places linked to \`/products/[id]/backlog#pbi-{pbi_code}\` after
materializing or in the planned-state link-card. That route doesn't
exist (product backlog lives at \`/products/[id]\` directly), and the
hash was double-prefixed (\`#pbi-PBI-32\`) since pbi_code already starts
with PBI-. Result: 404 for the user.
Fix: route to \`/products/[id]\` without anchor. The new PBI is the most
recent so visible near the top. Per-PBI anchor scrolling is a follow-up
once we add \`id="pbi-{id}"\` attributes to pbi-list rows.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
85 lines
2.6 KiB
TypeScript
85 lines
2.6 KiB
TypeScript
'use client'
|
|
|
|
// IdeaPbiLinkCard — toont de gekoppelde PBI bij PLANNED. Bij "stale link"
|
|
// (status===PLANNED maar pbi_id===null, want PBI elders verwijderd via
|
|
// de SetNull FK) tonen we de Re-link-banner.
|
|
|
|
import { useTransition } from 'react'
|
|
import Link from 'next/link'
|
|
import { useRouter } from 'next/navigation'
|
|
import { ExternalLink, Link2Off } from 'lucide-react'
|
|
import { toast } from 'sonner'
|
|
|
|
import { Button } from '@/components/ui/button'
|
|
import { relinkIdeaPlanAction } from '@/actions/ideas'
|
|
import type { IdeaDto } from '@/lib/idea-dto'
|
|
|
|
interface Props {
|
|
idea: IdeaDto
|
|
isDemo: boolean
|
|
}
|
|
|
|
export function IdeaPbiLinkCard({ idea, isDemo }: Props) {
|
|
const router = useRouter()
|
|
const [pending, startTransition] = useTransition()
|
|
|
|
if (idea.status !== 'planned') return null
|
|
|
|
if (idea.pbi && idea.product_id) {
|
|
return (
|
|
<div className="rounded-md border border-status-done/30 bg-status-done/10 p-4 flex items-center gap-3">
|
|
<div className="flex-1">
|
|
<p className="text-xs uppercase tracking-wide text-status-done font-medium">
|
|
Gepland
|
|
</p>
|
|
<p className="text-sm">
|
|
Gematerialiseerd als{' '}
|
|
<Link
|
|
href={`/products/${idea.product_id}`}
|
|
className="font-medium text-status-done hover:underline inline-flex items-center gap-1"
|
|
>
|
|
{idea.pbi.code} — {idea.pbi.title}
|
|
<ExternalLink className="size-3" />
|
|
</Link>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
// Stale link — pbi_id === null maar status nog PLANNED.
|
|
function handleRelink() {
|
|
if (isDemo) return
|
|
startTransition(async () => {
|
|
const r = await relinkIdeaPlanAction(idea.id)
|
|
if ('error' in r) {
|
|
toast.error(r.error)
|
|
return
|
|
}
|
|
toast.success('Idee terug naar PLAN_READY — open de Plan-tab.')
|
|
router.refresh()
|
|
})
|
|
}
|
|
|
|
return (
|
|
<div className="rounded-md border border-status-blocked/30 bg-status-blocked/10 p-4 space-y-2">
|
|
<div className="flex items-center gap-2">
|
|
<Link2Off className="size-4 text-status-blocked" />
|
|
<p className="text-sm font-medium text-status-blocked">
|
|
De gekoppelde PBI bestaat niet meer
|
|
</p>
|
|
</div>
|
|
<p className="text-sm text-muted-foreground">
|
|
Klik om dit idee terug naar PLAN_READY te zetten en opnieuw te materialiseren.
|
|
</p>
|
|
<Button
|
|
size="sm"
|
|
variant="outline"
|
|
onClick={handleRelink}
|
|
disabled={isDemo || pending}
|
|
>
|
|
Plan opnieuw beschikbaar maken
|
|
</Button>
|
|
</div>
|
|
)
|
|
}
|