Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { DataExtensionEditorViewState } from "../../data-extensions-editor/share
import { ModeledMethodsList } from "./ModeledMethodsList";
import { percentFormatter } from "./formatters";
import { Mode } from "../../data-extensions-editor/shared/mode";
import { groupMethods } from "../../data-extensions-editor/shared/sorting";

const LoadingContainer = styled.div`
text-align: center;
Expand Down Expand Up @@ -75,7 +74,9 @@ export function DataExtensionsEditor({
const [externalApiUsages, setExternalApiUsages] = useState<
ExternalApiUsage[]
>(initialExternalApiUsages);
const [unsavedModels, setUnsavedModels] = useState<Set<string>>(new Set());
const [modifiedSignatures, setModifiedSignatures] = useState<Set<string>>(
new Set(),
);

const [modeledMethods, setModeledMethods] = useState<
Record<string, ModeledMethod>
Expand Down Expand Up @@ -119,15 +120,11 @@ export function DataExtensionsEditor({
),
};
});
setUnsavedModels(
(oldUnsavedModels) =>
setModifiedSignatures(
(oldModifiedSignatures) =>
new Set([
...oldUnsavedModels,
...modelsAffectedByNewModeledMethods(
msg.modeledMethods,
externalApiUsages,
viewState?.mode ?? Mode.Application,
),
...oldModifiedSignatures,
...Object.keys(msg.modeledMethods),
]),
);
break;
Expand All @@ -145,7 +142,7 @@ export function DataExtensionsEditor({
return () => {
window.removeEventListener("message", listener);
};
}, [externalApiUsages, viewState?.mode]);
}, []);

const modeledPercentage = useMemo(
() => calculateModeledPercentage(externalApiUsages),
Expand All @@ -160,8 +157,9 @@ export function DataExtensionsEditor({
...oldModeledMethods,
[method.signature]: model,
}));
setUnsavedModels(
(oldUnsavedModels) => new Set([...oldUnsavedModels, modelName]),
setModifiedSignatures(
(oldModifiedSignatures) =>
new Set([...oldModifiedSignatures, method.signature]),
);
},
[],
Expand All @@ -179,12 +177,11 @@ export function DataExtensionsEditor({
externalApiUsages,
modeledMethods,
});
setUnsavedModels(new Set());
setModifiedSignatures(new Set());
}, [externalApiUsages, modeledMethods]);

const onSaveModelClick = useCallback(
(
modelName: string,
externalApiUsages: ExternalApiUsage[],
modeledMethods: Record<string, ModeledMethod>,
) => {
Expand All @@ -193,10 +190,12 @@ export function DataExtensionsEditor({
externalApiUsages,
modeledMethods,
});
setUnsavedModels((oldUnsavedModels) => {
const newUnsavedModels = new Set(oldUnsavedModels);
newUnsavedModels.delete(modelName);
return newUnsavedModels;
setModifiedSignatures((oldModifiedSignatures) => {
const newModifiedSignatures = new Set([...oldModifiedSignatures]);
for (const externalApiUsage of externalApiUsages) {
newModifiedSignatures.delete(externalApiUsage.signature);
}
return newModifiedSignatures;
});
},
[],
Expand Down Expand Up @@ -317,8 +316,8 @@ export function DataExtensionsEditor({
</ButtonsContainer>
<ModeledMethodsList
externalApiUsages={externalApiUsages}
unsavedModels={unsavedModels}
modeledMethods={modeledMethods}
modifiedSignatures={modifiedSignatures}
viewState={viewState}
onChange={onChange}
onSaveModelClick={onSaveModelClick}
Expand All @@ -331,15 +330,3 @@ export function DataExtensionsEditor({
</DataExtensionsEditorContainer>
);
}

function modelsAffectedByNewModeledMethods(
modeledMethods: Record<string, ModeledMethod>,
externalApiUsages: ExternalApiUsage[],
mode: Mode,
): string[] {
const signatures = new Set(Object.keys(modeledMethods));
const affectedExternalApiUsages = externalApiUsages.filter(
(externalApiUsage) => signatures.has(externalApiUsage.signature),
);
return Object.keys(groupMethods(affectedExternalApiUsages, mode));
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,14 @@ type Props = {
libraryVersion?: string;
externalApiUsages: ExternalApiUsage[];
modeledMethods: Record<string, ModeledMethod>;
modifiedSignatures: Set<string>;
viewState: DataExtensionEditorViewState;
hasUnsavedChanges: boolean;
onChange: (
modelName: string,
externalApiUsage: ExternalApiUsage,
modeledMethod: ModeledMethod,
) => void;
onSaveModelClick: (
modelName: string,
externalApiUsages: ExternalApiUsage[],
modeledMethods: Record<string, ModeledMethod>,
) => void;
Expand All @@ -95,8 +94,8 @@ export const LibraryRow = ({
libraryVersion,
externalApiUsages,
modeledMethods,
modifiedSignatures,
viewState,
hasUnsavedChanges,
onChange,
onSaveModelClick,
onGenerateFromLlmClick,
Expand Down Expand Up @@ -137,11 +136,11 @@ export const LibraryRow = ({

const handleSave = useCallback(
async (e: React.MouseEvent) => {
onSaveModelClick(title, externalApiUsages, modeledMethods);
onSaveModelClick(externalApiUsages, modeledMethods);
e.stopPropagation();
e.preventDefault();
},
[title, externalApiUsages, modeledMethods, onSaveModelClick],
[externalApiUsages, modeledMethods, onSaveModelClick],
);

const onChangeWithModelName = useCallback(
Expand All @@ -151,6 +150,12 @@ export const LibraryRow = ({
[onChange, title],
);

const hasUnsavedChanges = useMemo(() => {
return externalApiUsages.some((externalApiUsage) =>
modifiedSignatures.has(externalApiUsage.signature),
);
}, [externalApiUsages, modifiedSignatures]);

return (
<LibraryContainer>
<TitleContainer onClick={toggleExpanded} aria-expanded={isExpanded}>
Expand Down Expand Up @@ -195,6 +200,7 @@ export const LibraryRow = ({
<ModeledMethodDataGrid
externalApiUsages={externalApiUsages}
modeledMethods={modeledMethods}
modifiedSignatures={modifiedSignatures}
mode={viewState.mode}
onChange={onChangeWithModelName}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
VSCodeCheckbox,
VSCodeDataGridCell,
VSCodeDataGridRow,
VSCodeLink,
Expand All @@ -20,6 +19,10 @@ import { extensiblePredicateDefinitions } from "../../data-extensions-editor/pre
import { Mode } from "../../data-extensions-editor/shared/mode";
import { Dropdown } from "../common/Dropdown";
import { MethodClassifications } from "./MethodClassifications";
import {
ModelingStatus,
ModelingStatusIndicator,
} from "./ModelingStatusIndicator";

const ApiOrMethodCell = styled(VSCodeDataGridCell)`
display: flex;
Expand Down Expand Up @@ -51,6 +54,7 @@ const modelTypeOptions: Array<{ value: ModeledMethodType; label: string }> = [
type Props = {
externalApiUsage: ExternalApiUsage;
modeledMethod: ModeledMethod | undefined;
methodIsUnsaved: boolean;
mode: Mode;
onChange: (
externalApiUsage: ExternalApiUsage,
Expand All @@ -59,11 +63,12 @@ type Props = {
};

export const MethodRow = (props: Props) => {
const { externalApiUsage, modeledMethod } = props;
const { externalApiUsage, modeledMethod, methodIsUnsaved } = props;

const methodCanBeModeled =
!externalApiUsage.supported ||
(modeledMethod && modeledMethod?.type !== "none");
(modeledMethod && modeledMethod?.type !== "none") ||
methodIsUnsaved;

if (methodCanBeModeled) {
return <ModelableMethodRow {...props} />;
Expand All @@ -73,7 +78,8 @@ export const MethodRow = (props: Props) => {
};

function ModelableMethodRow(props: Props) {
const { externalApiUsage, modeledMethod, mode, onChange } = props;
const { externalApiUsage, modeledMethod, methodIsUnsaved, mode, onChange } =
props;

const argumentsList = useMemo(() => {
if (externalApiUsage.methodParameters === "()") {
Expand Down Expand Up @@ -192,10 +198,12 @@ function ModelableMethodRow(props: Props) {
: undefined;
const showKindCell = predicate?.supportedKinds;

const modelingStatus = getModelingStatus(modeledMethod, methodIsUnsaved);

return (
<VSCodeDataGridRow>
<ApiOrMethodCell gridColumn={1}>
<VSCodeCheckbox />
<ModelingStatusIndicator status={modelingStatus} />
<ExternalApiUsageName {...props} />
{mode === Mode.Application && (
<UsagesButton onClick={jumpToUsage}>
Expand Down Expand Up @@ -251,7 +259,7 @@ function UnmodelableMethodRow(props: Props) {
return (
<VSCodeDataGridRow>
<ApiOrMethodCell gridColumn={1}>
<VSCodeCheckbox />
<ModelingStatusIndicator status="saved" />
<ExternalApiUsageName {...props} />
{mode === Mode.Application && (
<UsagesButton onClick={jumpToUsage}>
Expand Down Expand Up @@ -287,3 +295,17 @@ function sendJumpToUsageMessage(externalApiUsage: ExternalApiUsage) {
location: externalApiUsage.usages[0].url,
});
}

function getModelingStatus(
modeledMethod: ModeledMethod | undefined,
methodIsUnsaved: boolean,
): ModelingStatus {
if (modeledMethod) {
if (methodIsUnsaved) {
return "unsaved";
} else if (modeledMethod.type !== "none") {
return "saved";
}
}
return "unmodeled";
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { sortMethods } from "../../data-extensions-editor/shared/sorting";
type Props = {
externalApiUsages: ExternalApiUsage[];
modeledMethods: Record<string, ModeledMethod>;
modifiedSignatures: Set<string>;
mode: Mode;
onChange: (
externalApiUsage: ExternalApiUsage,
Expand All @@ -24,6 +25,7 @@ type Props = {
export const ModeledMethodDataGrid = ({
externalApiUsages,
modeledMethods,
modifiedSignatures,
mode,
onChange,
}: Props) => {
Expand Down Expand Up @@ -56,6 +58,7 @@ export const ModeledMethodDataGrid = ({
key={externalApiUsage.signature}
externalApiUsage={externalApiUsage}
modeledMethod={modeledMethods[externalApiUsage.signature]}
methodIsUnsaved={modifiedSignatures.has(externalApiUsage.signature)}
mode={mode}
onChange={onChange}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,15 @@ import { DataExtensionEditorViewState } from "../../data-extensions-editor/share

type Props = {
externalApiUsages: ExternalApiUsage[];
unsavedModels: Set<string>;
modeledMethods: Record<string, ModeledMethod>;
modifiedSignatures: Set<string>;
viewState: DataExtensionEditorViewState;
onChange: (
modelName: string,
externalApiUsage: ExternalApiUsage,
modeledMethod: ModeledMethod,
) => void;
onSaveModelClick: (
modelName: string,
externalApiUsages: ExternalApiUsage[],
modeledMethods: Record<string, ModeledMethod>,
) => void;
Expand All @@ -38,8 +37,8 @@ const libraryNameOverrides: Record<string, string> = {

export const ModeledMethodsList = ({
externalApiUsages,
unsavedModels,
modeledMethods,
modifiedSignatures,
viewState,
onChange,
onSaveModelClick,
Expand Down Expand Up @@ -79,8 +78,8 @@ export const ModeledMethodsList = ({
title={libraryNameOverrides[libraryName] ?? libraryName}
libraryVersion={libraryVersions[libraryName]}
externalApiUsages={grouped[libraryName]}
hasUnsavedChanges={unsavedModels.has(libraryName)}
modeledMethods={modeledMethods}
modifiedSignatures={modifiedSignatures}
viewState={viewState}
onChange={onChange}
onSaveModelClick={onSaveModelClick}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as React from "react";
import { assertNever } from "../../common/helpers-pure";
import { Codicon } from "../common/icon/Codicon";

export type ModelingStatus = "unmodeled" | "unsaved" | "saved";

interface Props {
status: ModelingStatus;
}

export function ModelingStatusIndicator({ status }: Props) {
switch (status) {
case "unmodeled":
return <Codicon name="circle-large-outline" label="Method not modeled" />;
case "unsaved":
return <Codicon name="pass" label="Changes have not been saved" />;
case "saved":
return <Codicon name="pass-filled" label="Method modeled" />;
default:
assertNever(status);
}
}