import { useFetcher } from "@remix-run/react"; import { useEffect, useState } from "react"; import stableStringify from "json-stable-stringify"; import { Dialog, DialogContent, DialogHeader, DialogDescription, DialogFooter, } from "~/components/primitives/Dialog"; import { Button } from "~/components/primitives/Buttons"; import { Callout } from "~/components/primitives/Callout"; import { LockClosedIcon } from "@heroicons/react/20/solid"; import { CheckboxWithLabel } from "~/components/primitives/Checkbox"; import { cn } from "~/utils/cn"; import { FEATURE_FLAG, ORG_LOCKED_FLAGS, type FlagControlType } from "~/v3/featureFlags"; import { UNSET_VALUE, BooleanControl, EnumControl, NumberControl, StringControl, WorkerGroupControl, type WorkerGroup, } from "./FlagControls"; type LoaderData = { org: { id: string; title: string; slug: string }; orgFlags: Record; globalFlags: Record; controlTypes: Record; workerGroupName?: string; workerGroups?: WorkerGroup[]; isManagedCloud?: boolean; }; type ActionData = { success?: boolean; error?: string; }; type FeatureFlagsDialogProps = { orgId: string | null; orgTitle: string; open: boolean; onOpenChange: (open: boolean) => void; }; export function FeatureFlagsDialog({ orgId, orgTitle, open, onOpenChange, }: FeatureFlagsDialogProps) { const loadFetcher = useFetcher(); const saveFetcher = useFetcher(); const [overrides, setOverrides] = useState>({}); const [initialOverrides, setInitialOverrides] = useState>({}); const [saveError, setSaveError] = useState(null); const [unlocked, setUnlocked] = useState(false); const isLocked = (key: string) => !unlocked && ORG_LOCKED_FLAGS.includes(key); useEffect(() => { if (open && orgId) { setSaveError(null); setOverrides({}); setInitialOverrides({}); loadFetcher.load(`/admin/api/v2/orgs/${orgId}/feature-flags`); } }, [open, orgId]); useEffect(() => { if (loadFetcher.data) { const loaded = loadFetcher.data.orgFlags ?? {}; setOverrides({ ...loaded }); setInitialOverrides({ ...loaded }); } }, [loadFetcher.data]); useEffect(() => { if (saveFetcher.data?.success) { onOpenChange(false); } else if (saveFetcher.data?.error) { setSaveError(saveFetcher.data.error); } }, [saveFetcher.data]); const isDirty = stableStringify(overrides) !== stableStringify(initialOverrides); const setFlagValue = (key: string, value: unknown) => { setOverrides((prev) => ({ ...prev, [key]: value })); }; const unsetFlag = (key: string) => { setOverrides((prev) => { const next = { ...prev }; delete next[key]; return next; }); }; const handleSave = () => { if (!orgId) return; const body = Object.keys(overrides).length === 0 ? null : overrides; saveFetcher.submit(JSON.stringify(body), { method: "POST", action: `/admin/api/v2/orgs/${orgId}/feature-flags`, encType: "application/json", }); }; const data = loadFetcher.data; const isLoading = loadFetcher.state === "loading"; const isSaving = saveFetcher.state === "submitting"; const jsonPreview = Object.keys(overrides).length === 0 ? "null" : JSON.stringify(overrides, null, 2); const sortedFlagKeys = data ? Object.keys(data.controlTypes).sort() : []; return ( Feature flags - {orgTitle} Org-level overrides. Unset flags inherit from global defaults. {data && (
)}
{isLoading ? (
Loading flags...
) : data ? (
{sortedFlagKeys.map((key) => { const control = data.controlTypes[key]; const locked = isLocked(key); const globalValue = data.globalFlags[key as keyof typeof data.globalFlags]; const isWorkerGroup = key === FEATURE_FLAG.defaultWorkerInstanceGroupId; const globalDisplay = isWorkerGroup && data.workerGroupName && globalValue !== undefined ? `${data.workerGroupName} (${String(globalValue).slice(0, 8)}...)` : globalValue !== undefined ? String(globalValue) : "unset"; if (locked) { return (
{key}
global: {globalDisplay}
); } const isOverridden = key in overrides; return (
{key}
global: {globalDisplay}
{isWorkerGroup && data.workerGroups ? ( { if (val === UNSET_VALUE) { unsetFlag(key); } else { setFlagValue(key, val); } }} dimmed={!isOverridden} /> ) : control.type === "boolean" ? ( setFlagValue(key, val)} dimmed={!isOverridden} /> ) : control.type === "enum" ? ( { if (val === UNSET_VALUE) { unsetFlag(key); } else { setFlagValue(key, val); } }} dimmed={!isOverridden} /> ) : control.type === "number" ? ( { if (val === undefined) { unsetFlag(key); } else { setFlagValue(key, val); } }} dimmed={!isOverridden} /> ) : control.type === "string" ? ( { if (val === "") { unsetFlag(key); } else { setFlagValue(key, val); } }} dimmed={!isOverridden} /> ) : null}
); })}
) : null}
{data && (
Preview JSON
              {jsonPreview}
            
)} {saveError && {saveError}}
); }