Newer
Older
import { createContext, useContext, useEffect, useMemo } from "react";
import { useLoadQuestion } from "embedding-sdk/hooks/private/use-load-question";
import { useSdkSelector } from "embedding-sdk/store";
import { getPlugins } from "embedding-sdk/store/selectors";
import type { DataPickerValue } from "metabase/common/components/DataPicker";
import { useValidatedEntityId } from "metabase/lib/entity-id/hooks/use-validated-entity-id";
import { useCreateQuestion } from "metabase/query_builder/containers/use-create-question";
import { useSaveQuestion } from "metabase/query_builder/containers/use-save-question";
import { getEmbeddingMode } from "metabase/visualizations/click-actions/lib/modes";
import type Question from "metabase-lib/v1/Question";
EntityTypeFilterKeys,
InteractiveQuestionContextType,
InteractiveQuestionProviderProps,
} from "./types";
/**
* Note: This context should only be used as a wrapper for the InteractiveQuestionResult
* component. The idea behind this context is to allow the InteractiveQuestionResult component
* to use components within the ./components folder, which use the context for display
Phoomparin Mano
committed
* and functions.
* */
export const InteractiveQuestionContext = createContext<
InteractiveQuestionContextType | undefined
>(undefined);
const FILTER_MODEL_MAP: Record<EntityTypeFilterKeys, DataPickerValue["model"]> =
{
table: "table",
question: "card",
model: "dataset",
metric: "metric",
};
const mapEntityTypeFilterToDataPickerModels = (
entityTypeFilter: InteractiveQuestionProviderProps["entityTypeFilter"],
): InteractiveQuestionContextType["modelsFilterList"] => {
return entityTypeFilter?.map(entityType => FILTER_MODEL_MAP[entityType]);
};
export const InteractiveQuestionProvider = ({
deserializedCard,
componentPlugins,
onNavigateBack,
Phoomparin Mano
committed
children,
onBeforeSave,
onSave,
isSaveEnabled = true,
entityTypeFilter,
Phoomparin Mano
committed
saveToCollectionId,
}: InteractiveQuestionProviderProps) => {
const { id: cardId, isLoading: isLoadingValidatedId } = useValidatedEntityId({
type: "card",
id: initId,
});
const handleCreateQuestion = useCreateQuestion();
const handleSaveQuestion = useSaveQuestion();
const handleSave = async (question: Question) => {
if (isSaveEnabled) {
Phoomparin Mano
committed
const saveContext = { isNewQuestion: false };
await onBeforeSave?.(question, saveContext);
await handleSaveQuestion(question);
Phoomparin Mano
committed
onSave?.(question, saveContext);
await loadQuestion();
}
};
const handleCreate = async (question: Question) => {
Phoomparin Mano
committed
if (isSaveEnabled) {
const saveContext = { isNewQuestion: true };
await onBeforeSave?.(question, saveContext);
const createdQuestion = await handleCreateQuestion(question);
onSave?.(createdQuestion, saveContext);
// Set the latest saved question object to update the question title.
replaceQuestion(createdQuestion);
Phoomparin Mano
committed
}
Phoomparin Mano
committed
const {
question,
Phoomparin Mano
committed
originalQuestion,
Phoomparin Mano
committed
queryResults,
isQuestionLoading,
isQueryRunning,
Phoomparin Mano
committed
runQuestion,
replaceQuestion,
Phoomparin Mano
committed
loadQuestion,
Phoomparin Mano
committed
updateQuestion,
navigateToNewCard,
} = useLoadQuestion({
cardId,
options,
deserializedCard,
});
const globalPlugins = useSdkSelector(getPlugins);
const combinedPlugins = useMemo(() => {
return { ...globalPlugins, ...componentPlugins };
}, [globalPlugins, componentPlugins]);
Phoomparin Mano
committed
const mode = useMemo(() => {
return question && getEmbeddingMode(question, combinedPlugins ?? undefined);
}, [question, combinedPlugins]);
Phoomparin Mano
committed
const questionContext: InteractiveQuestionContextType = {
isQuestionLoading: isQuestionLoading || isLoadingValidatedId,
Phoomparin Mano
committed
isQueryRunning,
resetQuestion: loadQuestion,
Phoomparin Mano
committed
onNavigateBack,
Phoomparin Mano
committed
runQuestion,
replaceQuestion,
Phoomparin Mano
committed
updateQuestion,
navigateToNewCard,
plugins: combinedPlugins,
Phoomparin Mano
committed
question,
Phoomparin Mano
committed
originalQuestion,
Phoomparin Mano
committed
queryResults,
mode,
onSave: handleSave,
onCreate: handleCreate,
modelsFilterList: mapEntityTypeFilterToDataPickerModels(entityTypeFilter),
Phoomparin Mano
committed
isSaveEnabled,
saveToCollectionId,
Phoomparin Mano
committed
};
useEffect(() => {
loadQuestion();
}, [loadQuestion]);
return (
Phoomparin Mano
committed
<InteractiveQuestionContext.Provider value={questionContext}>
{children}
</InteractiveQuestionContext.Provider>
);
};
export const useInteractiveQuestionContext = () => {
const context = useContext(InteractiveQuestionContext);
if (context === undefined) {
throw new Error(
"useInteractiveQuestionContext must be used within a InteractiveQuestionProvider",
);
}
return context;
};