feat(PBI-49): add debugProps to backlog/, sprint/, solo/ components

This commit is contained in:
Scrum4Me Agent 2026-05-09 20:57:56 +02:00
parent f555cb547b
commit 6c004ffc74
22 changed files with 50 additions and 18 deletions

View file

@ -3,6 +3,7 @@
import { forwardRef } from 'react'
import { cn } from '@/lib/utils'
import { CodeBadge } from '@/components/shared/code-badge'
import { debugProps } from '@/lib/debug'
export const PRIORITY_BORDER: Record<number, string> = {
1: 'border-l-4 border-l-priority-critical',
@ -38,6 +39,7 @@ export const BacklogCard = forwardRef<HTMLDivElement, BacklogCardProps>(function
className,
)}
{...rest}
{...debugProps('backlog-card', 'BacklogCard', 'components/backlog/backlog-card.tsx')}
>
<div className="flex items-start justify-between gap-2">
<p className="text-sm leading-snug line-clamp-2 flex-1">{title}</p>

View file

@ -2,6 +2,7 @@
import { Button } from '@/components/ui/button'
import { DemoTooltip } from '@/components/shared/demo-tooltip'
import { debugProps } from '@/lib/debug'
interface EmptyPanelProps {
title?: string
@ -15,7 +16,7 @@ interface EmptyPanelProps {
export function EmptyPanel({ title, message, action }: EmptyPanelProps) {
return (
<div className="p-8 text-center text-muted-foreground space-y-3">
<div className="p-8 text-center text-muted-foreground space-y-3" {...debugProps('empty-panel', 'EmptyPanel', 'components/backlog/empty-panel.tsx')}>
{title && <p className="text-sm font-medium text-foreground">{title}</p>}
<p className="text-sm">{message}</p>
{action && (

View file

@ -27,6 +27,7 @@ import {
} from '@/components/shared/entity-dialog-layout'
import { createPbiAction, updatePbiAction } from '@/actions/pbis'
import type { PbiStatusApi } from '@/lib/task-status'
import { debugProps } from '@/lib/debug'
export interface PbiDialogPbi {
id: string
@ -120,6 +121,7 @@ export function PbiDialog({ state, onClose, isDemo = false }: PbiDialogProps) {
showCloseButton={false}
onKeyDown={handleKeyDown}
className={entityDialogContentClasses}
{...debugProps('pbi-dialog', 'PbiDialog', 'components/backlog/pbi-dialog.tsx')}
>
<div className={entityDialogHeaderClasses}>
<DialogTitle className="text-xl font-semibold">

View file

@ -31,6 +31,7 @@ import { useBacklogStore } from '@/stores/backlog-store'
import { deletePbiAction } from '@/actions/pbis'
import { reorderPbisAction, updatePbiPriorityAction } from '@/actions/stories'
import { cn } from '@/lib/utils'
import { debugProps } from '@/lib/debug'
import { PbiDialog, type PbiDialogState } from './pbi-dialog'
import { BacklogCard } from './backlog-card'
import { EmptyPanel } from './empty-panel'
@ -390,7 +391,7 @@ export function PbiList({ productId, isDemo }: PbiListProps) {
const activePbi = activeDragId ? pbiMap[activeDragId] : null
return (
<div className="flex flex-col h-full">
<div className="flex flex-col h-full" {...debugProps('pbi-list', 'PbiList', 'components/backlog/pbi-list.tsx')}>
<div className="flex items-center justify-end gap-2 px-4 py-2 border-b border-border bg-surface-container-low shrink-0">
{filterPriority !== 'all' && (
<button

View file

@ -38,6 +38,7 @@ import {
} from '@/components/shared/entity-dialog-layout'
import { createStoryAction, updateStoryAction, deleteStoryAction, getStoryLogsAction } from '@/actions/stories'
import { cn } from '@/lib/utils'
import { debugProps } from '@/lib/debug'
import type { Story } from './story-panel'
export type StoryDialogState =
@ -147,6 +148,7 @@ export function StoryDialog({ state, onClose, isDemo = false }: StoryDialogProps
showCloseButton={false}
onKeyDown={handleKeyDown}
className={entityDialogContentClasses}
{...debugProps('story-dialog', 'StoryDialog', 'components/backlog/story-dialog.tsx')}
>
<div className={cn(entityDialogHeaderClasses, 'flex-col items-stretch gap-1')}>
<div className="flex items-start gap-2">

View file

@ -30,6 +30,7 @@ import { usePlannerStore } from '@/stores/planner-store'
import { useBacklogStore } from '@/stores/backlog-store'
import { reorderStoriesAction } from '@/actions/stories'
import { StoryDialog, type StoryDialogState } from './story-dialog'
import { debugProps } from '@/lib/debug'
import { BacklogCard } from './backlog-card'
import { EmptyPanel } from './empty-panel'
import { DemoTooltip } from '@/components/shared/demo-tooltip'
@ -210,7 +211,7 @@ export function StoryPanel({ productId, isDemo }: StoryPanelProps) {
const hasActiveFilters = filterStatus !== null || filterPriority !== null
return (
<div className="flex flex-col h-full">
<div className="flex flex-col h-full" {...debugProps('story-panel', 'StoryPanel', 'components/backlog/story-panel.tsx')}>
<PanelNavBar
title="Stories"
actions={

View file

@ -30,6 +30,7 @@ import { useSelectionStore } from '@/stores/selection-store'
import { useBacklogStore, type BacklogTask } from '@/stores/backlog-store'
import { reorderTasksAction } from '@/actions/tasks'
import { BacklogCard } from './backlog-card'
import { debugProps } from '@/lib/debug'
import { EmptyPanel } from './empty-panel'
import { cn } from '@/lib/utils'
@ -158,9 +159,11 @@ export function TaskPanel({ isDemo, closePath }: TaskPanelProps) {
</DemoTooltip>
)
const dp = debugProps('task-panel', 'TaskPanel', 'components/backlog/task-panel.tsx')
if (tasks === null) {
return (
<div className="flex flex-col h-full">
<div className="flex flex-col h-full" {...dp}>
<PanelNavBar title="Taken" actions={navActions} />
<EmptyPanel message="Selecteer een story om de taken te bekijken." />
</div>
@ -169,7 +172,7 @@ export function TaskPanel({ isDemo, closePath }: TaskPanelProps) {
if (tasks.length === 0) {
return (
<div className="flex flex-col h-full">
<div className="flex flex-col h-full" {...dp}>
<PanelNavBar title="Taken" actions={navActions} />
<EmptyPanel
message="Nog geen taken voor deze story."
@ -186,7 +189,7 @@ export function TaskPanel({ isDemo, closePath }: TaskPanelProps) {
const activeTask = activeDragId ? tasks.find((t) => t.id === activeDragId) : null
return (
<div className="flex flex-col h-full">
<div className="flex flex-col h-full" {...dp}>
<PanelNavBar title="Taken" actions={navActions} />
<div className="flex-1 overflow-y-auto p-3">
<DndContext

View file

@ -8,6 +8,7 @@ import {
entityDialogFooterClasses,
entityDialogHeaderClasses,
} from '@/components/shared/entity-dialog-layout'
import { debugProps } from '@/lib/debug'
interface BatchEnqueueBlockerDialogProps {
open: boolean
@ -37,7 +38,7 @@ export function BatchEnqueueBlockerDialog({
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent showCloseButton={false} className={entityDialogContentClasses}>
<DialogContent showCloseButton={false} className={entityDialogContentClasses} {...debugProps('batch-enqueue-blocker-dialog', 'BatchEnqueueBlockerDialog', 'components/solo/batch-enqueue-blocker-dialog.tsx')}>
<div className={entityDialogHeaderClasses}>
<DialogTitle className="text-xl font-semibold">Blokkade gedetecteerd</DialogTitle>
</div>

View file

@ -4,6 +4,7 @@ import { useSoloStore } from '@/stores/solo-store'
import type { RealtimeStatus } from '@/stores/solo-store'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
import { cn } from '@/lib/utils'
import { debugProps } from '@/lib/debug'
function RealtimeIndicator({
status,
@ -63,7 +64,7 @@ export function SoloNavStatusIndicators({
workerQuotaPct < minQuotaPct
return (
<div className="flex items-center gap-3 px-2">
<div className="flex items-center gap-3 px-2" {...debugProps('solo-nav-status-indicators', 'SoloNavStatusIndicators', 'components/solo/nav-status-indicators.tsx')}>
<RealtimeIndicator
status={realtimeStatus}
showConnectingIndicator={showConnectingIndicator}

View file

@ -1,4 +1,5 @@
import Link from 'next/link'
import { debugProps } from '@/lib/debug'
interface NoActiveSprintProps {
productId: string
@ -7,7 +8,7 @@ interface NoActiveSprintProps {
export function NoActiveSprint({ productId, productName }: NoActiveSprintProps) {
return (
<div className="flex flex-col items-center justify-center h-full gap-4 text-center px-6">
<div className="flex flex-col items-center justify-center h-full gap-4 text-center px-6" {...debugProps('no-active-sprint', 'NoActiveSprint', 'components/solo/no-active-sprint.tsx')}>
<div className="text-4xl text-muted-foreground">🏃</div>
<h2 className="text-lg font-medium text-foreground">Geen actieve sprint</h2>
<p className="text-sm text-muted-foreground max-w-sm">

View file

@ -1,4 +1,5 @@
import Link from 'next/link'
import { debugProps } from '@/lib/debug'
interface Product {
id: string
@ -12,7 +13,7 @@ interface ProductPickerProps {
export function ProductPicker({ products }: ProductPickerProps) {
return (
<div className="p-6 max-w-2xl mx-auto w-full">
<div className="p-6 max-w-2xl mx-auto w-full" {...debugProps('product-picker', 'ProductPicker', 'components/solo/product-picker.tsx')}>
<h1 className="text-xl font-medium text-foreground mb-2">Solo bord</h1>
<p className="text-sm text-muted-foreground mb-6">
Kies een product om je persoonlijke Kanban-bord te openen.

View file

@ -10,6 +10,7 @@ import { useSoloStore } from '@/stores/solo-store'
import { taskStatusToApi } from '@/lib/task-status'
import { previewEnqueueAllAction, enqueueClaudeJobsBatchAction } from '@/actions/claude-jobs'
import { BatchEnqueueBlockerDialog } from './batch-enqueue-blocker-dialog'
import { debugProps } from '@/lib/debug'
import { Button } from '@/components/ui/button'
import { DemoTooltip } from '@/components/shared/demo-tooltip'
import { SplitPane } from '@/components/split-pane/split-pane'
@ -200,7 +201,7 @@ export function SoloBoard({
}
return (
<div className="flex flex-col h-full p-4 gap-4 min-h-0">
<div className="flex flex-col h-full p-4 gap-4 min-h-0" {...debugProps('solo-board', 'SoloBoard', 'components/solo/solo-board.tsx')}>
<div className="flex items-start justify-between gap-4 shrink-0">
<div className="min-w-0 flex items-center gap-3">
<DemoTooltip show={isDemo}>

View file

@ -3,6 +3,7 @@
import { useDroppable } from '@dnd-kit/core'
import { cn } from '@/lib/utils'
import { SoloTaskCard } from './solo-task-card'
import { debugProps } from '@/lib/debug'
import type { SoloTask } from './solo-board'
export const COLUMN_CONFIG = {
@ -40,6 +41,7 @@ export function SoloColumn({ status, tasks, isDemo, onTaskClick }: SoloColumnPro
'flex flex-col h-full rounded-lg border border-border overflow-hidden',
isOver && 'ring-2 ring-primary ring-inset',
)}
{...debugProps('solo-column', 'SoloColumn', 'components/solo/solo-column.tsx')}
>
<div className={cn('flex items-center gap-2 px-3 py-2', config.headerClass)}>
<span className="text-sm font-medium">{config.label}</span>

View file

@ -10,6 +10,7 @@ import { JOB_STATUS_LABELS, JOB_STATUS_COLORS, JOB_STATUS_ACTIVE } from '@/compo
import { useSoloStore } from '@/stores/solo-store'
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@/components/ui/tooltip'
import type { SoloTask } from './solo-board'
import { debugProps } from '@/lib/debug'
const PRIORITY_BORDER: Record<number, string> = {
1: 'border-l-4 border-l-priority-critical',
@ -47,6 +48,7 @@ export function SoloTaskCard({ task, isDemo, onClick }: SoloTaskCardProps) {
isDemo ? 'cursor-pointer' : 'cursor-grab active:cursor-grabbing',
)}
{...(!isDemo ? { ...attributes, ...listeners } : {})}
{...debugProps('solo-task-card', 'SoloTaskCard', 'components/solo/solo-task-card.tsx')}
>
{/* Regel 1: taaknaam + task_code */}
<div className="flex items-start justify-between gap-2">
@ -145,6 +147,7 @@ export function SoloTaskCardOverlay({ task }: { task: SoloTask }) {
'bg-surface-container rounded border border-primary px-3 py-2 shadow-xl opacity-90',
PRIORITY_BORDER[task.priority],
)}
{...debugProps('solo-task-card-overlay', 'SoloTaskCardOverlay', 'components/solo/solo-task-card.tsx')}
>
{/* Regel 1 */}
<div className="flex items-start justify-between gap-2">

View file

@ -19,6 +19,7 @@ import { useSoloStore } from '@/stores/solo-store'
import { enqueueClaudeJobAction, cancelClaudeJobAction } from '@/actions/claude-jobs'
import { cn } from '@/lib/utils'
import { getBranchUrl } from '@/lib/job-status-url'
import { debugProps } from '@/lib/debug'
import type { SoloTask } from './solo-board'
const STATUS_COLORS: Record<string, string> = {
@ -380,7 +381,7 @@ function TaskDetailContent({ task, productId, isDemo, repoUrl, onClose }: TaskDe
export function TaskDetailDialog({ task, productId, isDemo, repoUrl, onClose }: TaskDetailDialogProps) {
return (
<Dialog open={!!task} onOpenChange={(open) => { if (!open) onClose() }}>
<DialogContent showCloseButton={false} className={entityDialogContentClasses}>
<DialogContent showCloseButton={false} className={entityDialogContentClasses} {...debugProps('task-detail-dialog', 'TaskDetailDialog', 'components/solo/task-detail-dialog.tsx')}>
{task && (
<TaskDetailContent
key={task.id}

View file

@ -10,6 +10,7 @@ import { DemoTooltip } from '@/components/shared/demo-tooltip'
import { CodeBadge } from '@/components/shared/code-badge'
import { claimStoryAction } from '@/actions/stories'
import { cn } from '@/lib/utils'
import { debugProps } from '@/lib/debug'
export interface UnassignedStoryTask {
id: string
@ -156,7 +157,7 @@ export function UnassignedStoriesSheet({
return (
<Sheet open={open} onOpenChange={onOpenChange}>
<SheetContent side="right">
<SheetContent side="right" {...debugProps('unassigned-stories-sheet', 'UnassignedStoriesSheet', 'components/solo/unassigned-stories-sheet.tsx')}>
<SheetHeader>
<SheetTitle>Openstaande stories</SheetTitle>
</SheetHeader>

View file

@ -21,6 +21,7 @@ import {
entityDialogHeaderClasses,
} from '@/components/shared/entity-dialog-layout'
import { createSprintWithPbisAction } from '@/actions/sprints'
import { debugProps } from '@/lib/debug'
interface NewSprintDialogProps {
open: boolean
@ -102,6 +103,7 @@ export function NewSprintDialog({
showCloseButton={false}
onKeyDown={handleKeyDown}
className={entityDialogContentClasses}
{...debugProps('new-sprint-dialog', 'NewSprintDialog', 'components/sprint/new-sprint-dialog.tsx')}
>
<div className={entityDialogHeaderClasses}>
<DialogTitle className="text-xl font-semibold">Nieuwe sprint</DialogTitle>

View file

@ -26,6 +26,7 @@ import { PbiDialog, type PbiDialogState } from '@/components/backlog/pbi-dialog'
import { StoryDialog, type StoryDialogState } from '@/components/backlog/story-dialog'
import type { PbiStatusApi } from '@/lib/task-status'
import { cn } from '@/lib/utils'
import { debugProps } from '@/lib/debug'
const STATUS_COLORS: Record<string, string> = {
OPEN: 'bg-status-todo/15 text-status-todo border-status-todo/30',
@ -276,7 +277,7 @@ export function SprintBacklogLeft({
const orderedStories = order.map(id => storyMap[id]).filter(Boolean)
return (
<div className="flex flex-col h-full">
<div className="flex flex-col h-full" {...debugProps('sprint-backlog-left', 'SprintBacklogLeft', 'components/sprint/sprint-backlog.tsx')}>
<PanelNavBar
title="Sprint Backlog"
actions={
@ -631,7 +632,7 @@ export function SprintBacklogRight({ pbisWithStories, sprintStoryIds, isDemo, pr
)
return (
<div className="flex flex-col h-full">
<div className="flex flex-col h-full" {...debugProps('sprint-backlog-right', 'SprintBacklogRight', 'components/sprint/sprint-backlog.tsx')}>
<PanelNavBar title="Product Backlog" actions={headerActions} />
<div
ref={setNodeRef}

View file

@ -32,6 +32,7 @@ import {
} from '@/components/shared/entity-dialog-layout'
import { updateSprintGoalAction, updateSprintDatesAction, completeSprintAction, setAllSprintTasksDoneAction } from '@/actions/sprints'
import type { SprintStory } from './sprint-backlog'
import { debugProps } from '@/lib/debug'
interface Sprint {
id: string
@ -130,7 +131,7 @@ export function SprintHeader({ productId: _productId, productName, sprint, isDem
}
return (
<div className="px-4 py-3 border-b border-border bg-surface-container-low shrink-0">
<div className="px-4 py-3 border-b border-border bg-surface-container-low shrink-0" {...debugProps('sprint-header', 'SprintHeader', 'components/sprint/sprint-header.tsx')}>
<div className="flex items-center justify-between gap-4">
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2">

View file

@ -19,6 +19,7 @@ import {
DialogTitle,
} from '@/components/ui/dialog'
import { type PauseContext, pauseReasonLabel } from '@/lib/pause-context'
import { debugProps } from '@/lib/debug'
type SprintStatusValue = 'OPEN' | 'CLOSED' | 'ARCHIVED' | 'FAILED'
type SprintRunStatusValue =
@ -210,7 +211,7 @@ export function SprintRunControls({
</div>
<Dialog open={blockers !== null} onOpenChange={(open) => { if (!open) setBlockers(null) }}>
<DialogContent className="max-w-lg">
<DialogContent className="max-w-lg" {...debugProps('sprint-run-controls', 'SprintRunControls', 'components/sprint/sprint-run-controls.tsx')}>
<DialogHeader>
<DialogTitle>Sprint kan nog niet starten</DialogTitle>
<DialogDescription>

View file

@ -22,6 +22,7 @@ import {
} from '@/components/shared/entity-dialog-layout'
import { createSprintAction } from '@/actions/sprints'
import { useSelectionStore } from '@/stores/selection-store'
import { debugProps } from '@/lib/debug'
import { useBacklogStore } from '@/stores/backlog-store'
interface StartSprintButtonProps {
@ -89,6 +90,7 @@ export function StartSprintButton({ productId, isDemo = false }: StartSprintButt
showCloseButton={false}
onKeyDown={handleKeyDown}
className={entityDialogContentClasses}
{...debugProps('start-sprint-button', 'StartSprintButton', 'components/sprint/start-sprint-button.tsx')}
>
<div className={entityDialogHeaderClasses}>
<DialogTitle className="text-xl font-semibold">Nieuwe Sprint starten</DialogTitle>

View file

@ -21,6 +21,7 @@ import { PRIORITY_BORDER } from '@/components/backlog/backlog-card'
import { useSprintStore } from '@/stores/sprint-store'
import { updateTaskStatusAction, reorderTasksAction } from '@/actions/tasks'
import { DemoTooltip } from '@/components/shared/demo-tooltip'
import { debugProps } from '@/lib/debug'
import { cn } from '@/lib/utils'
const STATUS_CYCLE: Record<string, 'TO_DO' | 'IN_PROGRESS' | 'DONE'> = {
@ -200,7 +201,7 @@ export function TaskList({ storyId, sprintId: _sprintId, productId: _productId,
}
return (
<div className="flex flex-col h-full">
<div className="flex flex-col h-full" {...debugProps('task-list', 'TaskList', 'components/sprint/task-list.tsx')}>
<PanelNavBar
title="Taken"
actions={