1
1
"use client"
2
2
3
- import { useEffect , useState , useTransition } from "react"
4
- import { useRouter } from "next/navigation"
3
+ import { useRef , useState } from "react"
5
4
6
5
import { cn } from "@/lib/utils/cn"
7
6
8
7
import { Button } from "../ui/buttons/Button"
9
8
10
- import { forceABTestVariant } from "@/lib/ab-testing/actions"
9
+ import { useLocalStorage } from "@/hooks/useLocalStorage"
10
+ import { useOnClickOutside } from "@/hooks/useOnClickOutside"
11
11
import { ABTestAssignment } from "@/lib/ab-testing/types"
12
12
13
13
type ABTestDebugPanelProps = {
@@ -18,34 +18,26 @@ type ABTestDebugPanelProps = {
18
18
19
19
export function ABTestDebugPanel ( {
20
20
testKey,
21
- currentAssignment,
22
21
availableVariants,
23
22
} : ABTestDebugPanelProps ) {
24
23
const [ isOpen , setIsOpen ] = useState ( false )
25
- const [ isPending , startTransition ] = useTransition ( )
26
- const [ localAssignment , setLocalAssignment ] = useState ( currentAssignment )
27
- const router = useRouter ( )
28
-
29
- // Sync local state when server state changes
30
- useEffect ( ( ) => {
31
- setLocalAssignment ( currentAssignment )
32
- } , [ currentAssignment ] )
24
+ const [ selectedVariant , setSelectedVariant ] = useLocalStorage < number | null > (
25
+ `ab-test-${ testKey } ` ,
26
+ null
27
+ )
28
+ const panelRef = useRef < HTMLDivElement > ( null )
33
29
34
- const forceVariant = async ( variantName : string ) => {
35
- try {
36
- const newAssignment = await forceABTestVariant ( testKey , variantName )
37
- setLocalAssignment ( newAssignment )
30
+ useOnClickOutside ( panelRef , ( ) => setIsOpen ( false ) )
38
31
39
- startTransition ( ( ) => {
40
- router . refresh ( )
41
- } )
42
- } catch ( error ) {
43
- console . error ( "Failed to force variant:" , error )
44
- }
32
+ const forceVariant = ( variantIndex : number ) => {
33
+ setSelectedVariant ( variantIndex )
45
34
}
46
35
47
36
return (
48
- < div className = "fixed bottom-5 right-5 z-modal rounded-lg border-2 bg-background-low p-2.5 font-mono text-xs" >
37
+ < div
38
+ ref = { panelRef }
39
+ className = "fixed bottom-5 right-5 z-modal rounded-lg border-2 bg-background-low p-2.5 font-mono text-xs"
40
+ >
49
41
< Button
50
42
onClick = { ( ) => setIsOpen ( ! isOpen ) }
51
43
className = "w-full cursor-pointer rounded border-none bg-accent-a px-2.5 py-1 font-semibold text-white hover:bg-accent-a-hover"
@@ -59,35 +51,23 @@ export function ABTestDebugPanel({
59
51
< strong > Test:</ strong > { testKey }
60
52
</ div >
61
53
< div >
62
- < strong > Current:</ strong > { localAssignment ?. variant || "None" }
63
- { isPending && (
64
- < span className = "ml-2 text-xs text-gray-500" > Loading...</ span >
65
- ) }
66
- </ div >
67
- < div className = "mt-2.5" >
68
- < div >
69
- < strong > Force variant:</ strong >
70
- </ div >
71
- { availableVariants . map ( ( variant ) => (
72
- < Button
73
- key = { variant }
74
- variant = {
75
- localAssignment ?. variant === variant ? "solid" : "outline"
76
- }
77
- isSecondary
78
- onClick = { ( ) => forceVariant ( variant ) }
79
- disabled = { isPending }
80
- className = { cn (
81
- "my-0.5 block w-full rounded border border-gray-300 px-2 py-1 transition-opacity" ,
82
- localAssignment ?. variant === variant &&
83
- "bg-success text-white hover:bg-success-dark" ,
84
- isPending ? "cursor-not-allowed opacity-60" : "cursor-pointer"
85
- ) }
86
- >
87
- { variant }
88
- </ Button >
89
- ) ) }
54
+ < strong > Select variant:</ strong >
90
55
</ div >
56
+ { availableVariants . map ( ( variant , index ) => (
57
+ < Button
58
+ key = { variant }
59
+ variant = { selectedVariant === index ? "solid" : "outline" }
60
+ isSecondary
61
+ onClick = { ( ) => forceVariant ( index ) }
62
+ className = { cn (
63
+ "my-0.5 block w-full rounded border border-gray-300 px-2 py-1" ,
64
+ selectedVariant === index &&
65
+ "bg-success text-white hover:bg-success-dark"
66
+ ) }
67
+ >
68
+ { variant }
69
+ </ Button >
70
+ ) ) }
91
71
</ div >
92
72
) }
93
73
</ div >
0 commit comments