feat(PBI-98/T-1088): ProductRowActions (Activeer/Docs/Backlog + dropdown Archive)
- components/dashboard/product-row-actions.tsx: inline knoppen Activeer (via ActivateProductButton) of "Actief"-badge, Docs (BookOpen-icon → /docs), Open backlog (ArrowRight-icon → /products/[id]). Dropdown (•••) met Archiveer/Herstel toggle. - Archive: archiveProductAction (redirect /dashboard) of restoreProductAction (success + router.refresh). - Verwijderen-item komt in T-1089 zodra deleteProductAction bestaat. - DemoTooltip op dropdown-trigger (writes blokkeren); Docs/Backlog blijven klikbaar voor demo. - Wires ProductRowActions in ProductsTable acties-kolom. - 1028 tests blijven groen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ba37c8e8f2
commit
972e415fc9
2 changed files with 146 additions and 4 deletions
139
components/dashboard/product-row-actions.tsx
Normal file
139
components/dashboard/product-row-actions.tsx
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
'use client'
|
||||
|
||||
// Per-rij acties in de Dashboard ProductsTable. Inline: Activeer (of
|
||||
// "Actief"-badge), Docs, Open backlog. Dropdown: Archive/Restore toggle.
|
||||
// Verwijderen-item wordt in T-1089 toegevoegd zodra deleteProductAction
|
||||
// bestaat. Alle write-knoppen DemoTooltip-wrapped + stopPropagation om
|
||||
// te voorkomen dat een klik de rij-edit-dialog opent.
|
||||
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useTransition } from 'react'
|
||||
import { ArrowRight, BookOpen, MoreHorizontal } from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { ActivateProductButton } from '@/components/shared/activate-product-button'
|
||||
import { DemoTooltip } from '@/components/shared/demo-tooltip'
|
||||
import {
|
||||
archiveProductAction,
|
||||
restoreProductAction,
|
||||
} from '@/actions/products'
|
||||
import { debugProps } from '@/lib/debug'
|
||||
|
||||
interface Props {
|
||||
productId: string
|
||||
isActive: boolean
|
||||
isArchived: boolean
|
||||
isDemo: boolean
|
||||
}
|
||||
|
||||
export function ProductRowActions({
|
||||
productId,
|
||||
isActive,
|
||||
isArchived,
|
||||
isDemo,
|
||||
}: Props) {
|
||||
const router = useRouter()
|
||||
const [submitting, startSubmit] = useTransition()
|
||||
|
||||
function handleArchiveToggle() {
|
||||
startSubmit(async () => {
|
||||
if (isArchived) {
|
||||
const r = await restoreProductAction(productId)
|
||||
if (r && 'error' in r && r.error) {
|
||||
toast.error(r.error)
|
||||
} else {
|
||||
toast.success('Product hersteld')
|
||||
router.refresh()
|
||||
}
|
||||
} else {
|
||||
// archiveProductAction doet redirect('/dashboard') — geen handmatige
|
||||
// router.refresh nodig; bij fout returnt 'ie { error }.
|
||||
const r = await archiveProductAction(productId)
|
||||
if (r && 'error' in r && r.error) {
|
||||
toast.error(r.error)
|
||||
} else {
|
||||
toast.success('Product gearchiveerd')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="inline-flex items-center gap-1"
|
||||
{...debugProps(
|
||||
'product-row-actions',
|
||||
'ProductRowActions',
|
||||
'components/dashboard/product-row-actions.tsx',
|
||||
)}
|
||||
>
|
||||
{isActive ? (
|
||||
<Badge className="bg-primary-container text-primary-container-foreground text-[10px] px-1.5 py-0">
|
||||
Actief
|
||||
</Badge>
|
||||
) : (
|
||||
!isArchived && (
|
||||
<ActivateProductButton productId={productId} isDemo={isDemo} />
|
||||
)
|
||||
)}
|
||||
|
||||
<Button
|
||||
size="icon-sm"
|
||||
variant="ghost"
|
||||
aria-label="Docs"
|
||||
title="Docs"
|
||||
render={<Link href={`/products/${productId}/docs`} />}
|
||||
data-debug-id="product-row-actions__docs"
|
||||
>
|
||||
<BookOpen className="size-3.5" />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
size="icon-sm"
|
||||
variant="ghost"
|
||||
aria-label="Open backlog"
|
||||
title="Open backlog"
|
||||
render={<Link href={`/products/${productId}`} />}
|
||||
data-debug-id="product-row-actions__open"
|
||||
>
|
||||
<ArrowRight className="size-3.5" />
|
||||
</Button>
|
||||
|
||||
<DropdownMenu>
|
||||
<DemoTooltip show={isDemo}>
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<Button
|
||||
size="icon-sm"
|
||||
variant="ghost"
|
||||
aria-label="Meer acties"
|
||||
disabled={isDemo || submitting}
|
||||
data-debug-id="product-row-actions__more"
|
||||
>
|
||||
<MoreHorizontal className="size-3.5" />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</DemoTooltip>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem
|
||||
onClick={handleArchiveToggle}
|
||||
data-debug-id="product-row-actions__archive-toggle"
|
||||
>
|
||||
{isArchived ? 'Herstel' : 'Archiveer'}
|
||||
</DropdownMenuItem>
|
||||
{/* TODO T-1089 (C2): Verwijderen-item met DeleteProductConfirm */}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ import {
|
|||
ProductDialog,
|
||||
type ProductDialogProduct,
|
||||
} from '@/components/dialogs/product-dialog'
|
||||
import { ProductRowActions } from '@/components/dashboard/product-row-actions'
|
||||
import { useUserSettingsStore } from '@/stores/user-settings/store'
|
||||
import { debugProps } from '@/lib/debug'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
|
@ -240,10 +241,12 @@ export function ProductsTable({
|
|||
onClick={(e) => e.stopPropagation()}
|
||||
data-debug-id="products-table__actions-cell"
|
||||
>
|
||||
{/* TODO T-1088 (C1): <ProductRowActions productId={product.id} isActive={product.id === activeProductId} isArchived={product.archived} isDemo={isDemo} /> */}
|
||||
<span className="text-xs text-muted-foreground italic">
|
||||
acties (T-1088)
|
||||
</span>
|
||||
<ProductRowActions
|
||||
productId={product.id}
|
||||
isActive={product.id === activeProductId}
|
||||
isArchived={product.archived}
|
||||
isDemo={isDemo}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue