Skip to content

Commit 9d7fd49

Browse files
committed
fix: use ls for dev variant handling
1 parent 6d1d701 commit 9d7fd49

File tree

3 files changed

+74
-54
lines changed

3 files changed

+74
-54
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"use client"
2+
3+
import { IS_PREVIEW_DEPLOY, IS_PROD } from "@/lib/utils/env"
4+
5+
import { useIsClient } from "@/hooks/useIsClient"
6+
import { useLocalStorage } from "@/hooks/useLocalStorage"
7+
import { ABTestVariants } from "@/lib/ab-testing/types"
8+
9+
type ClientABTestWrapperProps = {
10+
testKey: string
11+
variants: ABTestVariants
12+
serverVariantIndex: number
13+
}
14+
15+
export function ClientABTestWrapper({
16+
testKey,
17+
variants,
18+
serverVariantIndex,
19+
}: ClientABTestWrapperProps) {
20+
const [localOverride] = useLocalStorage<number | null>(
21+
`ab-test-${testKey}`,
22+
null
23+
)
24+
const isClient = useIsClient()
25+
26+
// Dev mode or preview deploy: use localStorage override or default to original (index 0)
27+
if (!IS_PROD || IS_PREVIEW_DEPLOY) {
28+
if (isClient && localOverride !== null && localOverride < variants.length) {
29+
return <>{variants[localOverride]}</>
30+
}
31+
return <>{variants[0]}</> // Always default to original in dev/preview
32+
}
33+
34+
// Production: use server-determined variant
35+
return <>{variants[serverVariantIndex] || variants[0]}</>
36+
}

src/components/AB/TestDebugPanel.tsx

Lines changed: 31 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
"use client"
22

3-
import { useEffect, useState, useTransition } from "react"
4-
import { useRouter } from "next/navigation"
3+
import { useRef, useState } from "react"
54

65
import { cn } from "@/lib/utils/cn"
76

87
import { Button } from "../ui/buttons/Button"
98

10-
import { forceABTestVariant } from "@/lib/ab-testing/actions"
9+
import { useLocalStorage } from "@/hooks/useLocalStorage"
10+
import { useOnClickOutside } from "@/hooks/useOnClickOutside"
1111
import { ABTestAssignment } from "@/lib/ab-testing/types"
1212

1313
type ABTestDebugPanelProps = {
@@ -18,34 +18,26 @@ type ABTestDebugPanelProps = {
1818

1919
export function ABTestDebugPanel({
2020
testKey,
21-
currentAssignment,
2221
availableVariants,
2322
}: ABTestDebugPanelProps) {
2423
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)
3329

34-
const forceVariant = async (variantName: string) => {
35-
try {
36-
const newAssignment = await forceABTestVariant(testKey, variantName)
37-
setLocalAssignment(newAssignment)
30+
useOnClickOutside(panelRef, () => setIsOpen(false))
3831

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)
4534
}
4635

4736
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+
>
4941
<Button
5042
onClick={() => setIsOpen(!isOpen)}
5143
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({
5951
<strong>Test:</strong> {testKey}
6052
</div>
6153
<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>
9055
</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+
))}
9171
</div>
9272
)}
9373
</div>

src/components/AB/TestWrapper.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ReactNode } from "react"
22

33
import { IS_PREVIEW_DEPLOY, IS_PROD } from "@/lib/utils/env"
44

5+
import { ClientABTestWrapper } from "./ClientABTestWrapper"
56
import { ABTestDebugPanel } from "./TestDebugPanel"
67
import { ABTestTracker } from "./TestTracker"
78

@@ -31,7 +32,6 @@ const ABTestWrapper = async ({
3132

3233
// Find the variant index based on the assignment
3334
const variantIndex = getVariantIndex(assignment.variant, testKey)
34-
const selectedVariant = variants[variantIndex] || variants[0]
3535

3636
// Get available variants for debug panel
3737
const configs = await getABTestConfigs()
@@ -54,8 +54,12 @@ const ABTestWrapper = async ({
5454
/>
5555
)}
5656

57-
{/* Render selected variant */}
58-
{selectedVariant}
57+
{/* Render variant with client-side override support */}
58+
<ClientABTestWrapper
59+
testKey={testKey}
60+
variants={variants}
61+
serverVariantIndex={variantIndex}
62+
/>
5963
</>
6064
)
6165
} catch (error) {

0 commit comments

Comments
 (0)