diff --git a/__tests__/components/products/auto-pr-toggle.test.tsx b/__tests__/components/products/auto-pr-toggle.test.tsx new file mode 100644 index 0000000..b2f2d0a --- /dev/null +++ b/__tests__/components/products/auto-pr-toggle.test.tsx @@ -0,0 +1,46 @@ +// @vitest-environment jsdom +import { describe, it, expect, vi, beforeEach } from 'vitest' +import { render, screen, fireEvent } from '@testing-library/react' +import '@testing-library/jest-dom' + +vi.mock('@/actions/products', () => ({ + updateAutoPrAction: vi.fn(), +})) + +vi.mock('sonner', () => ({ toast: { error: vi.fn() } })) + +import { updateAutoPrAction } from '@/actions/products' +import { AutoPrToggle } from '@/components/products/auto-pr-toggle' + +const mockAction = updateAutoPrAction as ReturnType + +beforeEach(() => { + vi.clearAllMocks() + mockAction.mockResolvedValue({ success: true }) +}) + +describe('AutoPrToggle', () => { + it('renders in off state with aria-checked=false', () => { + render() + const toggle = screen.getByRole('switch') + expect(toggle).toHaveAttribute('aria-checked', 'false') + }) + + it('renders in on state with aria-checked=true', () => { + render() + const toggle = screen.getByRole('switch') + expect(toggle).toHaveAttribute('aria-checked', 'true') + }) + + it('calls updateAutoPrAction with true when toggled on', async () => { + render() + fireEvent.click(screen.getByRole('switch')) + expect(mockAction).toHaveBeenCalledWith('prod-1', true) + }) + + it('calls updateAutoPrAction with false when toggled off', async () => { + render() + fireEvent.click(screen.getByRole('switch')) + expect(mockAction).toHaveBeenCalledWith('prod-1', false) + }) +}) diff --git a/actions/products.ts b/actions/products.ts index 08f7385..892569f 100644 --- a/actions/products.ts +++ b/actions/products.ts @@ -250,3 +250,19 @@ export async function leaveProductAction(productId: string) { revalidatePath('/settings') return { success: true } } + +export async function updateAutoPrAction(id: string, auto_pr: boolean) { + const session = await getSession() + if (!session.userId) return { error: 'Niet ingelogd' } + if (session.isDemo) return { error: 'Niet beschikbaar in demo-modus' } + + const parsed = z.object({ auto_pr: z.boolean() }).safeParse({ auto_pr }) + if (!parsed.success) return { error: 'Ongeldige waarde voor auto_pr' } + + const product = await prisma.product.findFirst({ where: { id, user_id: session.userId } }) + if (!product) return { error: 'Product niet gevonden' } + + await prisma.product.update({ where: { id }, data: { auto_pr: parsed.data.auto_pr } }) + revalidatePath(`/products/${id}/settings`) + return { success: true } +} diff --git a/app/(app)/products/[id]/settings/page.tsx b/app/(app)/products/[id]/settings/page.tsx index 1ab3593..11748b0 100644 --- a/app/(app)/products/[id]/settings/page.tsx +++ b/app/(app)/products/[id]/settings/page.tsx @@ -7,6 +7,7 @@ import { ProductForm } from '@/components/products/product-form' import { ArchiveProductButton } from '@/components/products/archive-product-button' import { TeamManager } from '@/components/products/team-manager' import { updateProductAction } from '@/actions/products' +import { AutoPrToggle } from '@/components/products/auto-pr-toggle' import Link from 'next/link' interface Props { @@ -55,6 +56,16 @@ export default async function ProductSettingsPage({ params }: Props) { }} /> +
+
+

Agent-instellingen

+

+ Automatiseer acties na een succesvolle agent-job. +

+
+ +
+

Team

diff --git a/components/products/auto-pr-toggle.tsx b/components/products/auto-pr-toggle.tsx new file mode 100644 index 0000000..0158627 --- /dev/null +++ b/components/products/auto-pr-toggle.tsx @@ -0,0 +1,55 @@ +'use client' + +import { useState, useTransition } from 'react' +import { cn } from '@/lib/utils' +import { updateAutoPrAction } from '@/actions/products' +import { toast } from 'sonner' + +interface AutoPrToggleProps { + productId: string + initialValue: boolean +} + +export function AutoPrToggle({ productId, initialValue }: AutoPrToggleProps) { + const [enabled, setEnabled] = useState(initialValue) + const [isPending, startTransition] = useTransition() + + function handleToggle() { + const newValue = !enabled + setEnabled(newValue) + startTransition(async () => { + const result = await updateAutoPrAction(productId, newValue) + if (result.error) { + setEnabled(!newValue) + toast.error(typeof result.error === 'string' ? result.error : 'Opslaan mislukt') + } + }) + } + + return ( +
+ + Automatisch PR aanmaken na succesvolle agent-job +
+ ) +}