feat(PBI-49): add debugProps to jobs/ + ideas/ components

This commit is contained in:
Scrum4Me Agent 2026-05-09 21:03:49 +02:00
parent 6c004ffc74
commit c9f0e65a3d
15 changed files with 45 additions and 22 deletions

View file

@ -9,6 +9,7 @@ import { Download } from 'lucide-react'
import { toast } from 'sonner'
import { Button } from '@/components/ui/button'
import { debugProps } from '@/lib/debug'
import { downloadIdeaMdAction } from '@/actions/ideas'
interface Props {
@ -47,6 +48,7 @@ export function DownloadMdButton({ ideaId, kind, hasContent }: Props) {
onClick={handleClick}
disabled={pending || !hasContent}
title={hasContent ? `Download ${kind}_md` : 'Geen content'}
{...debugProps('download-md-button', 'DownloadMdButton', 'components/ideas/download-md-button.tsx')}
>
<Download className="size-3.5 mr-1" />
.md

View file

@ -20,6 +20,7 @@ import { getIdeaStatusBadge } from '@/lib/idea-status-colors'
import type { IdeaStatusApi } from '@/lib/idea-status'
import { isIdeaEditable } from '@/lib/idea-status'
import type { IdeaDto } from '@/lib/idea-dto'
import { debugProps } from '@/lib/debug'
import { updateIdeaAction, archiveIdeaAction, updateSecondaryProductsAction } from '@/actions/ideas'
import { IdeaRowActions } from '@/components/ideas/idea-row-actions'
import { IdeaMdEditor } from '@/components/ideas/idea-md-editor'
@ -132,7 +133,7 @@ export function IdeaDetailLayout({
const badge = getIdeaStatusBadge(API_TO_DB[idea.status])
return (
<div className="p-6 max-w-5xl mx-auto w-full space-y-6">
<div className="p-6 max-w-5xl mx-auto w-full space-y-6" {...debugProps('idea-detail-layout', 'IdeaDetailLayout', 'components/ideas/idea-detail-layout.tsx')}>
{/* Breadcrumb / back-link */}
<Link
href="/ideas"

View file

@ -29,6 +29,7 @@ import { DemoTooltip } from '@/components/shared/demo-tooltip'
import { getIdeaStatusBadge } from '@/lib/idea-status-colors'
import type { IdeaStatusApi } from '@/lib/idea-status'
import type { IdeaDto } from '@/lib/idea-dto'
import { debugProps } from '@/lib/debug'
import { createIdeaAction, archiveIdeaAction } from '@/actions/ideas'
import { IdeaRowActions } from '@/components/ideas/idea-row-actions'
@ -258,7 +259,7 @@ export function IdeaList({ ideas, products, isDemo }: IdeaListProps) {
}
return (
<div className="space-y-4">
<div className="space-y-4" {...debugProps('idea-list', 'IdeaList', 'components/ideas/idea-list.tsx')}>
{/* Top-bar: search + nieuw-knop */}
<div className="flex flex-wrap items-center gap-3">
<Input

View file

@ -16,6 +16,7 @@ import { toast } from 'sonner'
import { Button } from '@/components/ui/button'
import { Textarea } from '@/components/ui/textarea'
import { debugProps } from '@/lib/debug'
import { parsePlanMd, type PlanParseError } from '@/lib/idea-plan-parser'
import { updateGrillMdAction, updatePlanMdAction } from '@/actions/ideas'
@ -112,7 +113,7 @@ export function IdeaMdEditor({ ideaId, kind, initialValue, onCancel }: Props) {
const dirty = value !== initialValue
return (
<div className="space-y-3">
<div className="space-y-3" {...debugProps('idea-md-editor', 'IdeaMdEditor', 'components/ideas/idea-md-editor.tsx')}>
{errors.length > 0 && (
<div className="rounded-md border border-status-blocked/30 bg-status-blocked/10 p-3 space-y-1">
<p className="text-xs font-medium text-status-blocked">

View file

@ -11,6 +11,7 @@ import { ExternalLink, Link2Off } from 'lucide-react'
import { toast } from 'sonner'
import { Button } from '@/components/ui/button'
import { debugProps } from '@/lib/debug'
import { relinkIdeaPlanAction } from '@/actions/ideas'
import type { IdeaDto } from '@/lib/idea-dto'
@ -27,7 +28,7 @@ export function IdeaPbiLinkCard({ idea, isDemo }: Props) {
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="rounded-md border border-status-done/30 bg-status-done/10 p-4 flex items-center gap-3" {...debugProps('idea-pbi-link-card', 'IdeaPbiLinkCard', 'components/ideas/idea-pbi-link-card.tsx')}>
<div className="flex-1">
<p className="text-xs uppercase tracking-wide text-status-done font-medium">
Gepland
@ -62,7 +63,7 @@ export function IdeaPbiLinkCard({ idea, isDemo }: Props) {
}
return (
<div className="rounded-md border border-status-blocked/30 bg-status-blocked/10 p-4 space-y-2">
<div className="rounded-md border border-status-blocked/30 bg-status-blocked/10 p-4 space-y-2" {...debugProps('idea-pbi-link-card', 'IdeaPbiLinkCard', 'components/ideas/idea-pbi-link-card.tsx')}>
<div className="flex items-center gap-2">
<Link2Off className="size-4 text-status-blocked" />
<p className="text-sm font-medium text-status-blocked">

View file

@ -36,6 +36,7 @@ import {
} from '@/components/ui/tooltip'
import { DemoTooltip } from '@/components/shared/demo-tooltip'
import { useSoloStore } from '@/stores/solo-store'
import { debugProps } from '@/lib/debug'
import {
startGrillJobAction,
startMakePlanJobAction,
@ -134,7 +135,7 @@ export function IdeaRowActions({ idea, isDemo, onArchive }: IdeaRowActionsProps)
}
return (
<div className="flex items-center gap-1">
<div className="flex items-center gap-1" {...debugProps('idea-row-actions', 'IdeaRowActions', 'components/ideas/idea-row-actions.tsx')}>
{/* Bekijk PBI — alleen zichtbaar in PLANNED */}
{status === 'planned' && idea.pbi && idea.product_id && (
<Button

View file

@ -12,6 +12,7 @@ import { Badge } from '@/components/ui/badge'
import { StoryLog } from '@/components/shared/story-log'
import { JOB_STATUS_LABELS, JOB_STATUS_COLORS } from '@/components/shared/job-status'
import type { ClaudeJobStatusApi } from '@/lib/job-status'
import { debugProps } from '@/lib/debug'
import type { IdeaSyncData } from '@/app/(app)/ideas/[id]/sync-tab-server'
interface Props {
@ -88,7 +89,7 @@ export function IdeaSyncTab({ data }: Props) {
if (!pbi) return null
return (
<div className="space-y-4">
<div className="space-y-4" {...debugProps('idea-sync-tab', 'IdeaSyncTab', 'components/ideas/idea-sync-tab.tsx')}>
{/* Header: PBI-link + PR-status */}
<div className="flex flex-wrap items-center gap-3 rounded-md border border-border bg-surface-container p-3">
<a

View file

@ -25,6 +25,7 @@ import { toast } from 'sonner'
import { Button } from '@/components/ui/button'
import { Textarea } from '@/components/ui/textarea'
import { debugProps } from '@/lib/debug'
import { answerQuestion } from '@/actions/questions'
import { UserChatInput } from '@/components/ideas/user-chat-input'
@ -124,7 +125,7 @@ export function IdeaTimeline({
const showChatInput = planMd !== null
return (
<div className="space-y-4">
<div className="space-y-4" {...debugProps('idea-timeline', 'IdeaTimeline', 'components/ideas/idea-timeline.tsx')}>
{merged.length === 0 ? (
<p className="text-sm text-muted-foreground py-8 text-center italic">
Nog geen activiteit op dit idee.

View file

@ -7,6 +7,7 @@ import { toast } from 'sonner'
import { Button } from '@/components/ui/button'
import { Textarea } from '@/components/ui/textarea'
import { debugProps } from '@/lib/debug'
import { createUserQuestionAction } from '@/actions/user-questions'
interface Props {
@ -39,7 +40,7 @@ export function UserChatInput({ ideaId, isDemo = false }: Props) {
if (isDemo) {
return (
<div className="rounded-md border border-input bg-surface-container p-3">
<div className="rounded-md border border-input bg-surface-container p-3" {...debugProps('user-chat-input', 'UserChatInput', 'components/ideas/user-chat-input.tsx')}>
<p className="text-xs text-muted-foreground italic">
Demo-modus: vragen stellen is niet beschikbaar.
</p>
@ -48,7 +49,7 @@ export function UserChatInput({ ideaId, isDemo = false }: Props) {
}
return (
<div className="space-y-2 rounded-md border border-input bg-surface-container p-3">
<div className="space-y-2 rounded-md border border-input bg-surface-container p-3" {...debugProps('user-chat-input', 'UserChatInput', 'components/ideas/user-chat-input.tsx')}>
<label className="text-xs font-medium text-muted-foreground">
Stel een vraag over dit plan
</label>

View file

@ -1,6 +1,7 @@
'use client'
import { cn } from '@/lib/utils'
import { debugProps } from '@/lib/debug'
import { JOB_STATUS_LABELS, JOB_STATUS_COLORS } from '@/components/shared/job-status'
import { jobStatusToApi } from '@/lib/job-status'
import type { ClaudeJobKind, ClaudeJobStatus } from '@prisma/client'
@ -61,6 +62,7 @@ export default function JobCard({
'border rounded-lg p-3 cursor-pointer hover:bg-surface-container transition-colors text-sm',
isSelected && 'ring-2 ring-primary',
)}
{...debugProps('job-card', 'JobCard', 'components/jobs/job-card.tsx')}
>
<div className="flex justify-between items-center gap-2">
<span className="text-[10px] px-1.5 py-0.5 rounded border bg-muted text-muted-foreground font-mono">

View file

@ -7,6 +7,7 @@ import { JOB_STATUS_LABELS, JOB_STATUS_COLORS } from '@/components/shared/job-st
import { jobStatusToApi } from '@/lib/job-status'
import type { JobWithRelations } from '@/actions/jobs-page'
import { Button } from '@/components/ui/button'
import { debugProps } from '@/lib/debug'
import { DemoTooltip } from '@/components/shared/demo-tooltip'
import { restartClaudeJobAction } from '@/actions/claude-jobs'
@ -57,7 +58,7 @@ export default function JobDetailPane({ job, isDemo }: JobDetailPaneProps) {
if (!job) {
return (
<div className="flex items-center justify-center h-full text-sm text-muted-foreground">
<div className="flex items-center justify-center h-full text-sm text-muted-foreground" {...debugProps('job-detail-pane', 'JobDetailPane', 'components/jobs/job-detail-pane.tsx')}>
Selecteer een job om details te zien
</div>
)
@ -75,7 +76,7 @@ export default function JobDetailPane({ job, isDemo }: JobDetailPaneProps) {
}
return (
<div className="overflow-y-auto h-full p-4">
<div className="overflow-y-auto h-full p-4" {...debugProps('job-detail-pane', 'JobDetailPane', 'components/jobs/job-detail-pane.tsx')}>
<FieldRow label="Status">
<span className={cn('text-xs px-2 py-0.5 rounded-full border font-medium', JOB_STATUS_COLORS[apiStatus])}>
{JOB_STATUS_LABELS[apiStatus]}

View file

@ -1,5 +1,6 @@
'use client'
import { debugProps } from '@/lib/debug'
import type { JobWithRelations } from '@/actions/jobs-page'
interface FieldRowProps {
@ -42,7 +43,7 @@ interface JobUsagePaneProps {
export default function JobUsagePane({ job }: JobUsagePaneProps) {
if (!job) {
return (
<div className="flex items-center justify-center h-full text-sm text-muted-foreground">
<div className="flex items-center justify-center h-full text-sm text-muted-foreground" {...debugProps('job-usage-pane', 'JobUsagePane', 'components/jobs/job-usage-pane.tsx')}>
Selecteer een job om gebruik te zien
</div>
)
@ -57,7 +58,7 @@ export default function JobUsagePane({ job }: JobUsagePaneProps) {
const costLabel = job.costUsd != null ? `$${job.costUsd.toFixed(4)}` : '—'
return (
<div className="overflow-y-auto h-full p-4">
<div className="overflow-y-auto h-full p-4" {...debugProps('job-usage-pane', 'JobUsagePane', 'components/jobs/job-usage-pane.tsx')}>
<FieldRow label="Model">{job.modelId ?? '—'}</FieldRow>
<FieldRow label="Tokens invoer">{formatNumber(job.inputTokens)}</FieldRow>
<FieldRow label="Tokens uitvoer">{formatNumber(job.outputTokens)}</FieldRow>

View file

@ -7,6 +7,7 @@ import JobsColumn from './jobs-column'
import JobDetailPane from './job-detail-pane'
import JobUsagePane from './job-usage-pane'
import SprintSubTasksPane from './sprint-sub-tasks-pane'
import { debugProps } from '@/lib/debug'
import { useJobsStore } from '@/stores/jobs-store'
import useJobsRealtime from '@/hooks/use-jobs-realtime'
import type { ClaudeJobStatusApi } from '@/lib/job-status'
@ -96,11 +97,13 @@ export default function JobsBoard({ initialActiveJobs, initialDoneJobs, isDemo }
)
return (
<SplitPane
panes={[leftPane, middlePane, rightPane]}
defaultSplit={[25, 50, 25]}
cookieKey="jobs"
tabLabels={['Actief', 'Details', 'Klaar']}
/>
<div className="h-full" {...debugProps('jobs-board', 'JobsBoard', 'components/jobs/jobs-board.tsx')}>
<SplitPane
panes={[leftPane, middlePane, rightPane]}
defaultSplit={[25, 50, 25]}
cookieKey="jobs"
tabLabels={['Actief', 'Details', 'Klaar']}
/>
</div>
)
}

View file

@ -7,6 +7,7 @@ import JobCard from './job-card'
import { JOB_STATUS_LABELS } from '@/components/shared/job-status'
import { jobStatusToApi, type ClaudeJobStatusApi } from '@/lib/job-status'
import { cn } from '@/lib/utils'
import { debugProps } from '@/lib/debug'
import type { JobWithRelations } from '@/actions/jobs-page'
import type { ClaudeJobKind } from '@prisma/client'
@ -170,7 +171,7 @@ export default function JobsColumn({
const activeFilterCount = filterKinds.size + filterStatuses.size
return (
<div className="flex flex-col h-full">
<div className="flex flex-col h-full" {...debugProps('jobs-column', 'JobsColumn', 'components/jobs/jobs-column.tsx')}>
<div className="flex items-center justify-between gap-2 px-2 py-1.5 border-b border-border bg-surface-container-low shrink-0">
<span className="text-xs font-medium text-muted-foreground px-1">{title}</span>
<div className="flex items-center gap-1.5 flex-wrap justify-end">

View file

@ -2,6 +2,7 @@
import { useEffect, useState } from 'react'
import { cn } from '@/lib/utils'
import { debugProps } from '@/lib/debug'
import { JOB_STATUS_LABELS, JOB_STATUS_COLORS } from '@/components/shared/job-status'
import type { ClaudeJobStatusApi } from '@/lib/job-status'
@ -63,5 +64,9 @@ function SubTaskList({ jobId }: { jobId: string }) {
export default function SprintSubTasksPane({ jobId, isSprintJob }: SprintSubTasksPaneProps) {
if (!isSprintJob || !jobId) return null
return <SubTaskList key={jobId} jobId={jobId} />
return (
<div {...debugProps('sprint-sub-tasks-pane', 'SprintSubTasksPane', 'components/jobs/sprint-sub-tasks-pane.tsx')}>
<SubTaskList key={jobId} jobId={jobId} />
</div>
)
}