@datarecce/ui 0.1.1 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../recce-source/js/src/lib/api/cacheKeys.ts","../recce-source/js/src/lib/const.ts","../recce-source/js/src/lib/api/axiosClient.ts","../recce-source/js/src/lib/api/instanceInfo.ts","../recce-source/js/src/lib/hooks/useRecceInstanceInfo.tsx","../recce-source/js/src/components/ui/toaster.tsx","../recce-source/js/src/lib/hooks/useCheckToast.tsx","../recce-source/js/src/lib/hooks/useClipBoardToast.tsx","../recce-source/js/src/components/lineage/LineageViewContext.tsx","../recce-source/js/src/lib/api/track.ts","../recce-source/js/src/components/lineage/useValueDiffAlertDialog.tsx"],"names":["trk","nodeCount","jsx","jsxs","Portal"],"mappings":"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","file":"hooks.mjs","sourcesContent":["export const cacheKeys = {\n rowCount: (model: string) => [\"row_count\", model],\n lineage: () => [\"lineage\"],\n checks: () => [\"checks\", \"list\"],\n check: (checkId: string) => [\"checks\", checkId],\n checkEvents: (checkId: string) => [\"checks\", checkId, \"events\"],\n runs: () => [\"runs\"],\n run: (runId: string) => [\"runs\", runId],\n runsAggregated: () => [\"runs_aggregated\"],\n flag: () => [\"flag\"],\n instanceInfo: () => [\"instance_info\"],\n user: () => [\"user\"],\n};\n","let apiUrl = process.env.NEXT_PUBLIC_API_URL;\napiUrl ??= typeof window !== \"undefined\" ? window.location.origin : \"\";\n\nexport const PUBLIC_API_URL = apiUrl;\n\nlet cloudWebUrl = process.env.NEXT_PUBLIC_CLOUD_WEB_URL;\ncloudWebUrl ??= \"https://cloud.datarecce.io\";\n\nexport const PUBLIC_CLOUD_WEB_URL = cloudWebUrl;\n","import { QueryClient } from \"@tanstack/react-query\";\nimport axios from \"axios\";\nimport { PUBLIC_API_URL } from \"@/lib/const\";\n\nexport const axiosClient = axios.create({\n baseURL: PUBLIC_API_URL,\n});\n\nexport const reactQueryClient = new QueryClient();\n","import { AxiosResponse } from \"axios\";\nimport { axiosClient } from \"./axiosClient\";\n\nexport interface RecceInstanceInfo {\n server_mode: \"server\" | \"preview\" | \"read-only\";\n single_env: boolean;\n authed: boolean;\n cloud_instance: boolean;\n lifetime_expired_at?: Date;\n idle_timeout?: number;\n share_url?: string;\n session_id?: string;\n organization_name?: string;\n web_url?: string;\n}\n\nexport async function getRecceInstanceInfo(): Promise<RecceInstanceInfo> {\n return (\n await axiosClient.get<never, AxiosResponse<RecceInstanceInfo>>(\n \"/api/instance-info\",\n )\n ).data;\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { cacheKeys } from \"../api/cacheKeys\";\nimport { getRecceInstanceInfo, RecceInstanceInfo } from \"../api/instanceInfo\";\n\nexport const useRecceInstanceInfo = () => {\n return useQuery<RecceInstanceInfo>({\n queryKey: cacheKeys.instanceInfo(),\n queryFn: getRecceInstanceInfo,\n });\n};\n","\"use client\";\n\nimport type { CreateToasterReturn } from \"@chakra-ui/react\";\nimport {\n Toaster as ChakraToaster,\n createToaster,\n Portal,\n Spinner,\n Stack,\n Toast,\n} from \"@chakra-ui/react\";\n\nexport const toaster: CreateToasterReturn = createToaster({\n placement: \"bottom-end\",\n pauseOnPageIdle: true,\n});\n\nexport const Toaster = () => {\n return (\n <Portal>\n <ChakraToaster toaster={toaster} insetInline={{ mdDown: \"4\" }}>\n {(toast) => (\n <Toast.Root width={{ md: \"sm\" }}>\n {toast.type === \"loading\" ? (\n <Spinner size=\"sm\" color=\"blue.solid\" />\n ) : (\n <Toast.Indicator />\n )}\n <Stack gap=\"1\" flex=\"1\" maxWidth=\"100%\">\n {toast.title && <Toast.Title>{toast.title}</Toast.Title>}\n {toast.description && (\n <Toast.Description>{toast.description}</Toast.Description>\n )}\n </Stack>\n {toast.action && (\n <Toast.ActionTrigger>{toast.action.label}</Toast.ActionTrigger>\n )}\n {toast.closable && <Toast.CloseTrigger />}\n </Toast.Root>\n )}\n </ChakraToaster>\n </Portal>\n );\n};\n","import { toaster } from \"@/components/ui/toaster\";\n\nexport function useCheckToast() {\n function markedAsApprovedToast() {\n toaster.create({\n title: \"Marked as approved\",\n type: \"success\",\n duration: 2000,\n });\n }\n return {\n markedAsApprovedToast,\n };\n}\n","import { toaster } from \"@/components/ui/toaster\";\n\nexport function useClipBoardToast() {\n function successToast(message: string) {\n toaster.create({\n description: message,\n type: \"info\",\n duration: 5000,\n closable: true,\n });\n }\n\n function failToast(title: string, error: unknown) {\n toaster.create({\n title: title,\n description: String(error),\n type: \"error\",\n duration: 5000,\n closable: true,\n });\n }\n\n return {\n successToast,\n failToast,\n };\n}\n","import React, { createContext, useContext } from \"react\";\nimport { CllInput, ColumnLineageData } from \"@/lib/api/cll\";\nimport { LineageDiffViewOptions } from \"@/lib/api/lineagecheck\";\nimport { Run } from \"@/lib/api/types\";\nimport { LineageGraphNode, LineageGraphNodes } from \"./lineage\";\n\ntype NewType = LineageDiffViewOptions;\ntype ActionMode = \"per_node\" | \"multi_nodes\";\n\ninterface NodeAction {\n mode: ActionMode;\n status?: \"pending\" | \"running\" | \"success\" | \"failure\" | \"skipped\";\n skipReason?: string;\n run?: Run;\n}\n\nexport interface ActionState {\n mode: ActionMode;\n status: \"pending\" | \"running\" | \"canceling\" | \"canceled\" | \"completed\";\n currentRun?: Partial<Run>;\n completed: number;\n total: number;\n actions: Record<string, NodeAction>;\n}\n\nexport interface LineageViewContextType {\n interactive: boolean;\n nodes: LineageGraphNodes[];\n focusedNode?: LineageGraphNode;\n selectedNodes: LineageGraphNode[];\n cll: ColumnLineageData | undefined;\n\n // context menu\n showContextMenu: (event: React.MouseEvent, node: LineageGraphNodes) => void;\n\n // filter\n viewOptions: LineageDiffViewOptions;\n onViewOptionsChanged: (options: NewType) => void;\n\n // Multi nodes selection\n selectMode: \"selecting\" | \"action_result\" | undefined;\n selectNode: (nodeId: string) => void;\n selectParentNodes: (nodeId: string, degree?: number) => void;\n selectChildNodes: (nodeId: string, degree?: number) => void;\n deselect: () => void;\n\n // node state\n isNodeHighlighted: (nodeId: string) => boolean;\n isNodeSelected: (nodeId: string) => boolean;\n isEdgeHighlighted: (source: string, target: string) => boolean;\n getNodeAction: (nodeId: string) => NodeAction;\n getNodeColumnSet: (nodeId: string) => Set<string>;\n isNodeShowingChangeAnalysis: (nodeId: string) => boolean;\n\n //actions\n runRowCount: () => Promise<void>;\n runRowCountDiff: () => Promise<void>;\n runValueDiff: () => Promise<void>;\n addLineageDiffCheck: (viewMode?: string) => void;\n addSchemaDiffCheck: () => void;\n cancel: () => void;\n actionState: ActionState;\n\n // Column Level Lineage\n centerNode: (nodeId: string) => void;\n showColumnLevelLineage: (cll?: CllInput) => Promise<void>;\n resetColumnLevelLineage: (previous?: boolean) => Promise<void>;\n}\n\nexport const LineageViewContext = createContext<\n LineageViewContextType | undefined\n>(undefined);\n\nexport const useLineageViewContextSafe = (): LineageViewContextType => {\n const context = useContext(LineageViewContext);\n if (!context) {\n throw new Error(\n \"useLineageViewContext must be used within a LineageViewProvider\",\n );\n }\n return context;\n};\n\nexport const useLineageViewContext = (): LineageViewContextType | undefined => {\n return useContext(LineageViewContext);\n};\n","import {\n AmplitudeReturn,\n BaseEvent,\n EventOptions,\n Result,\n} from \"@amplitude/analytics-core\";\nimport { initAll, track as trk } from \"@amplitude/unified\";\n\nfunction track(\n eventInput: string | BaseEvent,\n // biome-ignore lint/suspicious/noExplicitAny: Amplitude library uses any for event properties\n eventProperties?: Record<string, any> | undefined,\n eventOptions?: EventOptions | undefined,\n): AmplitudeReturn<Result> {\n // If Amplitude isn't initialized, log to console instead\n if (!amplitudeInitialized) {\n console.log(\"[Tracking]\", eventInput, eventProperties, eventOptions);\n }\n return trk(eventInput, eventProperties, eventOptions);\n}\n\nlet amplitudeInitialized = false;\n\nexport function trackInit() {\n function getCookie(key: string) {\n const b = document.cookie.match(\"(^|;)\\\\s*\" + key + \"\\\\s*=\\\\s*([^;]+)\");\n return b ? b.pop() : \"\";\n }\n\n const userId =\n process.env.NODE_ENV === \"development\"\n ? \"web_dev\"\n : getCookie(\"recce_user_id\");\n const apiKey = process.env.AMPLITUDE_API_KEY;\n if (userId && apiKey) {\n try {\n void initAll(apiKey, {\n analytics: {\n userId,\n autocapture: true,\n },\n sessionReplay: {\n sampleRate: 1,\n },\n });\n amplitudeInitialized = true;\n } catch (e) {\n console.error(e);\n }\n }\n\n // Log when Amplitude is not initialized (for development/debugging)\n if (!amplitudeInitialized) {\n console.log(\n \"[Tracking] Amplitude not initialized (missing API key or user ID). Events will be logged to console instead.\",\n );\n }\n}\n\ninterface MultiNodeActionProps {\n type:\n | \"row_count\"\n | \"row_count_diff\"\n | \"value_diff\"\n | \"schema_diff\"\n | \"lineage_diff\";\n selected: \"single\" | \"multi\" | \"none\";\n}\n\nexport function trackMultiNodesAction(props: MultiNodeActionProps) {\n track(\"[Web] multi_nodes_action\", props);\n}\n\ninterface HistoryActionProps {\n name: \"show\" | \"hide\" | \"click_run\" | \"add_to_checklist\" | \"go_to_check\";\n}\n\nexport function trackHistoryAction(props: HistoryActionProps) {\n track(\"[Web] history_action\", props);\n}\n\ninterface PreviewChangeProps {\n action: \"explore\" | \"run\" | \"close\";\n node?: string;\n status?: \"success\" | \"failure\";\n}\n\nexport function trackPreviewChange(props: PreviewChangeProps) {\n track(\"[Experiment] preview_change\", props);\n}\n\ninterface PreviewChangeFeedbackProps {\n feedback: \"like\" | \"dislike\" | \"form\";\n node?: string;\n}\n\nexport function trackPreviewChangeFeedback(props: PreviewChangeFeedbackProps) {\n track(\"[Experiment] preview_change\", props);\n}\n\ninterface SingleEnvironmentProps {\n action:\n | \"onboarding\"\n | \"external_link\"\n | \"preview_changes\"\n | `target_base_added`;\n from?: \"onboarding\" | \"preview_changes\";\n node?: string;\n}\n\nexport function trackSingleEnvironment(props: SingleEnvironmentProps) {\n track(\"[Experiment] single_environment\", props);\n}\n\nexport function getExperimentTrackingBreakingChangeEnabled() {\n return false;\n}\n\ninterface ColumnLevelLineageProps {\n action: \"view\";\n source: \"schema_column\" | \"changed_column\" | \"cll_column\";\n}\n\nexport function trackColumnLevelLineage(props: ColumnLevelLineageProps) {\n track(\"[Web] column_level_lineage\", props);\n}\n\ninterface ShareStateProps {\n name: \"enable\" | \"create\" | \"copy\";\n}\n\nexport function trackShareState(props: ShareStateProps) {\n track(\"[Web] share_state\", props);\n}\n\ninterface StateActionProps {\n name: \"import\" | \"export\";\n}\n\nexport function trackStateAction(props: StateActionProps) {\n track(\"[Web] state_action\", props);\n}\n\ninterface CopyToClipboardProps {\n from: \"run\" | \"check\" | \"lineage_view\";\n type: string;\n}\n\nexport function trackCopyToClipboard(props: CopyToClipboardProps) {\n track(\"[Click] copy_to_clipboard\", props);\n}\n\ninterface TrackNavProps {\n from: string;\n to: string;\n}\n\nexport function trackNavigation(props: TrackNavProps) {\n track(\"[Web] navigation_change\", props);\n}\n\nexport interface LineageViewRenderProps {\n node_count: number;\n view_mode: string;\n impact_radius_enabled: boolean;\n cll_column_active?: boolean;\n right_sidebar_open: boolean;\n [status: string]: number | string | boolean | undefined;\n}\n\nexport function trackLineageViewRender(props: LineageViewRenderProps) {\n track(\"[Web] lineage_view_render\", props);\n}\n\nexport interface EnvironmentConfigProps {\n review_mode: boolean;\n adapter_type: string | null;\n has_git_info: boolean;\n has_pr_info: boolean;\n // Adapter-specific (shape varies by adapter_type)\n base?: {\n schema_count?: number;\n dbt_version?: string | null;\n timestamp?: string | null;\n has_env?: boolean;\n };\n current?: {\n schema_count?: number;\n dbt_version?: string | null;\n timestamp?: string | null;\n has_env?: boolean;\n };\n schemas_match?: boolean;\n}\n\nexport function trackEnvironmentConfig(props: EnvironmentConfigProps) {\n track(\"[Web] environment_config\", props);\n}\n\n// Explore action types\nexport const EXPLORE_ACTION = {\n ROW_COUNT: \"row_count\",\n ROW_COUNT_DIFF: \"row_count_diff\",\n PROFILE: \"profile\",\n PROFILE_DIFF: \"profile_diff\",\n VALUE_DIFF: \"value_diff\",\n SCHEMA_DIFF: \"schema_diff\",\n LINEAGE_DIFF: \"lineage_diff\",\n QUERY: \"query\",\n HISTOGRAM_DIFF: \"histogram_diff\",\n TOP_K_DIFF: \"top_k_diff\",\n} as const;\n\n// Explore action sources\nexport const EXPLORE_SOURCE = {\n LINEAGE_VIEW_TOP_BAR: \"lineage_view_top_bar\",\n LINEAGE_VIEW_CONTEXT_MENU: \"lineage_view_context_menu\",\n NODE_KEBAB_MENU: \"node_kebab_menu\",\n NODE_SIDEBAR_SINGLE_ENV: \"node_sidebar_single_env\",\n NODE_SIDEBAR_MULTI_ENV: \"node_sidebar_multi_env\",\n SCHEMA_ROW_COUNT_BUTTON: \"schema_row_count_button\",\n SCHEMA_COLUMN_MENU: \"schema_column_menu\",\n} as const;\n\nexport type ExploreActionType =\n (typeof EXPLORE_ACTION)[keyof typeof EXPLORE_ACTION];\nexport type ExploreSourceType =\n (typeof EXPLORE_SOURCE)[keyof typeof EXPLORE_SOURCE];\n\ninterface ExploreActionProps {\n action: ExploreActionType;\n source: ExploreSourceType;\n node_count?: number;\n}\n\nexport function trackExploreAction(props: ExploreActionProps) {\n track(\"[Web] explore_action\", props);\n}\n\n// Explore action form events\nexport const EXPLORE_FORM_EVENT = {\n EXECUTE: \"execute\",\n CANCEL: \"cancel\",\n} as const;\n\nexport type ExploreFormEventType =\n (typeof EXPLORE_FORM_EVENT)[keyof typeof EXPLORE_FORM_EVENT];\n\ninterface ExploreActionFormProps {\n action: ExploreActionType;\n event: ExploreFormEventType;\n}\n\nexport function trackExploreActionForm(props: ExploreActionFormProps) {\n track(\"[Web] explore_action_form\", props);\n}\n\n// Helper to check if a run type is an explore action\nexport function isExploreAction(type: string): type is ExploreActionType {\n return Object.values(EXPLORE_ACTION).includes(type as ExploreActionType);\n}\n\n// Lineage selection action types\nexport const LINEAGE_SELECTION_ACTION = {\n SELECT_PARENT_NODES: \"select_parent_nodes\",\n SELECT_CHILD_NODES: \"select_child_nodes\",\n SELECT_ALL_UPSTREAM: \"select_all_upstream\",\n SELECT_ALL_DOWNSTREAM: \"select_all_downstream\",\n} as const;\n\nexport type LineageSelectionActionType =\n (typeof LINEAGE_SELECTION_ACTION)[keyof typeof LINEAGE_SELECTION_ACTION];\n\ninterface LineageSelectionProps {\n action: LineageSelectionActionType;\n node_count?: number;\n}\n\nexport function trackLineageSelection(props: LineageSelectionProps) {\n track(\"[Web] lineage_selection\", props);\n}\n","import {\n Box,\n Button,\n CloseButton,\n Dialog,\n Flex,\n Portal,\n useDisclosure,\n} from \"@chakra-ui/react\";\nimport React, { useCallback, useRef, useState } from \"react\";\nimport {\n EXPLORE_ACTION,\n EXPLORE_FORM_EVENT,\n trackExploreActionForm,\n} from \"@/lib/api/track\";\n\nfunction useValueDiffAlertDialog() {\n const { open, onOpen, onClose } = useDisclosure();\n const [nodeCount, setNodeCount] = useState(0);\n const [resolvePromise, setResolvePromise] =\n useState<(value: boolean) => void>();\n const cancelRef = useRef<HTMLButtonElement>(null);\n\n const confirm = useCallback(\n (nodeCount: number) => {\n setNodeCount(nodeCount);\n return new Promise<boolean>((resolve) => {\n setResolvePromise(() => resolve);\n onOpen();\n });\n },\n [onOpen],\n );\n\n const handleConfirm = () => {\n trackExploreActionForm({\n action: EXPLORE_ACTION.VALUE_DIFF,\n event: EXPLORE_FORM_EVENT.EXECUTE,\n });\n resolvePromise?.(true);\n onClose();\n };\n\n const handleCancel = () => {\n trackExploreActionForm({\n action: EXPLORE_ACTION.VALUE_DIFF,\n event: EXPLORE_FORM_EVENT.CANCEL,\n });\n resolvePromise?.(false);\n onClose();\n };\n\n const ValueDiffAlertDialog = (\n <Dialog.Root\n size={\"xl\"}\n open={open}\n role=\"alertdialog\"\n initialFocusEl={() => {\n return cancelRef.current;\n }}\n onOpenChange={handleCancel}\n >\n <Portal>\n <Dialog.Backdrop />\n <Dialog.Positioner>\n <Dialog.Content>\n <Dialog.Header fontSize=\"lg\" fontWeight=\"bold\">\n <Dialog.Title>Value Diff on {nodeCount} nodes</Dialog.Title>\n </Dialog.Header>\n\n <Dialog.Body gap=\"20px\" as={Flex} direction=\"column\">\n <Box>\n Value diff will be executed on {nodeCount} nodes in the Lineage,\n which can add extra costs to your bill.\n </Box>\n </Dialog.Body>\n\n <Dialog.Footer gap={1}>\n <Button\n ref={cancelRef}\n onClick={handleCancel}\n variant=\"outline\"\n colorPalette=\"gray\"\n >\n Cancel\n </Button>\n <Button colorPalette=\"iochmara\" onClick={handleConfirm} ml={3}>\n Execute\n </Button>\n </Dialog.Footer>\n <Dialog.CloseTrigger asChild>\n <CloseButton size=\"sm\" />\n </Dialog.CloseTrigger>\n </Dialog.Content>\n </Dialog.Positioner>\n </Portal>\n </Dialog.Root>\n );\n\n return { confirm, AlertDialog: ValueDiffAlertDialog };\n}\n\nexport default useValueDiffAlertDialog;\n"]}
1
+ {"version":3,"sources":["../recce-source/js/src/lib/api/cacheKeys.ts","../recce-source/js/src/lib/const.ts","../recce-source/js/src/lib/api/axiosClient.ts","../recce-source/js/src/lib/api/instanceInfo.ts","../recce-source/js/src/lib/hooks/useRecceInstanceInfo.tsx","../recce-source/js/src/components/ui/toaster.tsx","../recce-source/js/src/lib/hooks/useCheckToast.tsx","../recce-source/js/src/lib/hooks/useClipBoardToast.tsx","../recce-source/js/src/components/lineage/LineageViewContext.tsx","../recce-source/js/src/lib/api/track.ts","../recce-source/js/src/components/lineage/useValueDiffAlertDialog.tsx"],"names":["createContext","useContext","trk","useState","useCallback","nodeCount","jsxs","jsx","Stack"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAO,IAAM,SAAA,GAAY;AAAA,EACvB,QAAA,EAAU,CAAC,KAAA,KAAkB,CAAC,aAAa,KAAK,CAAA;AAAA,EAChD,OAAA,EAAS,MAAM,CAAC,SAAS,CAAA;AAAA,EACzB,MAAA,EAAQ,MAAM,CAAC,QAAA,EAAU,MAAM,CAAA;AAAA,EAC/B,KAAA,EAAO,CAAC,OAAA,KAAoB,CAAC,UAAU,OAAO,CAAA;AAAA,EAC9C,aAAa,CAAC,OAAA,KAAoB,CAAC,QAAA,EAAU,SAAS,QAAQ,CAAA;AAAA,EAC9D,IAAA,EAAM,MAAM,CAAC,MAAM,CAAA;AAAA,EACnB,GAAA,EAAK,CAAC,KAAA,KAAkB,CAAC,QAAQ,KAAK,CAAA;AAAA,EACtC,cAAA,EAAgB,MAAM,CAAC,iBAAiB,CAAA;AAAA,EACxC,IAAA,EAAM,MAAM,CAAC,MAAM,CAAA;AAAA,EACnB,YAAA,EAAc,MAAM,CAAC,eAAe,CAAA;AAAA,EACpC,IAAA,EAAM,MAAM,CAAC,MAAM;AACrB,CAAA;;;ACZA,IAAI,MAAA,GAAS,QAAQ,GAAA,CAAI,mBAAA;AACzB,MAAA,KAAW,OAAO,MAAA,KAAW,WAAA,GAAc,MAAA,CAAO,SAAS,MAAA,GAAS,EAAA;AAE7D,IAAM,cAAA,GAAiB,MAAA;AAEZ,QAAQ,GAAA,CAAI;;;ACDvB,IAAM,WAAA,GAAc,MAAM,MAAA,CAAO;AAAA,EACtC,OAAA,EAAS;AACX,CAAC,CAAA;AAE+B,IAAI,WAAA;;;ACQpC,eAAsB,oBAAA,GAAmD;AACvE,EAAA,OAAA,CACE,MAAM,WAAA,CAAY,GAAA;AAAA,IAChB;AAAA,GACF,EACA,IAAA;AACJ;;;AClBO,IAAM,uBAAuB,MAAM;AACxC,EAAA,OAAO,QAAA,CAA4B;AAAA,IACjC,QAAA,EAAU,UAAU,YAAA,EAAa;AAAA,IACjC,OAAA,EAAS;AAAA,GACV,CAAA;AACH;ACsCuB,cAA0C,IAAI;AAErE,IAAI,cAAA,GAAiB,CAAA;AAmHrB,IAAM,SAAA,uBAAkD,GAAA,EAAI;AAErD,IAAM,OAAA,GAAU;AAAA,EACrB,MAAA,EAAQ,CAAC,OAAA,KAAkC;AACzC,IAAA,MAAM,EAAA,GAAK,OAAA,CAAQ,EAAA,IAAM,CAAA,MAAA,EAAS,EAAE,cAAc,CAAA,CAAA;AAClD,IAAA,SAAA,CAAU,OAAA;AAAA,MAAQ,CAAC,QAAA,KACjB,QAAA,CAAS,EAAE,IAAA,EAAM,QAAA,EAAU,OAAA,EAAS,EAAE,GAAG,OAAA,EAAS,EAAA,EAAG,EAAG;AAAA,KAC1D;AACA,IAAA,OAAO,EAAA;AAAA,EACT,CAAA;AAAA,EACA,OAAA,EAAS,CAAC,OAAA,KACR,OAAA,CAAQ,MAAA,CAAO,EAAE,GAAG,OAAA,EAAS,IAAA,EAAM,SAAA,EAAW,CAAA;AAAA,EAChD,KAAA,EAAO,CAAC,OAAA,KACN,OAAA,CAAQ,MAAA,CAAO,EAAE,GAAG,OAAA,EAAS,IAAA,EAAM,OAAA,EAAS,CAAA;AAAA,EAC9C,OAAA,EAAS,CAAC,OAAA,KACR,OAAA,CAAQ,MAAA,CAAO,EAAE,GAAG,OAAA,EAAS,IAAA,EAAM,SAAA,EAAW,CAAA;AAAA,EAChD,IAAA,EAAM,CAAC,OAAA,KACL,OAAA,CAAQ,MAAA,CAAO,EAAE,GAAG,OAAA,EAAS,IAAA,EAAM,MAAA,EAAQ,CAAA;AAAA,EAC7C,OAAA,EAAS,CAAC,OAAA,KACR,OAAA,CAAQ,MAAA,CAAO,EAAE,GAAG,OAAA,EAAS,IAAA,EAAM,SAAA,EAAW,CAAA;AAAA,EAChD,OAAA,EAAS,CAAC,EAAA,KAAe;AACvB,IAAA,SAAA,CAAU,OAAA,CAAQ,CAAC,QAAA,KAAa,QAAA,CAAS,EAAE,IAAA,EAAM,SAAA,EAAW,EAAA,EAAI,CAAC,CAAA;AAAA,EACnE,CAAA;AAAA;AAAA,EAEA,MAAA,EAAQ,CAAC,EAAA,KAAe;AACtB,IAAA,SAAA,CAAU,OAAA,CAAQ,CAAC,QAAA,KAAa,QAAA,CAAS,EAAE,IAAA,EAAM,SAAA,EAAW,EAAA,EAAI,CAAC,CAAA;AAAA,EACnE,CAAA;AAAA,EACA,MAAA,EAAQ,CAAC,EAAA,EAAY,OAAA,KAAmC;AACtD,IAAA,SAAA,CAAU,OAAA,CAAQ,CAAC,QAAA,KAAa,QAAA,CAAS,EAAE,MAAM,QAAA,EAAU,EAAA,EAAI,OAAA,EAAS,CAAC,CAAA;AAAA,EAC3E,CAAA;AAAA,EACA,SAAA,EAAW,CAAC,QAAA,KAA0C;AACpD,IAAA,SAAA,CAAU,IAAI,QAAQ,CAAA;AACtB,IAAA,OAAO,MAAM,SAAA,CAAU,MAAA,CAAO,QAAQ,CAAA;AAAA,EACxC;AACF,CAAA;;;ACpMO,SAAS,aAAA,GAAgB;AAC9B,EAAA,SAAS,qBAAA,GAAwB;AAC/B,IAAA,OAAA,CAAQ,MAAA,CAAO;AAAA,MACb,KAAA,EAAO,oBAAA;AAAA,MACP,IAAA,EAAM,SAAA;AAAA,MACN,QAAA,EAAU;AAAA,KACX,CAAA;AAAA,EACH;AACA,EAAA,OAAO;AAAA,IACL;AAAA,GACF;AACF;;;ACXO,SAAS,iBAAA,GAAoB;AAClC,EAAA,SAAS,aAAa,OAAA,EAAiB;AACrC,IAAA,OAAA,CAAQ,MAAA,CAAO;AAAA,MACb,WAAA,EAAa,OAAA;AAAA,MACb,IAAA,EAAM,MAAA;AAAA,MACN,QAAA,EAAU,GAAA;AAAA,MACV,QAAA,EAAU;AAAA,KACX,CAAA;AAAA,EACH;AAEA,EAAA,SAAS,SAAA,CAAU,OAAe,KAAA,EAAgB;AAChD,IAAA,OAAA,CAAQ,MAAA,CAAO;AAAA,MACb,KAAA;AAAA,MACA,WAAA,EAAa,OAAO,KAAK,CAAA;AAAA,MACzB,IAAA,EAAM,OAAA;AAAA,MACN,QAAA,EAAU,GAAA;AAAA,MACV,QAAA,EAAU;AAAA,KACX,CAAA;AAAA,EACH;AAEA,EAAA,OAAO;AAAA,IACL,YAAA;AAAA,IACA;AAAA,GACF;AACF;AC2CO,IAAM,kBAAA,GAAqBA,cAEhC,MAAS,CAAA;AAYJ,IAAM,wBAAwB,MAA0C;AAC7E,EAAA,OAAOC,WAAW,kBAAkB,CAAA;AACtC;AC7EA,SAAS,KAAA,CACP,UAAA,EAEA,eAAA,EACA,YAAA,EACyB;AAEzB,EAA2B;AACzB,IAAA,OAAA,CAAQ,GAAA,CAAI,YAAA,EAAc,UAAA,EAAY,eAAA,EAAiB,YAAY,CAAA;AAAA,EACrE;AACA,EAAA,OAAOC,OAAA,CAAI,UAAA,EAAY,eAAA,EAAiB,YAAY,CAAA;AACtD;AAqLO,IAAM,cAAA,GAAiB;AAAA,EAK5B,UAAA,EAAY,YAMd,CAAA;AA6BO,IAAM,kBAAA,GAAqB;AAAA,EAChC,OAAA,EAAS,SAAA;AAAA,EACT,MAAA,EAAQ;AACV,CAAA;AAUO,SAAS,uBAAuB,KAAA,EAA+B;AACpE,EAAA,KAAA,CAAM,6BAA6B,KAAK,CAAA;AAC1C;AC/OA,SAAS,uBAAA,GAA0B;AACjC,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAIC,SAAS,KAAK,CAAA;AACtC,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAIA,SAAS,CAAC,CAAA;AAC5C,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GACtCA,QAAAA,EAAmC;AACrC,EAAA,MAAM,SAAA,GAAY,OAA0B,IAAI,CAAA;AAEhD,EAAA,MAAM,OAAA,GAAUC,WAAAA,CAAY,CAACC,UAAAA,KAAsB;AACjD,IAAA,YAAA,CAAaA,UAAS,CAAA;AACtB,IAAA,OAAO,IAAI,OAAA,CAAiB,CAAC,OAAA,KAAY;AACvC,MAAA,iBAAA,CAAkB,MAAM,OAAO,CAAA;AAC/B,MAAA,OAAA,CAAQ,IAAI,CAAA;AAAA,IACd,CAAC,CAAA;AAAA,EACH,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,gBAAgB,MAAM;AAC1B,IAAA,sBAAA,CAAuB;AAAA,MACrB,QAAQ,cAAA,CAAe,UAAA;AAAA,MACvB,OAAO,kBAAA,CAAmB;AAAA,KAC3B,CAAA;AACD,IAAA,cAAA,GAAiB,IAAI,CAAA;AACrB,IAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,EACf,CAAA;AAEA,EAAA,MAAM,eAAe,MAAM;AACzB,IAAA,sBAAA,CAAuB;AAAA,MACrB,QAAQ,cAAA,CAAe,UAAA;AAAA,MACvB,OAAO,kBAAA,CAAmB;AAAA,KAC3B,CAAA;AACD,IAAA,cAAA,GAAiB,KAAK,CAAA;AACtB,IAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,EACf,CAAA;AAEA,EAAA,MAAM,uCACJC,IAAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,IAAA;AAAA,MACA,OAAA,EAAS,YAAA;AAAA,MACT,QAAA,EAAS,IAAA;AAAA,MACT,SAAA,EAAS,IAAA;AAAA,MACT,iBAAA,EAAgB,+BAAA;AAAA,MAEhB,QAAA,EAAA;AAAA,wBAAAA,IAAAA;AAAA,UAAC,WAAA;AAAA,UAAA;AAAA,YACC,EAAA,EAAG,+BAAA;AAAA,YACH,EAAA,EAAI,EAAE,QAAA,EAAU,UAAA,EAAY,YAAY,MAAA,EAAO;AAAA,YAChD,QAAA,EAAA;AAAA,cAAA,gBAAA;AAAA,cACgB,SAAA;AAAA,cAAU;AAAA;AAAA;AAAA,SAC3B;AAAA,wBACAC,GAAAA;AAAA,UAAC,UAAA;AAAA,UAAA;AAAA,YACC,YAAA,EAAW,OAAA;AAAA,YACX,OAAA,EAAS,YAAA;AAAA,YACT,EAAA,EAAI;AAAA,cACF,QAAA,EAAU,UAAA;AAAA,cACV,KAAA,EAAO,CAAA;AAAA,cACP,GAAA,EAAK,CAAA;AAAA,cACL,KAAA,EAAO;AAAA,aACT;AAAA,YAEA,QAAA,kBAAAA,IAAC,OAAA,EAAA,EAAQ;AAAA;AAAA,SACX;AAAA,wBACAA,GAAAA,CAAC,aAAA,EAAA,EACC,QAAA,kBAAAA,GAAAA,CAACC,MAAAA,EAAA,EAAM,OAAA,EAAQ,MAAA,EACb,QAAA,kBAAAF,IAAAA,CAAC,GAAA,EAAA,EAAI,QAAA,EAAA;AAAA,UAAA,iCAAA;AAAA,UAC6B,SAAA;AAAA,UAAU;AAAA,SAAA,EAE5C,GACF,CAAA,EACF,CAAA;AAAA,wBACAA,IAAAA,CAAC,aAAA,EAAA,EAAc,IAAI,EAAE,GAAA,EAAK,KAAI,EAC5B,QAAA,EAAA;AAAA,0BAAAC,GAAAA;AAAA,YAAC,MAAA;AAAA,YAAA;AAAA,cACC,GAAA,EAAK,SAAA;AAAA,cACL,OAAA,EAAS,YAAA;AAAA,cACT,OAAA,EAAQ,UAAA;AAAA,cACR,KAAA,EAAM,SAAA;AAAA,cACP,QAAA,EAAA;AAAA;AAAA,WAED;AAAA,0BACAA,GAAAA;AAAA,YAAC,MAAA;AAAA,YAAA;AAAA,cACC,KAAA,EAAM,UAAA;AAAA,cACN,OAAA,EAAQ,WAAA;AAAA,cACR,OAAA,EAAS,aAAA;AAAA,cACT,EAAA,EAAI,EAAE,EAAA,EAAI,GAAA,EAAI;AAAA,cACf,QAAA,EAAA;AAAA;AAAA;AAED,SAAA,EACF;AAAA;AAAA;AAAA,GACF;AAGF,EAAA,OAAO,EAAE,OAAA,EAAS,WAAA,EAAa,oBAAA,EAAqB;AACtD;AAEA,IAAO,+BAAA,GAAQ","file":"hooks.mjs","sourcesContent":["export const cacheKeys = {\n rowCount: (model: string) => [\"row_count\", model],\n lineage: () => [\"lineage\"],\n checks: () => [\"checks\", \"list\"],\n check: (checkId: string) => [\"checks\", checkId],\n checkEvents: (checkId: string) => [\"checks\", checkId, \"events\"],\n runs: () => [\"runs\"],\n run: (runId: string) => [\"runs\", runId],\n runsAggregated: () => [\"runs_aggregated\"],\n flag: () => [\"flag\"],\n instanceInfo: () => [\"instance_info\"],\n user: () => [\"user\"],\n};\n","let apiUrl = process.env.NEXT_PUBLIC_API_URL;\napiUrl ??= typeof window !== \"undefined\" ? window.location.origin : \"\";\n\nexport const PUBLIC_API_URL = apiUrl;\n\nlet cloudWebUrl = process.env.NEXT_PUBLIC_CLOUD_WEB_URL;\ncloudWebUrl ??= \"https://cloud.datarecce.io\";\n\nexport const PUBLIC_CLOUD_WEB_URL = cloudWebUrl;\n","import { QueryClient } from \"@tanstack/react-query\";\nimport axios from \"axios\";\nimport { PUBLIC_API_URL } from \"@/lib/const\";\n\nexport const axiosClient = axios.create({\n baseURL: PUBLIC_API_URL,\n});\n\nexport const reactQueryClient = new QueryClient();\n","import { AxiosResponse } from \"axios\";\nimport { axiosClient } from \"./axiosClient\";\n\nexport interface RecceInstanceInfo {\n server_mode: \"server\" | \"preview\" | \"read-only\";\n single_env: boolean;\n authed: boolean;\n cloud_instance: boolean;\n lifetime_expired_at?: Date;\n idle_timeout?: number;\n share_url?: string;\n session_id?: string;\n organization_name?: string;\n web_url?: string;\n}\n\nexport async function getRecceInstanceInfo(): Promise<RecceInstanceInfo> {\n return (\n await axiosClient.get<never, AxiosResponse<RecceInstanceInfo>>(\n \"/api/instance-info\",\n )\n ).data;\n}\n","import { useQuery } from \"@tanstack/react-query\";\nimport { cacheKeys } from \"../api/cacheKeys\";\nimport { getRecceInstanceInfo, RecceInstanceInfo } from \"../api/instanceInfo\";\n\nexport const useRecceInstanceInfo = () => {\n return useQuery<RecceInstanceInfo>({\n queryKey: cacheKeys.instanceInfo(),\n queryFn: getRecceInstanceInfo,\n });\n};\n","\"use client\";\n\nimport Alert from \"@mui/material/Alert\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport Snackbar from \"@mui/material/Snackbar\";\nimport Stack from \"@mui/material/Stack\";\nimport Typography from \"@mui/material/Typography\";\nimport {\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useState,\n} from \"react\";\n\n/**\n * Toast types and interfaces\n */\nexport interface ToastOptions {\n id?: string;\n title?: string;\n description?: ReactNode;\n type?: \"success\" | \"error\" | \"warning\" | \"info\" | \"loading\";\n duration?: number;\n closable?: boolean;\n action?: {\n label: string;\n onClick: () => void;\n };\n}\n\ninterface ToastState extends ToastOptions {\n id: string;\n open: boolean;\n}\n\ninterface ToasterContextValue {\n toast: (options: ToastOptions) => string;\n success: (options: Omit<ToastOptions, \"type\">) => string;\n error: (options: Omit<ToastOptions, \"type\">) => string;\n warning: (options: Omit<ToastOptions, \"type\">) => string;\n info: (options: Omit<ToastOptions, \"type\">) => string;\n loading: (options: Omit<ToastOptions, \"type\">) => string;\n dismiss: (id: string) => void;\n update: (id: string, options: Partial<ToastOptions>) => void;\n}\n\nconst ToasterContext = createContext<ToasterContextValue | null>(null);\n\nlet toastIdCounter = 0;\n\n/**\n * Simple toaster implementation using MUI Snackbar\n */\nexport function ToasterProvider({ children }: { children: ReactNode }) {\n const [toasts, setToasts] = useState<ToastState[]>([]);\n\n const createToast = useCallback((options: ToastOptions): string => {\n const id = options.id || `toast-${++toastIdCounter}`;\n const newToast: ToastState = {\n id,\n open: true,\n duration: options.type === \"loading\" ? null : (options.duration ?? 5000),\n closable: options.closable ?? true,\n ...options,\n } as ToastState;\n\n setToasts((prev) => {\n // Remove existing toast with same id\n const filtered = prev.filter((t) => t.id !== id);\n return [...filtered, newToast];\n });\n\n return id;\n }, []);\n\n const dismiss = useCallback((id: string) => {\n setToasts((prev) =>\n prev.map((t) => (t.id === id ? { ...t, open: false } : t)),\n );\n // Remove after animation\n setTimeout(() => {\n setToasts((prev) => prev.filter((t) => t.id !== id));\n }, 300);\n }, []);\n\n const update = useCallback((id: string, options: Partial<ToastOptions>) => {\n setToasts((prev) =>\n prev.map((t) => (t.id === id ? { ...t, ...options } : t)),\n );\n }, []);\n\n const contextValue: ToasterContextValue = {\n toast: createToast,\n success: (opts) => createToast({ ...opts, type: \"success\" }),\n error: (opts) => createToast({ ...opts, type: \"error\" }),\n warning: (opts) => createToast({ ...opts, type: \"warning\" }),\n info: (opts) => createToast({ ...opts, type: \"info\" }),\n loading: (opts) => createToast({ ...opts, type: \"loading\" }),\n dismiss,\n update,\n };\n\n return (\n <ToasterContext.Provider value={contextValue}>\n {children}\n {toasts.map((toast) => (\n <Snackbar\n key={toast.id}\n open={toast.open}\n autoHideDuration={toast.duration}\n onClose={() => toast.closable && dismiss(toast.id)}\n anchorOrigin={{ vertical: \"bottom\", horizontal: \"right\" }}\n >\n <Alert\n severity={toast.type === \"loading\" ? \"info\" : toast.type || \"info\"}\n onClose={toast.closable ? () => dismiss(toast.id) : undefined}\n icon={\n toast.type === \"loading\" ? (\n <CircularProgress size={20} color=\"inherit\" />\n ) : undefined\n }\n sx={{ width: \"100%\", minWidth: 300 }}\n >\n <Stack spacing={0.5}>\n {toast.title && (\n <Typography variant=\"subtitle2\" fontWeight=\"bold\">\n {toast.title}\n </Typography>\n )}\n {toast.description && (\n <Typography variant=\"body2\" component=\"div\">\n {toast.description}\n </Typography>\n )}\n </Stack>\n </Alert>\n </Snackbar>\n ))}\n </ToasterContext.Provider>\n );\n}\n\n/**\n * Hook to use the toaster\n */\nexport function useToaster(): ToasterContextValue {\n const context = useContext(ToasterContext);\n if (!context) {\n throw new Error(\"useToaster must be used within ToasterProvider\");\n }\n return context;\n}\n\n/**\n * Standalone toaster instance for use outside React context\n * Uses a simple event-based system\n */\ninterface ToastEvent {\n type: \"create\" | \"dismiss\" | \"update\";\n options?: ToastOptions;\n id?: string;\n}\n\nconst listeners: Set<(event: ToastEvent) => void> = new Set();\n\nexport const toaster = {\n create: (options: ToastOptions): string => {\n const id = options.id || `toast-${++toastIdCounter}`;\n listeners.forEach((listener) =>\n listener({ type: \"create\", options: { ...options, id } }),\n );\n return id;\n },\n success: (options: Omit<ToastOptions, \"type\">) =>\n toaster.create({ ...options, type: \"success\" }),\n error: (options: Omit<ToastOptions, \"type\">) =>\n toaster.create({ ...options, type: \"error\" }),\n warning: (options: Omit<ToastOptions, \"type\">) =>\n toaster.create({ ...options, type: \"warning\" }),\n info: (options: Omit<ToastOptions, \"type\">) =>\n toaster.create({ ...options, type: \"info\" }),\n loading: (options: Omit<ToastOptions, \"type\">) =>\n toaster.create({ ...options, type: \"loading\" }),\n dismiss: (id: string) => {\n listeners.forEach((listener) => listener({ type: \"dismiss\", id }));\n },\n // Alias for dismiss (for backward compatibility)\n remove: (id: string) => {\n listeners.forEach((listener) => listener({ type: \"dismiss\", id }));\n },\n update: (id: string, options: Partial<ToastOptions>) => {\n listeners.forEach((listener) => listener({ type: \"update\", id, options }));\n },\n subscribe: (listener: (event: ToastEvent) => void) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n};\n\n/**\n * Toaster component that renders toasts from the standalone toaster\n */\nexport function Toaster() {\n const [toasts, setToasts] = useState<ToastState[]>([]);\n\n // Subscribe to toast events\n useState(() => {\n const unsubscribe = toaster.subscribe((event) => {\n if (event.type === \"create\" && event.options) {\n const newToast: ToastState = {\n id: event.options.id || `toast-${++toastIdCounter}`,\n open: true,\n duration:\n event.options.type === \"loading\"\n ? undefined\n : (event.options.duration ?? 5000),\n closable: event.options.closable ?? true,\n ...event.options,\n } as ToastState;\n setToasts((prev) => {\n const filtered = prev.filter((t) => t.id !== newToast.id);\n return [...filtered, newToast];\n });\n } else if (event.type === \"dismiss\" && event.id) {\n const id = event.id;\n setToasts((prev) =>\n prev.map((t) => (t.id === id ? { ...t, open: false } : t)),\n );\n setTimeout(() => {\n setToasts((prev) => prev.filter((t) => t.id !== id));\n }, 300);\n } else if (event.type === \"update\" && event.id && event.options) {\n setToasts((prev) =>\n prev.map((t) => (t.id === event.id ? { ...t, ...event.options } : t)),\n );\n }\n });\n return unsubscribe;\n });\n\n const handleClose = (id: string) => {\n setToasts((prev) =>\n prev.map((t) => (t.id === id ? { ...t, open: false } : t)),\n );\n setTimeout(() => {\n setToasts((prev) => prev.filter((t) => t.id !== id));\n }, 300);\n };\n\n return (\n <>\n {toasts.map((toast) => (\n <Snackbar\n key={toast.id}\n open={toast.open}\n autoHideDuration={toast.duration}\n onClose={() => toast.closable && handleClose(toast.id)}\n anchorOrigin={{ vertical: \"bottom\", horizontal: \"right\" }}\n >\n <Alert\n severity={toast.type === \"loading\" ? \"info\" : toast.type || \"info\"}\n onClose={toast.closable ? () => handleClose(toast.id) : undefined}\n icon={\n toast.type === \"loading\" ? (\n <CircularProgress size={20} color=\"inherit\" />\n ) : undefined\n }\n sx={{ width: \"100%\", minWidth: 300 }}\n >\n <Stack spacing={0.5}>\n {toast.title && (\n <Typography variant=\"subtitle2\" fontWeight=\"bold\">\n {toast.title}\n </Typography>\n )}\n {toast.description && (\n <Typography variant=\"body2\" component=\"div\">\n {toast.description}\n </Typography>\n )}\n </Stack>\n </Alert>\n </Snackbar>\n ))}\n </>\n );\n}\n","import { toaster } from \"@/components/ui/toaster\";\n\nexport function useCheckToast() {\n function markedAsApprovedToast() {\n toaster.create({\n title: \"Marked as approved\",\n type: \"success\",\n duration: 2000,\n });\n }\n return {\n markedAsApprovedToast,\n };\n}\n","import { toaster } from \"@/components/ui/toaster\";\n\nexport function useClipBoardToast() {\n function successToast(message: string) {\n toaster.create({\n description: message,\n type: \"info\",\n duration: 5000,\n closable: true,\n });\n }\n\n function failToast(title: string, error: unknown) {\n toaster.create({\n title: title,\n description: String(error),\n type: \"error\",\n duration: 5000,\n closable: true,\n });\n }\n\n return {\n successToast,\n failToast,\n };\n}\n","import React, { createContext, useContext } from \"react\";\nimport { CllInput, ColumnLineageData } from \"@/lib/api/cll\";\nimport { LineageDiffViewOptions } from \"@/lib/api/lineagecheck\";\nimport { Run } from \"@/lib/api/types\";\nimport { LineageGraphNode, LineageGraphNodes } from \"./lineage\";\n\ntype NewType = LineageDiffViewOptions;\ntype ActionMode = \"per_node\" | \"multi_nodes\";\n\ninterface NodeAction {\n mode: ActionMode;\n status?: \"pending\" | \"running\" | \"success\" | \"failure\" | \"skipped\";\n skipReason?: string;\n run?: Run;\n}\n\nexport interface ActionState {\n mode: ActionMode;\n status: \"pending\" | \"running\" | \"canceling\" | \"canceled\" | \"completed\";\n currentRun?: Partial<Run>;\n completed: number;\n total: number;\n actions: Record<string, NodeAction>;\n}\n\nexport interface LineageViewContextType {\n interactive: boolean;\n nodes: LineageGraphNodes[];\n focusedNode?: LineageGraphNode;\n selectedNodes: LineageGraphNode[];\n cll: ColumnLineageData | undefined;\n\n // context menu\n showContextMenu: (event: React.MouseEvent, node: LineageGraphNodes) => void;\n\n // filter\n viewOptions: LineageDiffViewOptions;\n onViewOptionsChanged: (options: NewType) => void;\n\n // Multi nodes selection\n selectMode: \"selecting\" | \"action_result\" | undefined;\n selectNode: (nodeId: string) => void;\n selectParentNodes: (nodeId: string, degree?: number) => void;\n selectChildNodes: (nodeId: string, degree?: number) => void;\n deselect: () => void;\n\n // node state\n isNodeHighlighted: (nodeId: string) => boolean;\n isNodeSelected: (nodeId: string) => boolean;\n isEdgeHighlighted: (source: string, target: string) => boolean;\n getNodeAction: (nodeId: string) => NodeAction;\n getNodeColumnSet: (nodeId: string) => Set<string>;\n isNodeShowingChangeAnalysis: (nodeId: string) => boolean;\n\n //actions\n runRowCount: () => Promise<void>;\n runRowCountDiff: () => Promise<void>;\n runValueDiff: () => Promise<void>;\n addLineageDiffCheck: (viewMode?: string) => void;\n addSchemaDiffCheck: () => void;\n cancel: () => void;\n actionState: ActionState;\n\n // Column Level Lineage\n centerNode: (nodeId: string) => void;\n showColumnLevelLineage: (cll?: CllInput) => Promise<void>;\n resetColumnLevelLineage: (previous?: boolean) => Promise<void>;\n}\n\nexport const LineageViewContext = createContext<\n LineageViewContextType | undefined\n>(undefined);\n\nexport const useLineageViewContextSafe = (): LineageViewContextType => {\n const context = useContext(LineageViewContext);\n if (!context) {\n throw new Error(\n \"useLineageViewContext must be used within a LineageViewProvider\",\n );\n }\n return context;\n};\n\nexport const useLineageViewContext = (): LineageViewContextType | undefined => {\n return useContext(LineageViewContext);\n};\n","import {\n AmplitudeReturn,\n BaseEvent,\n EventOptions,\n Result,\n} from \"@amplitude/analytics-core\";\nimport { initAll, track as trk } from \"@amplitude/unified\";\n\nfunction track(\n eventInput: string | BaseEvent,\n // biome-ignore lint/suspicious/noExplicitAny: Amplitude library uses any for event properties\n eventProperties?: Record<string, any> | undefined,\n eventOptions?: EventOptions | undefined,\n): AmplitudeReturn<Result> {\n // If Amplitude isn't initialized, log to console instead\n if (!amplitudeInitialized) {\n console.log(\"[Tracking]\", eventInput, eventProperties, eventOptions);\n }\n return trk(eventInput, eventProperties, eventOptions);\n}\n\nlet amplitudeInitialized = false;\n\nexport function trackInit() {\n function getCookie(key: string) {\n const b = document.cookie.match(\"(^|;)\\\\s*\" + key + \"\\\\s*=\\\\s*([^;]+)\");\n return b ? b.pop() : \"\";\n }\n\n const userId =\n process.env.NODE_ENV === \"development\"\n ? \"web_dev\"\n : getCookie(\"recce_user_id\");\n const apiKey = process.env.AMPLITUDE_API_KEY;\n if (userId && apiKey) {\n try {\n void initAll(apiKey, {\n analytics: {\n userId,\n autocapture: true,\n },\n sessionReplay: {\n sampleRate: 1,\n },\n });\n amplitudeInitialized = true;\n } catch (e) {\n console.error(e);\n }\n }\n\n // Log when Amplitude is not initialized (for development/debugging)\n if (!amplitudeInitialized) {\n console.log(\n \"[Tracking] Amplitude not initialized (missing API key or user ID). Events will be logged to console instead.\",\n );\n }\n}\n\ninterface MultiNodeActionProps {\n type:\n | \"row_count\"\n | \"row_count_diff\"\n | \"value_diff\"\n | \"schema_diff\"\n | \"lineage_diff\";\n selected: \"single\" | \"multi\" | \"none\";\n}\n\nexport function trackMultiNodesAction(props: MultiNodeActionProps) {\n track(\"[Web] multi_nodes_action\", props);\n}\n\ninterface HistoryActionProps {\n name: \"show\" | \"hide\" | \"click_run\" | \"add_to_checklist\" | \"go_to_check\";\n}\n\nexport function trackHistoryAction(props: HistoryActionProps) {\n track(\"[Web] history_action\", props);\n}\n\ninterface PreviewChangeProps {\n action: \"explore\" | \"run\" | \"close\";\n node?: string;\n status?: \"success\" | \"failure\";\n}\n\nexport function trackPreviewChange(props: PreviewChangeProps) {\n track(\"[Experiment] preview_change\", props);\n}\n\ninterface PreviewChangeFeedbackProps {\n feedback: \"like\" | \"dislike\" | \"form\";\n node?: string;\n}\n\nexport function trackPreviewChangeFeedback(props: PreviewChangeFeedbackProps) {\n track(\"[Experiment] preview_change\", props);\n}\n\ninterface SingleEnvironmentProps {\n action:\n | \"onboarding\"\n | \"external_link\"\n | \"preview_changes\"\n | `target_base_added`;\n from?: \"onboarding\" | \"preview_changes\";\n node?: string;\n}\n\nexport function trackSingleEnvironment(props: SingleEnvironmentProps) {\n track(\"[Experiment] single_environment\", props);\n}\n\nexport function getExperimentTrackingBreakingChangeEnabled() {\n return false;\n}\n\ninterface ColumnLevelLineageProps {\n action: \"view\";\n source: \"schema_column\" | \"changed_column\" | \"cll_column\";\n}\n\nexport function trackColumnLevelLineage(props: ColumnLevelLineageProps) {\n track(\"[Web] column_level_lineage\", props);\n}\n\ninterface ShareStateProps {\n name: \"enable\" | \"create\" | \"copy\";\n}\n\nexport function trackShareState(props: ShareStateProps) {\n track(\"[Web] share_state\", props);\n}\n\ninterface StateActionProps {\n name: \"import\" | \"export\";\n}\n\nexport function trackStateAction(props: StateActionProps) {\n track(\"[Web] state_action\", props);\n}\n\ninterface CopyToClipboardProps {\n from: \"run\" | \"check\" | \"lineage_view\";\n type: string;\n}\n\nexport function trackCopyToClipboard(props: CopyToClipboardProps) {\n track(\"[Click] copy_to_clipboard\", props);\n}\n\ninterface TrackNavProps {\n from: string;\n to: string;\n}\n\nexport function trackNavigation(props: TrackNavProps) {\n track(\"[Web] navigation_change\", props);\n}\n\nexport interface LineageViewRenderProps {\n node_count: number;\n view_mode: string;\n impact_radius_enabled: boolean;\n cll_column_active?: boolean;\n right_sidebar_open: boolean;\n [status: string]: number | string | boolean | undefined;\n}\n\nexport function trackLineageViewRender(props: LineageViewRenderProps) {\n track(\"[Web] lineage_view_render\", props);\n}\n\nexport interface EnvironmentConfigProps {\n review_mode: boolean;\n adapter_type: string | null;\n has_git_info: boolean;\n has_pr_info: boolean;\n // Adapter-specific (shape varies by adapter_type)\n base?: {\n schema_count?: number;\n dbt_version?: string | null;\n timestamp?: string | null;\n has_env?: boolean;\n };\n current?: {\n schema_count?: number;\n dbt_version?: string | null;\n timestamp?: string | null;\n has_env?: boolean;\n };\n schemas_match?: boolean;\n}\n\nexport function trackEnvironmentConfig(props: EnvironmentConfigProps) {\n track(\"[Web] environment_config\", props);\n}\n\n// Explore action types\nexport const EXPLORE_ACTION = {\n ROW_COUNT: \"row_count\",\n ROW_COUNT_DIFF: \"row_count_diff\",\n PROFILE: \"profile\",\n PROFILE_DIFF: \"profile_diff\",\n VALUE_DIFF: \"value_diff\",\n SCHEMA_DIFF: \"schema_diff\",\n LINEAGE_DIFF: \"lineage_diff\",\n QUERY: \"query\",\n HISTOGRAM_DIFF: \"histogram_diff\",\n TOP_K_DIFF: \"top_k_diff\",\n} as const;\n\n// Explore action sources\nexport const EXPLORE_SOURCE = {\n LINEAGE_VIEW_TOP_BAR: \"lineage_view_top_bar\",\n LINEAGE_VIEW_CONTEXT_MENU: \"lineage_view_context_menu\",\n NODE_KEBAB_MENU: \"node_kebab_menu\",\n NODE_SIDEBAR_SINGLE_ENV: \"node_sidebar_single_env\",\n NODE_SIDEBAR_MULTI_ENV: \"node_sidebar_multi_env\",\n SCHEMA_ROW_COUNT_BUTTON: \"schema_row_count_button\",\n SCHEMA_COLUMN_MENU: \"schema_column_menu\",\n} as const;\n\nexport type ExploreActionType =\n (typeof EXPLORE_ACTION)[keyof typeof EXPLORE_ACTION];\nexport type ExploreSourceType =\n (typeof EXPLORE_SOURCE)[keyof typeof EXPLORE_SOURCE];\n\ninterface ExploreActionProps {\n action: ExploreActionType;\n source: ExploreSourceType;\n node_count?: number;\n}\n\nexport function trackExploreAction(props: ExploreActionProps) {\n track(\"[Web] explore_action\", props);\n}\n\n// Explore action form events\nexport const EXPLORE_FORM_EVENT = {\n EXECUTE: \"execute\",\n CANCEL: \"cancel\",\n} as const;\n\nexport type ExploreFormEventType =\n (typeof EXPLORE_FORM_EVENT)[keyof typeof EXPLORE_FORM_EVENT];\n\ninterface ExploreActionFormProps {\n action: ExploreActionType;\n event: ExploreFormEventType;\n}\n\nexport function trackExploreActionForm(props: ExploreActionFormProps) {\n track(\"[Web] explore_action_form\", props);\n}\n\n// Helper to check if a run type is an explore action\nexport function isExploreAction(type: string): type is ExploreActionType {\n return Object.values(EXPLORE_ACTION).includes(type as ExploreActionType);\n}\n\n// Lineage selection action types\nexport const LINEAGE_SELECTION_ACTION = {\n SELECT_PARENT_NODES: \"select_parent_nodes\",\n SELECT_CHILD_NODES: \"select_child_nodes\",\n SELECT_ALL_UPSTREAM: \"select_all_upstream\",\n SELECT_ALL_DOWNSTREAM: \"select_all_downstream\",\n} as const;\n\nexport type LineageSelectionActionType =\n (typeof LINEAGE_SELECTION_ACTION)[keyof typeof LINEAGE_SELECTION_ACTION];\n\ninterface LineageSelectionProps {\n action: LineageSelectionActionType;\n node_count?: number;\n}\n\nexport function trackLineageSelection(props: LineageSelectionProps) {\n track(\"[Web] lineage_selection\", props);\n}\n","import Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport MuiDialog from \"@mui/material/Dialog\";\nimport DialogActions from \"@mui/material/DialogActions\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport DialogTitle from \"@mui/material/DialogTitle\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Stack from \"@mui/material/Stack\";\nimport React, { useCallback, useRef, useState } from \"react\";\nimport { IoClose } from \"react-icons/io5\";\nimport {\n EXPLORE_ACTION,\n EXPLORE_FORM_EVENT,\n trackExploreActionForm,\n} from \"@/lib/api/track\";\n\nfunction useValueDiffAlertDialog() {\n const [open, setOpen] = useState(false);\n const [nodeCount, setNodeCount] = useState(0);\n const [resolvePromise, setResolvePromise] =\n useState<(value: boolean) => void>();\n const cancelRef = useRef<HTMLButtonElement>(null);\n\n const confirm = useCallback((nodeCount: number) => {\n setNodeCount(nodeCount);\n return new Promise<boolean>((resolve) => {\n setResolvePromise(() => resolve);\n setOpen(true);\n });\n }, []);\n\n const handleConfirm = () => {\n trackExploreActionForm({\n action: EXPLORE_ACTION.VALUE_DIFF,\n event: EXPLORE_FORM_EVENT.EXECUTE,\n });\n resolvePromise?.(true);\n setOpen(false);\n };\n\n const handleCancel = () => {\n trackExploreActionForm({\n action: EXPLORE_ACTION.VALUE_DIFF,\n event: EXPLORE_FORM_EVENT.CANCEL,\n });\n resolvePromise?.(false);\n setOpen(false);\n };\n\n const ValueDiffAlertDialog = (\n <MuiDialog\n open={open}\n onClose={handleCancel}\n maxWidth=\"md\"\n fullWidth\n aria-labelledby=\"value-diff-alert-dialog-title\"\n >\n <DialogTitle\n id=\"value-diff-alert-dialog-title\"\n sx={{ fontSize: \"1.125rem\", fontWeight: \"bold\" }}\n >\n Value Diff on {nodeCount} nodes\n </DialogTitle>\n <IconButton\n aria-label=\"close\"\n onClick={handleCancel}\n sx={{\n position: \"absolute\",\n right: 8,\n top: 8,\n color: \"grey.500\",\n }}\n >\n <IoClose />\n </IconButton>\n <DialogContent>\n <Stack spacing=\"20px\">\n <Box>\n Value diff will be executed on {nodeCount} nodes in the Lineage,\n which can add extra costs to your bill.\n </Box>\n </Stack>\n </DialogContent>\n <DialogActions sx={{ gap: 0.5 }}>\n <Button\n ref={cancelRef}\n onClick={handleCancel}\n variant=\"outlined\"\n color=\"neutral\"\n >\n Cancel\n </Button>\n <Button\n color=\"iochmara\"\n variant=\"contained\"\n onClick={handleConfirm}\n sx={{ ml: 1.5 }}\n >\n Execute\n </Button>\n </DialogActions>\n </MuiDialog>\n );\n\n return { confirm, AlertDialog: ValueDiffAlertDialog };\n}\n\nexport default useValueDiffAlertDialog;\n"]}