Skip to content

Commit 7b78b85

Browse files
AdiGajbhiyecoderabbitai[bot]mdesmet
authored
fix: indicate docs and tests edit changes (#1557)
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Michiel De Smet <mdesmet@gmail.com>
1 parent 5f446dd commit 7b78b85

21 files changed

+362
-278
lines changed

webview_panels/src/lib/altimate/main.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1695,6 +1695,7 @@
16951695
--gray--gray-02: #3a3d41;
16961696
--gray--blue--gray-01: #082247;
16971697
--gray--blue--gray-02: #374c6a;
1698+
--background-orange: #32240c;
16981699
}
16991700
.altimate-component.vscode-dark {
17001701
--primary-color: #247efe;
@@ -1730,6 +1731,7 @@
17301731
--gray--gray-02: #3a3d41;
17311732
--gray--blue--gray-01: #082247;
17321733
--gray--blue--gray-02: #374c6a;
1734+
--background-orange: #32240c;
17331735
}
17341736
.altimate-component .card {
17351737
padding: 16px;

webview_panels/src/main.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ body.vscode-high-contrast-light {
4343
--gray--gray-02: #3a3d41;
4444
--gray--blue--gray-01: #082247;
4545
--gray--blue--gray-02: #374c6a;
46+
--background-orange: #32240c;
4647
}
4748

4849
body.vscode-dark,
@@ -80,6 +81,7 @@ body.vscode-high-contrast:not(.vscode-high-contrast-light) {
8081
--gray--gray-02: #3a3d41;
8182
--gray--blue--gray-01: #082247;
8283
--gray--blue--gray-02: #374c6a;
84+
--background-orange: #32240c;
8385
}
8486

8587
body {

webview_panels/src/modules/documentationEditor/DocumentationEditor.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import useDocumentationContext from "./state/useDocumentationContext";
1717
import classes from "./styles.module.scss";
1818
import { addDefaultActions } from "./utils";
1919
import ConversationsRightPanel from "./components/conversation/ConversationsRightPanel";
20-
import useIncomingDocsDataHandler from "./useIncomingDocsDataHandler";
2120
import CoachAiIfModified from "./components/docGenerator/CoachAiIfModified";
2221
import Citations from "./components/docGenerator/Citations";
2322
import { Citation } from "@lib";
@@ -29,7 +28,6 @@ const DocumentationEditor = (): JSX.Element => {
2928
dispatch,
3029
} = useDocumentationContext();
3130
const { postMessageToDataPilot } = useAppContext();
32-
useIncomingDocsDataHandler();
3331

3432
const modelTests = useMemo(() => {
3533
return currentDocsTests?.filter((test) => !test.column_name);
@@ -130,6 +128,7 @@ const DocumentationEditor = (): JSX.Element => {
130128
onSubmit={onModelDocSubmit}
131129
placeholder="Describe your model"
132130
title={`Model: ${currentDocsData.name}`}
131+
tests={modelTests}
133132
/>
134133
<EntityWithTests
135134
title={currentDocsData.name}

webview_panels/src/modules/documentationEditor/DocumentationProvider.tsx

Lines changed: 163 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { executeRequestInAsync } from "@modules/app/requestExecutor";
1+
import {
2+
executeRequestInAsync,
3+
executeRequestInSync,
4+
} from "@modules/app/requestExecutor";
25
import { IncomingMessageProps } from "@modules/app/types";
36
import { panelLogger } from "@modules/logger";
47
import {
@@ -7,6 +10,7 @@ import {
710
useEffect,
811
useMemo,
912
useReducer,
13+
useRef,
1014
} from "react";
1115
import documentationSlice, {
1216
initialState,
@@ -21,6 +25,7 @@ import documentationSlice, {
2125
updateColumnsInCurrentDocsData,
2226
updateConversationsRightPanelState,
2327
updateCurrentDocsData,
28+
updateCurrentDocsTests,
2429
updateSelectedConversationGroup,
2530
updateUserInstructions,
2631
} from "./state/documentationSlice";
@@ -31,7 +36,7 @@ import {
3136
MetadataColumn,
3237
} from "./state/types";
3338
import { ContextProps } from "./types";
34-
import { getGenerationsInModel } from "./utils";
39+
import { getGenerationsInModel, isStateDirty } from "./utils";
3540
import DocumentationEditor from "./DocumentationEditor";
3641
import { ConversationGroup, DbtDocsShareDetails } from "@lib";
3742
import { TelemetryEvents } from "@telemetryEvents";
@@ -43,14 +48,38 @@ export const DocumentationContext = createContext<ContextProps>({
4348
dispatch: () => null,
4449
});
4550

51+
type IncomingMessageEvent = MessageEvent<
52+
IncomingMessageProps & {
53+
docs?: DBTDocumentation;
54+
tests?: DBTModelTest[];
55+
project?: string;
56+
columns?: DBTDocumentation["columns"];
57+
model?: string;
58+
name?: string;
59+
description?: string;
60+
collaborationEnabled?: boolean;
61+
missingDocumentationMessage?: {
62+
message: string;
63+
type: "error" | "warning";
64+
};
65+
}
66+
>;
67+
68+
enum ActionState {
69+
CANCEL_STAY = "Stay",
70+
DISCARD_PROCEED = "Discard",
71+
SAVE_PROCEED = "Save changes",
72+
}
73+
4674
const DocumentationProvider = (): JSX.Element => {
4775
const {
4876
state: { isComponentsApiInitialized },
4977
} = useAppContext();
5078
const [state, dispatch] = useReducer(
5179
documentationSlice.reducer,
52-
documentationSlice.getInitialState()
80+
documentationSlice.getInitialState(),
5381
);
82+
const stateRef = useRef(state);
5483

5584
const updateFocus = (name?: string) => {
5685
dispatch(setInsertedEntityName(name));
@@ -84,107 +113,147 @@ const DocumentationProvider = (): JSX.Element => {
84113
updateSelectedConversationGroup({
85114
shareId,
86115
conversationGroupId: conversation_group_id,
87-
})
116+
}),
88117
);
89118
};
90119

91-
const onMessage = useCallback(
92-
(
93-
event: MessageEvent<
94-
IncomingMessageProps & {
95-
docs?: DBTDocumentation;
96-
tests?: DBTModelTest[];
97-
project?: string;
98-
columns?: DBTDocumentation["columns"];
99-
model?: string;
100-
name?: string;
101-
description?: string;
102-
collaborationEnabled?: boolean;
103-
missingDocumentationMessage?: {
104-
message: string;
105-
type: "error" | "warning";
106-
};
107-
}
108-
>
109-
) => {
110-
const { command, ...params } = event.data;
111-
switch (command) {
112-
case "viewConversation":
113-
handleViewConversation(
114-
params as unknown as Parameters<typeof handleViewConversation>["0"]
115-
);
120+
const renderDocumentation = (event: IncomingMessageEvent) => {
121+
dispatch(
122+
setIncomingDocsData({
123+
docs: event.data.docs,
124+
tests: event.data.tests,
125+
}),
126+
);
127+
dispatch(setProject(event.data.project));
128+
dispatch(
129+
updateCollaborationEnabled(Boolean(event.data.collaborationEnabled)),
130+
);
131+
dispatch(
132+
setMissingDocumentationMessage(event.data.missingDocumentationMessage),
133+
);
134+
};
135+
136+
const onMessage = useCallback((event: IncomingMessageEvent) => {
137+
const { command, ...params } = event.data;
138+
switch (command) {
139+
case "viewConversation":
140+
handleViewConversation(
141+
params as unknown as Parameters<typeof handleViewConversation>["0"],
142+
);
143+
break;
144+
case "conversations:updates":
145+
handleConversationUpdates(
146+
params as unknown as Parameters<
147+
typeof handleConversationUpdates
148+
>["0"],
149+
);
150+
break;
151+
case "renderDocumentation": {
152+
if (
153+
event.data.docs?.uniqueId ===
154+
stateRef.current.currentDocsData?.uniqueId
155+
) {
116156
break;
117-
case "conversations:updates":
118-
handleConversationUpdates(
119-
params as unknown as Parameters<
120-
typeof handleConversationUpdates
121-
>["0"]
122-
);
157+
}
158+
if (!isStateDirty(stateRef.current)) {
159+
renderDocumentation(event);
123160
break;
124-
case "renderDocumentation":
125-
dispatch(
126-
setIncomingDocsData({
127-
docs: event.data.docs,
128-
tests: event.data.tests,
129-
})
130-
);
131-
dispatch(setProject(event.data.project));
132-
dispatch(
133-
updateCollaborationEnabled(Boolean(event.data.collaborationEnabled))
134-
);
161+
}
162+
const { currentDocsData, currentDocsTests } = stateRef.current;
163+
executeRequestInSync("showWarningMessage", {
164+
infoMessage: `You have unsaved changes in model: ‘${currentDocsData?.name}’. Would you
165+
like to discard the changes, save them and proceed, or remain in the
166+
current state?`,
167+
items: [
168+
ActionState.DISCARD_PROCEED,
169+
ActionState.CANCEL_STAY,
170+
ActionState.SAVE_PROCEED,
171+
],
172+
})
173+
.then(async (action) => {
174+
switch (action) {
175+
case ActionState.SAVE_PROCEED: {
176+
const result = (await executeRequestInSync(
177+
"saveDocumentation",
178+
{
179+
...currentDocsData,
180+
updatedTests: currentDocsTests,
181+
dialogType: "Existing file",
182+
},
183+
)) as { saved: boolean };
184+
if (result.saved) {
185+
dispatch(updateCurrentDocsData(event.data.docs));
186+
dispatch(updateCurrentDocsTests(event.data.tests));
187+
}
188+
renderDocumentation(event);
189+
break;
190+
}
191+
case ActionState.DISCARD_PROCEED: {
192+
dispatch(updateCurrentDocsData(event.data.docs));
193+
dispatch(updateCurrentDocsTests(event.data.tests));
194+
renderDocumentation(event);
195+
break;
196+
}
197+
case ActionState.CANCEL_STAY: {
198+
break;
199+
}
200+
default:
201+
break;
202+
}
203+
})
204+
.catch((err) => {
205+
panelLogger.error(
206+
"error while showing unsaved changes dialog",
207+
err,
208+
);
209+
});
210+
break;
211+
}
212+
case "renderColumnsFromMetadataFetch":
213+
if (event.data.columns) {
135214
dispatch(
136-
setMissingDocumentationMessage(
137-
event.data.missingDocumentationMessage
138-
)
215+
updateColumnsAfterSync({
216+
columns: event.data.columns,
217+
}),
139218
);
140-
break;
141-
case "renderColumnsFromMetadataFetch":
142-
if (event.data.columns) {
143-
dispatch(
144-
updateColumnsAfterSync({
145-
columns: event.data.columns,
146-
})
147-
);
148-
}
149-
break;
150-
case "docgen:insert":
151-
panelLogger.info("received new doc gen", event.data);
152-
// insert model desc
153-
if (params.model) {
154-
dispatch(
155-
updateCurrentDocsData({
156-
description: params.description,
157-
name: params.model,
158-
isNewGeneration: true,
159-
})
160-
);
161-
updateFocus(params.model);
162-
return;
163-
}
164-
// insert column desc
219+
}
220+
break;
221+
case "docgen:insert":
222+
panelLogger.info("received new doc gen", event.data);
223+
// insert model desc
224+
if (params.model) {
165225
dispatch(
166-
updateColumnsInCurrentDocsData({
167-
columns: [params as Partial<MetadataColumn>],
226+
updateCurrentDocsData({
227+
description: params.description,
228+
name: params.model,
168229
isNewGeneration: true,
169-
})
230+
}),
170231
);
171-
updateFocus((params as Partial<MetadataColumn>).name);
232+
updateFocus(params.model);
233+
return;
234+
}
235+
// insert column desc
236+
dispatch(
237+
updateColumnsInCurrentDocsData({
238+
columns: [params as Partial<MetadataColumn>],
239+
isNewGeneration: true,
240+
}),
241+
);
242+
updateFocus((params as Partial<MetadataColumn>).name);
172243

173-
break;
174-
default:
175-
break;
176-
}
177-
},
178-
[]
179-
);
244+
break;
245+
default:
246+
break;
247+
}
248+
}, []);
180249

181250
const loadGenerationsHistory = (project: string, model: string) => {
182251
getGenerationsInModel(project, model)
183252
.then((data) => {
184253
dispatch(setGenerationsHistory(data));
185254
})
186255
.catch((err) =>
187-
panelLogger.error("error while loading generations history", err)
256+
panelLogger.error("error while loading generations history", err),
188257
);
189258
};
190259

@@ -197,8 +266,8 @@ const DocumentationProvider = (): JSX.Element => {
197266
if (userInstructions) {
198267
dispatch(
199268
updateUserInstructions(
200-
JSON.parse(userInstructions) as DocsGenerateUserInstructions
201-
)
269+
JSON.parse(userInstructions) as DocsGenerateUserInstructions,
270+
),
202271
);
203272
}
204273
loadGenerationsHistory(state.project, state.currentDocsData.name);
@@ -220,9 +289,14 @@ const DocumentationProvider = (): JSX.Element => {
220289
state,
221290
dispatch,
222291
}),
223-
[state, dispatch]
292+
[state, dispatch],
224293
);
225294

295+
// hack to get latest state in onMessage
296+
useEffect(() => {
297+
stateRef.current = state;
298+
}, [state]);
299+
226300
if (!isComponentsApiInitialized) {
227301
return <div>Loading...</div>;
228302
}

0 commit comments

Comments
 (0)