From d292e445d9b91ac433b3d27445a2b2e6557a5968 Mon Sep 17 00:00:00 2001 From: Janpeter Visser <30029041+madhura68@users.noreply.github.com> Date: Sat, 9 May 2026 22:46:29 +0200 Subject: [PATCH] Sprint: Verbeteren debug mode (#179) * feat(PBI-49): add debugProps helper + Vitest test Adds lib/debug.ts with debugProps(id, component, file) that returns data-debug-id and data-debug-label attrs in dev mode, empty object in production. Adds __tests__/lib/debug.test.ts covering both modes. Co-Authored-By: Claude Sonnet 4.6 * docs(PBI-49): add debug-id pattern doc + CLAUDE.md reference Adds docs/patterns/debug-id.md documenting the named-component boundary rule (6 punten), helper-voorbeeld, skip-criteria en motivatie voor handmatige pad-argumenten. Voegt verwijzing toe aan CLAUDE.md patterns-tabel. Co-Authored-By: Claude Sonnet 4.6 * refactor(PBI-49): migrate 17 shared/ components to debugProps helper Replace hardcoded data-debug-id + data-debug-label attribute pairs with {...debugProps(id, component, file)} spread in all 17 components/shared/ files. Existing debug-ids preserved unchanged. * feat(PBI-49): add debugProps to backlog/, sprint/, solo/ components * feat(PBI-49): add debugProps to jobs/ + ideas/ components * feat(PBI-49): add debugProps to products/ + settings/ + notifications/ components * feat(PBI-49): add debugProps to admin/ + dashboard/ + dialogs/ + mobile/ + split-pane/ Co-Authored-By: Claude Sonnet 4.6 * fix(PBI-49): use attr(data-debug-id) for debug tooltip in globals.css * refactor(PBI-49): remove data-debug-label from debugProps helper + test Co-Authored-By: Claude Sonnet 4.6 * refactor(PBI-49): strip unused component/file args from debugProps in shared/ Co-Authored-By: Claude Sonnet 4.6 * feat(PBI-49): add BEM sub-element data-debug-id to StatusBar, NavBar, PanelNavBar Co-Authored-By: Claude Sonnet 4.6 * feat(PBI-49): add BEM sub-element data-debug-id to components/sprint/* - new-sprint-dialog: __submit on submit button - sprint-backlog: __list on SprintBacklogLeft + SprintBacklogRight scroll areas - sprint-board-client: root wrapper div (display:contents) + __drag-overlay - sprint-header: __title on goal button, __dates on dates button, __actions on action cluster - sprint-run-controls: root on controls div, __start/__cancel on action buttons; __blockers-dialog on dialog content - start-sprint-button: root on trigger button, __dialog on dialog content, __submit on submit button - sync-active-sprint-cookie: no debug-id (returns null, side-effect only), comment added Co-Authored-By: Claude Sonnet 4.6 * feat(PBI-49): add BEM sub-element data-debug-id to components/backlog/* * feat(PBI-49): add BEM sub-element data-debug-id to components/ideas/* * feat(PBI-49): add BEM sub-element data-debug-id to components/dashboard/* + components/markdown.tsx Co-Authored-By: Claude Sonnet 4.6 * feat(PBI-49): add BEM sub-element data-debug-id to new-product-button Co-Authored-By: Claude Sonnet 4.6 * feat(PBI-49): add BEM sub-element data-debug-id to components/solo/* Co-Authored-By: Claude Sonnet 4.6 * feat(PBI-49): add BEM sub-elements to nav-status-indicators Co-Authored-By: Claude Sonnet 4.6 * feat(PBI-49): add BEM sub-element data-debug-id to components/jobs/* Co-Authored-By: Claude Sonnet 4.6 * feat(PBI-49): add BEM sub-element data-debug-id to components/products/* * feat(PBI-49): add BEM sub-element data-debug-id to components/notifications/* - answer-modal: __content (scroll area), __submit (footer) - notifications-bridge: skip comment (bridge, non-rendering wrapper) - notifications-realtime-mount: skip comment (returns null) - notifications-sheet: __header, __items (questions list) - push-toggle: __switch (button), __label (button text) on subscribed/unsubscribed states * feat(PBI-49): add BEM sub-element data-debug-id to components/settings/* - leave-product-button: root only (single-button component) - min-quota-editor: __input (number input), __save (save button) - profile-editor: __username (bio/short-description input), __save (submit) - role-manager: __roles (checkbox list), __add (save button) - token-manager: __tokens (active tokens list), __generate (create button) Co-Authored-By: Claude Sonnet 4.6 * feat(PBI-49): add BEM sub-element data-debug-id to admin, auth, dialogs, entity-dialog, mobile, split-pane * docs(PBI-49): add debug-labels BEM pattern doc + CLAUDE.md entry Co-Authored-By: Claude Sonnet 4.6 --------- Co-authored-by: Claude Sonnet 4.6 --- CLAUDE.md | 2 + __tests__/lib/debug.test.ts | 23 ++++ app/globals.css | 2 +- components/admin/jobs-table.tsx | 9 +- components/admin/products-table.tsx | 7 +- components/admin/users-table.tsx | 7 +- components/auth/auth-form.tsx | 7 +- components/backlog/backlog-card.tsx | 4 +- components/backlog/empty-panel.tsx | 6 +- components/backlog/pbi-dialog.tsx | 5 +- components/backlog/pbi-list.tsx | 7 +- components/backlog/story-dialog.tsx | 5 +- components/backlog/story-panel.tsx | 5 +- components/backlog/task-panel.tsx | 10 +- components/dashboard/new-product-button.tsx | 5 +- components/dashboard/product-list.tsx | 8 +- components/dialogs/product-dialog.tsx | 5 +- .../entity-dialog/dirty-close-guard.tsx | 6 +- components/ideas/download-md-button.tsx | 2 + components/ideas/idea-detail-layout.tsx | 7 +- components/ideas/idea-list.tsx | 7 +- components/ideas/idea-md-editor.tsx | 5 +- components/ideas/idea-pbi-link-card.tsx | 8 +- components/ideas/idea-row-actions.tsx | 40 +++--- components/ideas/idea-sync-tab.tsx | 7 +- components/ideas/idea-timeline.tsx | 5 +- components/ideas/user-chat-input.tsx | 7 +- components/jobs/job-card.tsx | 8 +- components/jobs/job-detail-pane.tsx | 9 +- components/jobs/job-usage-pane.tsx | 25 ++-- components/jobs/jobs-board.tsx | 19 +-- components/jobs/jobs-column.tsx | 7 +- components/jobs/sprint-sub-tasks-pane.tsx | 9 +- components/markdown.tsx | 3 +- components/mobile/landscape-guard.tsx | 4 +- components/mobile/logout-button.tsx | 2 + components/mobile/mobile-tab-bar.tsx | 3 + components/notifications/answer-modal.tsx | 6 +- .../notifications/notifications-bridge.tsx | 1 + .../notifications-realtime-mount.tsx | 1 + .../notifications/notifications-sheet.tsx | 7 +- components/notifications/push-toggle.tsx | 21 ++-- .../products/archive-product-button.tsx | 4 +- components/products/auto-pr-toggle.tsx | 6 +- components/products/edit-product-button.tsx | 5 +- components/products/pr-strategy-select.tsx | 5 +- components/products/product-form.tsx | 7 +- components/products/team-manager.tsx | 7 +- components/settings/leave-product-button.tsx | 27 +++-- components/settings/min-quota-editor.tsx | 6 +- components/settings/profile-editor.tsx | 6 +- components/settings/role-manager.tsx | 7 +- components/settings/token-manager.tsx | 7 +- components/shared/activate-product-button.tsx | 3 +- components/shared/alert-toast.tsx | 3 +- components/shared/app-icon.tsx | 5 +- components/shared/code-badge.tsx | 4 +- components/shared/demo-tooltip.tsx | 5 +- components/shared/min-width-banner.tsx | 5 +- components/shared/nav-bar.tsx | 11 +- components/shared/notifications-bell.tsx | 3 +- components/shared/panel-nav-bar.tsx | 8 +- components/shared/pbi-status-select.tsx | 3 +- components/shared/priority-select.tsx | 3 +- components/shared/set-current-product.tsx | 3 +- components/shared/sprint-switcher.tsx | 5 +- components/shared/status-bar.tsx | 8 +- components/shared/story-log.tsx | 8 +- components/shared/user-avatar.tsx | 3 +- components/shared/user-menu.tsx | 3 +- .../solo/batch-enqueue-blocker-dialog.tsx | 8 +- components/solo/nav-status-indicators.tsx | 15 ++- components/solo/no-active-sprint.tsx | 6 +- components/solo/product-picker.tsx | 5 +- components/solo/realtime-bridge.tsx | 1 + components/solo/solo-board.tsx | 7 +- components/solo/solo-column.tsx | 6 +- components/solo/solo-task-card.tsx | 7 +- components/solo/task-detail-dialog.tsx | 5 +- components/solo/unassigned-stories-sheet.tsx | 7 +- components/split-pane/split-pane.tsx | 7 +- components/sprint/new-sprint-dialog.tsx | 3 + components/sprint/sprint-backlog.tsx | 7 +- components/sprint/sprint-board-client.tsx | 5 +- components/sprint/sprint-header.tsx | 9 +- components/sprint/sprint-run-controls.tsx | 7 +- components/sprint/start-sprint-button.tsx | 5 +- .../sprint/sync-active-sprint-cookie.tsx | 1 + components/sprint/task-list.tsx | 7 +- docs/INDEX.md | 2 + docs/patterns/debug-id.md | 64 ++++++++++ docs/patterns/debug-labels.md | 114 ++++++++++++++++++ lib/debug.ts | 14 +++ 93 files changed, 600 insertions(+), 218 deletions(-) create mode 100644 __tests__/lib/debug.test.ts create mode 100644 docs/patterns/debug-id.md create mode 100644 docs/patterns/debug-labels.md create mode 100644 lib/debug.ts diff --git a/CLAUDE.md b/CLAUDE.md index 72b4e92..4ed8cdc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -98,6 +98,8 @@ Volledige MCP-tool documentatie: [docs/runbooks/mcp-integration.md](./docs/runbo | Story met UI-component | `docs/patterns/story-with-ui-component.md` | | Web Push | `docs/patterns/web-push.md` | | Job-config resolver (PBI-67) | `lib/job-config.ts` ↔ `scrum4me-mcp/src/lib/job-config.ts` | +| Debug-id op component-root | `docs/patterns/debug-id.md` | +| Debug-labels (BEM) | `docs/patterns/debug-labels.md` | --- diff --git a/__tests__/lib/debug.test.ts b/__tests__/lib/debug.test.ts new file mode 100644 index 0000000..12a1e33 --- /dev/null +++ b/__tests__/lib/debug.test.ts @@ -0,0 +1,23 @@ +import { describe, it, expect, vi } from 'vitest' + +import { debugProps } from '@/lib/debug' + +describe('debugProps', () => { + it('returns data-debug-id attr in dev mode', () => { + const result = debugProps('sprint-board', 'SprintBoard', 'components/sprint/sprint-board.tsx') + expect(result).toEqual({ + 'data-debug-id': 'sprint-board', + }) + }) + + it('returns empty object in production mode', () => { + const original = process.env.NODE_ENV + try { + vi.stubEnv('NODE_ENV', 'production') + const result = debugProps('sprint-board', 'SprintBoard', 'components/sprint/sprint-board.tsx') + expect(result).toEqual({}) + } finally { + vi.stubEnv('NODE_ENV', original ?? 'test') + } + }) +}) diff --git a/app/globals.css b/app/globals.css index 6c32368..e8d3b09 100644 --- a/app/globals.css +++ b/app/globals.css @@ -11,7 +11,7 @@ body.debug-mode [data-debug-id] { position: relative; } body.debug-mode [data-debug-id]:hover::after { - content: attr(data-debug-label); + content: attr(data-debug-id); position: absolute; top: 0; left: 0; diff --git a/components/admin/jobs-table.tsx b/components/admin/jobs-table.tsx index cddf90b..a236bed 100644 --- a/components/admin/jobs-table.tsx +++ b/components/admin/jobs-table.tsx @@ -12,6 +12,7 @@ import { TableRow, } from '@/components/ui/table' import { cancelJobAction, deleteJobAction } from '@/actions/admin/jobs' +import { debugProps } from '@/lib/debug' type Job = { id: string @@ -100,7 +101,7 @@ function JobRow({ job }: { job: Job }) { function StatusTable({ jobs }: { jobs: Job[] }) { return ( - +
ID @@ -171,7 +172,7 @@ function CostRow({ job }: { job: Job }) { function CostsTable({ jobs }: { jobs: Job[] }) { return ( -
+
ID @@ -203,8 +204,8 @@ export function JobsTable({ jobs }: { jobs: Job[] }) { const [view, setView] = useState<'status' | 'costs'>('status') return ( -
-
+
+
+ Naam Eigenaar @@ -90,7 +91,7 @@ export function ProductsTable({ products }: { products: Product[] }) { Acties - + {products.length === 0 && ( diff --git a/components/admin/users-table.tsx b/components/admin/users-table.tsx index 172cd41..32161c5 100644 --- a/components/admin/users-table.tsx +++ b/components/admin/users-table.tsx @@ -26,6 +26,7 @@ import { updateUserRolesAction, setMustResetPasswordAction, } from '@/actions/admin/users' +import { debugProps } from '@/lib/debug' type UserWithRoles = { id: string @@ -190,8 +191,8 @@ export function UsersTable({ currentUserId: string }) { return ( -
- +
+ Gebruiker Email @@ -201,7 +202,7 @@ export function UsersTable({ Acties - + {users.map(user => ( {user.username} diff --git a/components/auth/auth-form.tsx b/components/auth/auth-form.tsx index 6ec179b..4879c57 100644 --- a/components/auth/auth-form.tsx +++ b/components/auth/auth-form.tsx @@ -4,13 +4,14 @@ import { useActionState } from 'react' import { useFormStatus } from 'react-dom' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' +import { debugProps } from '@/lib/debug' type ActionResult = { error: string | Record } | undefined function SubmitButton({ label }: { label: string }) { const { pending } = useFormStatus() return ( - ) @@ -34,7 +35,7 @@ export function AuthForm({ action, submitLabel }: AuthFormProps) { const errorMessage = getErrorMessage(state) return ( -
+
@@ -64,6 +66,7 @@ export function AuthForm({ action, submitLabel }: AuthFormProps) { minLength={8} placeholder="••••••••" className="bg-input-background border-border focus-visible:ring-primary" + data-debug-id="auth-form__password" /> diff --git a/components/backlog/backlog-card.tsx b/components/backlog/backlog-card.tsx index 7a93910..26fab89 100644 --- a/components/backlog/backlog-card.tsx +++ b/components/backlog/backlog-card.tsx @@ -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 = { 1: 'border-l-4 border-l-priority-critical', @@ -38,9 +39,10 @@ export const BacklogCard = forwardRef(function className, )} {...rest} + {...debugProps('backlog-card', 'BacklogCard', 'components/backlog/backlog-card.tsx')} >
-

{title}

+

{title}

{code && }
{(badge || actions) && ( diff --git a/components/backlog/empty-panel.tsx b/components/backlog/empty-panel.tsx index e48f688..6fd531b 100644 --- a/components/backlog/empty-panel.tsx +++ b/components/backlog/empty-panel.tsx @@ -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,8 +16,8 @@ interface EmptyPanelProps { export function EmptyPanel({ title, message, action }: EmptyPanelProps) { return ( -
- {title &&

{title}

} +
+ {title &&

{title}

}

{message}

{action && ( @@ -25,6 +26,7 @@ export function EmptyPanel({ title, message, action }: EmptyPanelProps) { variant="outline" disabled={action.disabled} onClick={action.disabled ? undefined : action.onClick} + {...debugProps('empty-panel__cta')} > {action.label} diff --git a/components/backlog/pbi-dialog.tsx b/components/backlog/pbi-dialog.tsx index 0efbba3..64664dc 100644 --- a/components/backlog/pbi-dialog.tsx +++ b/components/backlog/pbi-dialog.tsx @@ -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')} >
@@ -168,6 +170,7 @@ export function PbiDialog({ state, onClose, isDemo = false }: PbiDialogProps) { disabled={isDemo} aria-invalid={!!fieldError('title')} className={fieldError('title') ? 'border-error' : ''} + {...debugProps('pbi-dialog__title')} /> {fieldError('title') &&

{fieldError('title')}

}
@@ -207,7 +210,7 @@ export function PbiDialog({ state, onClose, isDemo = false }: PbiDialogProps) { Annuleren - diff --git a/components/backlog/pbi-list.tsx b/components/backlog/pbi-list.tsx index 588e0b0..77d8511 100644 --- a/components/backlog/pbi-list.tsx +++ b/components/backlog/pbi-list.tsx @@ -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,8 +391,8 @@ export function PbiList({ productId, isDemo }: PbiListProps) { const activePbi = activeDragId ? pbiMap[activeDragId] : null return ( -
-
+
+
{filterPriority !== 'all' && ( - diff --git a/components/backlog/story-panel.tsx b/components/backlog/story-panel.tsx index 9707a62..c1dd2c1 100644 --- a/components/backlog/story-panel.tsx +++ b/components/backlog/story-panel.tsx @@ -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 ( -
+
-
+
{selectedPbiId === null ? ( ) : rawStories.length === 0 ? ( diff --git a/components/backlog/task-panel.tsx b/components/backlog/task-panel.tsx index c3d7526..4f4f524 100644 --- a/components/backlog/task-panel.tsx +++ b/components/backlog/task-panel.tsx @@ -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' @@ -152,15 +153,18 @@ export function TaskPanel({ isDemo, closePath }: TaskPanelProps) { if (!selectedStoryId) return router.push(`${closePath}?newTask=1&storyId=${selectedStoryId}`) }} + {...debugProps('task-panel__actions')} > + Nieuwe taak ) + const dp = debugProps('task-panel', 'TaskPanel', 'components/backlog/task-panel.tsx') + if (tasks === null) { return ( -
+
@@ -169,7 +173,7 @@ export function TaskPanel({ isDemo, closePath }: TaskPanelProps) { if (tasks.length === 0) { return ( -
+
t.id === activeDragId) : null return ( -
+
- +
+ +
+

{showArchived ? 'Geen gearchiveerde producten.' @@ -69,7 +70,7 @@ export function ProductList({ products, isDemo, showArchived = false, activeProd } return ( -

+
{products.map(product => (
-
+
{product.code && } diff --git a/components/dialogs/product-dialog.tsx b/components/dialogs/product-dialog.tsx index a478fb9..20eeaea 100644 --- a/components/dialogs/product-dialog.tsx +++ b/components/dialogs/product-dialog.tsx @@ -28,6 +28,7 @@ import { import { productSchema, type ProductInput } from '@/lib/schemas/product' import { createProductAction, updateProductAction } from '@/actions/products' import { useProductsStore } from '@/stores/products-store' +import { debugProps } from '@/lib/debug' export interface ProductDialogProduct { id: string @@ -159,6 +160,7 @@ export function ProductDialog(props: Props) { showCloseButton={false} onKeyDown={handleKeyDown} className={entityDialogContentClasses} + {...debugProps('product-dialog', 'ProductDialog', 'components/dialogs/product-dialog.tsx')} >
@@ -170,6 +172,7 @@ export function ProductDialog(props: Props) { id="product-form" onSubmit={form.handleSubmit(onSubmit)} className={entityDialogBodyClasses} + data-debug-id="product-dialog__content" >
+
diff --git a/components/ideas/idea-md-editor.tsx b/components/ideas/idea-md-editor.tsx index 85c52bd..44a6368 100644 --- a/components/ideas/idea-md-editor.tsx +++ b/components/ideas/idea-md-editor.tsx @@ -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 ( -
+
{errors.length > 0 && (

@@ -138,6 +139,7 @@ export function IdeaMdEditor({ ideaId, kind, initialValue, onCancel }: Props) { onKeyDown={onKeyDown} rows={24} className="font-mono text-sm leading-relaxed" + data-debug-id="idea-md-editor__textarea" placeholder={ kind === 'grill' ? '# Idee — ...\n## Scope\n...' @@ -159,6 +161,7 @@ export function IdeaMdEditor({ ideaId, kind, initialValue, onCancel }: Props) { size="sm" onClick={save} disabled={!dirty || submitting || (errors.length > 0 && kind === 'plan')} + data-debug-id="idea-md-editor__save" > Opslaan diff --git a/components/ideas/idea-pbi-link-card.tsx b/components/ideas/idea-pbi-link-card.tsx index 1d47854..53a1490 100644 --- a/components/ideas/idea-pbi-link-card.tsx +++ b/components/ideas/idea-pbi-link-card.tsx @@ -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,9 +28,9 @@ export function IdeaPbiLinkCard({ idea, isDemo }: Props) { if (idea.pbi && idea.product_id) { return ( -

+
-

+

Gepland

@@ -37,6 +38,7 @@ export function IdeaPbiLinkCard({ idea, isDemo }: Props) { {idea.pbi.code} — {idea.pbi.title} @@ -62,7 +64,7 @@ export function IdeaPbiLinkCard({ idea, isDemo }: Props) { } return ( -

+

diff --git a/components/ideas/idea-row-actions.tsx b/components/ideas/idea-row-actions.tsx index 769a6cd..1a9350d 100644 --- a/components/ideas/idea-row-actions.tsx +++ b/components/ideas/idea-row-actions.tsx @@ -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 ( -

+
{/* Bekijk PBI — alleen zichtbaar in PLANNED */} {status === 'planned' && idea.pbi && idea.product_id && ( diff --git a/components/ideas/idea-sync-tab.tsx b/components/ideas/idea-sync-tab.tsx index b05e46b..a5dc0e0 100644 --- a/components/ideas/idea-sync-tab.tsx +++ b/components/ideas/idea-sync-tab.tsx @@ -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,9 +89,9 @@ export function IdeaSyncTab({ data }: Props) { if (!pbi) return null return ( -
+
{/* Header: PBI-link + PR-status */} - ) } diff --git a/components/ideas/idea-timeline.tsx b/components/ideas/idea-timeline.tsx index c6265d6..9fab88a 100644 --- a/components/ideas/idea-timeline.tsx +++ b/components/ideas/idea-timeline.tsx @@ -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,13 +125,13 @@ export function IdeaTimeline({ const showChatInput = planMd !== null return ( -
+
{merged.length === 0 ? (

Nog geen activiteit op dit idee.

) : ( -
    +
      {merged.map((entry, i) => { // Expliciete locale + format om SSR/CSR hydration-mismatch te voorkomen // (server-locale verschilde van browser-locale). diff --git a/components/ideas/user-chat-input.tsx b/components/ideas/user-chat-input.tsx index cbdb4bd..3ed7c7c 100644 --- a/components/ideas/user-chat-input.tsx +++ b/components/ideas/user-chat-input.tsx @@ -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 ( -
      +

      Demo-modus: vragen stellen is niet beschikbaar.

      @@ -48,7 +49,7 @@ export function UserChatInput({ ideaId, isDemo = false }: Props) { } return ( -
      +
      @@ -58,12 +59,14 @@ export function UserChatInput({ ideaId, isDemo = false }: Props) { rows={3} placeholder="Bijv. Waarom is gekozen voor X in plaats van Y?" disabled={pending} + data-debug-id="user-chat-input__textarea" />
      ) : ( -
        +
          {questions.map((q) => { // story-questions: forYou wanneer assignee = ingelogd; idee-vragen // zijn altijd "voor jou" (idee is strikt user_id-only). diff --git a/components/notifications/push-toggle.tsx b/components/notifications/push-toggle.tsx index 0351335..92a497a 100644 --- a/components/notifications/push-toggle.tsx +++ b/components/notifications/push-toggle.tsx @@ -10,6 +10,7 @@ import { subscribeToPush, unsubscribeFromPush, } from '@/lib/push-client' +import { debugProps } from '@/lib/debug' type PushStatus = | 'loading' @@ -85,7 +86,7 @@ export function PushToggle({ vapidPublicKey }: PushToggleProps) { if (status === 'ios-needs-install') { return ( -
          +
          Op iPhone/iPad: tik op het delen-icoon en kies{' '} Zet op beginscherm. Daarna kun je notificaties activeren.
          @@ -94,7 +95,7 @@ export function PushToggle({ vapidPublicKey }: PushToggleProps) { if (status === 'denied') { return ( -

          +

          Notificaties zijn geblokkeerd. Schakel ze in via je browser-instellingen.

          ) @@ -102,15 +103,19 @@ export function PushToggle({ vapidPublicKey }: PushToggleProps) { if (status === 'unsubscribed') { return ( - +
          + +
          ) } return ( - +
          + +
          ) } diff --git a/components/products/archive-product-button.tsx b/components/products/archive-product-button.tsx index a083e77..6a2244e 100644 --- a/components/products/archive-product-button.tsx +++ b/components/products/archive-product-button.tsx @@ -3,6 +3,7 @@ import { useState, useTransition } from 'react' import { Button } from '@/components/ui/button' import { archiveProductAction } from '@/actions/products' +import { debugProps } from '@/lib/debug' interface ArchiveProductButtonProps { productId: string @@ -20,7 +21,7 @@ export function ArchiveProductButton({ productId }: ArchiveProductButtonProps) { if (confirming) { return ( -
          +
          diff --git a/components/products/auto-pr-toggle.tsx b/components/products/auto-pr-toggle.tsx index 0158627..114f426 100644 --- a/components/products/auto-pr-toggle.tsx +++ b/components/products/auto-pr-toggle.tsx @@ -4,6 +4,7 @@ import { useState, useTransition } from 'react' import { cn } from '@/lib/utils' import { updateAutoPrAction } from '@/actions/products' import { toast } from 'sonner' +import { debugProps } from '@/lib/debug' interface AutoPrToggleProps { productId: string @@ -27,13 +28,14 @@ export function AutoPrToggle({ productId, initialValue }: AutoPrToggleProps) { } return ( -
          +
          - Automatisch PR aanmaken na succesvolle agent-job + Automatisch PR aanmaken na succesvolle agent-job
          ) } diff --git a/components/products/edit-product-button.tsx b/components/products/edit-product-button.tsx index 958eb1c..0805a23 100644 --- a/components/products/edit-product-button.tsx +++ b/components/products/edit-product-button.tsx @@ -4,6 +4,7 @@ import { useState } from 'react' import { Button } from '@/components/ui/button' import { DemoTooltip } from '@/components/shared/demo-tooltip' import { ProductDialog, type ProductDialogProduct } from '@/components/dialogs/product-dialog' +import { debugProps } from '@/lib/debug' interface Props { product: ProductDialogProduct @@ -16,7 +17,7 @@ export function EditProductButton({ product, isDemo = false, size = 'sm', varian const [open, setOpen] = useState(false) return ( - <> + @@ -34,16 +35,18 @@ export function LeaveProductButton({ productId, isDemo = false }: LeaveProductBu } return ( - - - + + + + + ) } diff --git a/components/settings/min-quota-editor.tsx b/components/settings/min-quota-editor.tsx index feaf2d2..f1e8147 100644 --- a/components/settings/min-quota-editor.tsx +++ b/components/settings/min-quota-editor.tsx @@ -5,6 +5,7 @@ import { toast } from 'sonner' import { Button } from '@/components/ui/button' import { DemoTooltip } from '@/components/shared/demo-tooltip' import { updateMinQuotaPctAction } from '@/actions/settings' +import { debugProps } from '@/lib/debug' interface MinQuotaEditorProps { currentValue: number @@ -27,7 +28,7 @@ export function MinQuotaEditor({ currentValue, isDemo }: MinQuotaEditorProps) { } return ( -
          +