Scrum4Me/components/product-docs/product-doc-folder-toggle.tsx
Madhura68 e32ae45eb7 feat(PBI-96/T-1072): /docs/settings page + folder-toggle
- app/(app)/products/[id]/docs/settings/page.tsx: server-route met
  breadcrumb + intro, geeft isOwner door aan toggle-component.
- components/product-docs/product-doc-folder-toggle.tsx: client met
  8 checkboxes (één per ProductDocFolder enum-lid). Owner kan
  toggelen → toggleProductDocFolderAction (optimistic update +
  rollback bij error). ProductMember (niet-owner) krijgt waarschuwing
  en disabled checkboxes. DemoTooltip-wrapped, demo kan niets togglen.
- Voetnoot legt anti-data-loss uit: "Folders uitzetten verwijdert
  geen bestaande docs".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 14:35:01 +02:00

142 lines
4.5 KiB
TypeScript

'use client'
// Folder-toggle UI voor /docs/settings. 8 checkboxes (één per
// ProductDocFolder enum-lid). Owner kan toggelen; ProductMember ziet
// read-only checkboxes met waarschuwing.
//
// DemoTooltip-wrapped per checkbox; demo-user kan niets togglen.
import { useState, useTransition } from 'react'
import { useRouter } from 'next/navigation'
import { toast } from 'sonner'
import { DemoTooltip } from '@/components/shared/demo-tooltip'
import { debugProps } from '@/lib/debug'
import {
PRODUCT_DOC_FOLDERS,
type ProductDocFolderApi,
} from '@/lib/schemas/product-doc'
import { productDocFolderToApi } from '@/lib/product-doc-folder'
import { toggleProductDocFolderAction } from '@/actions/product-docs'
import type { ProductDocFolder } from '@prisma/client'
const FOLDER_LABELS: Record<ProductDocFolderApi, string> = {
adr: 'ADRs — Architecture Decision Records',
architecture: 'Architecture — Topische arch-bestanden',
patterns: 'Patterns — Herbruikbare code-patronen',
plans: 'Plans — Feature- en PBI-plannen',
runbooks: 'Runbooks — Operationele procedures',
specs: 'Specs — Functionele specificaties',
manual: 'Manual — Developer manual',
api: 'API — API-contract details',
}
interface Props {
productId: string
/** Folders die nu enabled zijn (DB-enum). */
initialEnabledFolders: ProductDocFolder[]
isOwner: boolean
isDemo: boolean
}
export function ProductDocFolderToggle({
productId,
initialEnabledFolders,
isOwner,
isDemo,
}: Props) {
const router = useRouter()
const initialApiSet = new Set(
initialEnabledFolders.map((f) => productDocFolderToApi(f)),
)
const [enabledSet, setEnabledSet] = useState<Set<ProductDocFolderApi>>(initialApiSet)
const [pendingFolder, setPendingFolder] = useState<ProductDocFolderApi | null>(null)
const [submitting, startSubmit] = useTransition()
function toggle(folder: ProductDocFolderApi) {
const currentlyEnabled = enabledSet.has(folder)
const targetEnabled = !currentlyEnabled
// Optimistic update
const next = new Set(enabledSet)
if (targetEnabled) next.add(folder)
else next.delete(folder)
setEnabledSet(next)
setPendingFolder(folder)
startSubmit(async () => {
const r = await toggleProductDocFolderAction({
product_id: productId,
folder,
enabled: targetEnabled,
})
if ('error' in r) {
// Rollback
setEnabledSet(enabledSet)
toast.error(r.error)
} else {
toast.success(targetEnabled ? `Folder ${folder} aangezet` : `Folder ${folder} uitgezet`)
router.refresh()
}
setPendingFolder(null)
})
}
return (
<div
className="space-y-3"
{...debugProps(
'product-doc-folder-toggle',
'ProductDocFolderToggle',
'components/product-docs/product-doc-folder-toggle.tsx',
)}
>
{!isOwner && (
<p className="text-xs text-muted-foreground italic">
Alleen de eigenaar van dit product kan folders aan- of uitzetten.
</p>
)}
<ul className="space-y-2">
{PRODUCT_DOC_FOLDERS.map((folder) => {
const checked = enabledSet.has(folder)
const isPending = pendingFolder === folder
const disabled = !isOwner || isDemo || submitting
return (
<li key={folder}>
<DemoTooltip show={isDemo}>
<label
className={`flex items-start gap-2 rounded-md border border-border p-2 ${
disabled ? 'opacity-70' : 'hover:bg-surface-container/60 cursor-pointer'
}`}
data-debug-id={`product-doc-folder-toggle__row--${folder}`}
>
<input
type="checkbox"
checked={checked}
disabled={disabled}
onChange={() => toggle(folder)}
className="mt-0.5 accent-primary"
/>
<div className="flex-1 text-xs">
<p className="font-medium">{FOLDER_LABELS[folder]}</p>
{isPending && (
<p className="text-muted-foreground text-[10px]">Bezig</p>
)}
</div>
</label>
</DemoTooltip>
</li>
)
})}
</ul>
<p className="text-xs text-muted-foreground">
Folders uitzetten verwijdert geen bestaande docs die blijven leesbaar
via directe URL en kunnen worden verwijderd voor cleanup.
</p>
</div>
)
}