Add PostHog consent integration and extract integration plugin system

Add PostHog opt-in/opt-out consent support mirroring the existing Google
Consent Mode integration: database column, GraphQL field, console UI
toggle, and client-side posthog-js calls.

Extract both GCM and PostHog logic from CookieBannerClient into a
ConsentIntegration plugin interface so future integrations can be added
without modifying the client core.

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-04-24 15:51:40 +04:00
parent c6f548b8ff
commit 7f1dffad80
14 changed files with 281 additions and 54 deletions

View File

@@ -60,6 +60,7 @@ export const categorySectionFragment = graphql`
description
kind
gcmConsentTypes
posthogConsent
cookies(first: 100, orderBy: { field: CREATED_AT, direction: ASC })
@connection(key: "CategorySection_cookies", filters: [])
@required(action: THROW) {
@@ -99,6 +100,7 @@ const updateCategoryMutation = graphql`
description
rank
gcmConsentTypes
posthogConsent
updatedAt
}
cookieBanner {
@@ -239,7 +241,10 @@ export function CategorySection({ categoryKey, onDelete }: CategorySectionProps)
const cookies = category.cookies.edges.map(e => e.node);
const isMutating = isUpdating || isCreating || isUpdatingCookie;
const handleSaveCategory = (name: string, slug: string, description: string, gcmConsentTypes: string[]) => {
const handleSaveCategory = (
name: string, slug: string, description: string,
gcmConsentTypes: string[], posthogConsent: boolean,
) => {
updateCategory({
variables: {
input: {
@@ -248,6 +253,7 @@ export function CategorySection({ categoryKey, onDelete }: CategorySectionProps)
slug,
description,
gcmConsentTypes,
posthogConsent,
},
},
onCompleted(_response, errors) {
@@ -485,6 +491,7 @@ export function CategorySection({ categoryKey, onDelete }: CategorySectionProps)
slug={category.slug}
description={category.description}
gcmConsentTypes={[...category.gcmConsentTypes]}
posthogConsent={category.posthogConsent}
isUpdating={isUpdating}
onSave={handleSaveCategory}
onCancel={() => setIsEditingCategory(false)}
@@ -541,6 +548,16 @@ export function CategorySection({ categoryKey, onDelete }: CategorySectionProps)
))}
</div>
)}
{category.posthogConsent && (
<div className="mt-2 flex items-center gap-1.5">
<span className="text-xs text-txt-secondary/70">
{__("PostHog:")}
</span>
<Badge variant="neutral">
{__("Tracking consent")}
</Badge>
</div>
)}
</>
)}
</div>

View File

@@ -31,8 +31,9 @@ interface EditCategoryFormProps {
slug: string;
description: string;
gcmConsentTypes: string[];
posthogConsent: boolean;
isUpdating: boolean;
onSave: (name: string, slug: string, description: string, gcmConsentTypes: string[]) => void;
onSave: (name: string, slug: string, description: string, gcmConsentTypes: string[], posthogConsent: boolean) => void;
onCancel: () => void;
}
@@ -41,6 +42,7 @@ export function EditCategoryForm({
slug,
description,
gcmConsentTypes,
posthogConsent,
isUpdating,
onSave,
onCancel,
@@ -50,6 +52,7 @@ export function EditCategoryForm({
const [editSlug, setEditSlug] = useState(slug);
const [editDescription, setEditDescription] = useState(description);
const [editGcmTypes, setEditGcmTypes] = useState<string[]>(gcmConsentTypes);
const [editPosthogConsent, setEditPosthogConsent] = useState(posthogConsent);
const toggleGcmType = (type: string) => {
setEditGcmTypes(prev =>
@@ -102,11 +105,28 @@ export function EditCategoryForm({
))}
</div>
</div>
<div>
<label className="text-sm font-medium">
{__("PostHog")}
</label>
<p className="text-xs text-muted-foreground mb-2">
{__("Control PostHog tracking consent based on this category.")}
</p>
<label className="flex items-center gap-1.5 text-xs cursor-pointer">
<input
type="checkbox"
checked={editPosthogConsent}
onChange={() => setEditPosthogConsent(prev => !prev)}
className="rounded"
/>
<span>{__("Opt in/out of PostHog tracking")}</span>
</label>
</div>
<div className="flex items-center gap-2">
<Button
onClick={() => {
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(editSlug)) return;
onSave(editName, editSlug, editDescription, editGcmTypes);
onSave(editName, editSlug, editDescription, editGcmTypes, editPosthogConsent);
}}
disabled={isUpdating}
>