+
@@ -327,98 +252,70 @@ export function NotebookComponent({ showPageHeader }: NotebookComponentProps) {
- {isSavedObjectNotebook && (
-
-
-
- }
- title="Query"
- description="Write contents directly using markdown, SQL or PPL."
- footer={
-
- createParagraph({
- index: 0,
- input: {
- inputText: '%ppl ',
- inputType: 'CODE',
- },
- })
- }
- style={{ marginBottom: 17 }}
- >
- Add query
-
- }
- />
-
-
- }
- title="Visualization"
- description="Import OpenSearch Dashboards or Observability visualizations to the notes."
- footer={
-
- createParagraph({
- index: 0,
- input: {
- inputText: '',
- inputType: 'VISUALIZATION',
- },
- })
- }
- style={{ marginBottom: 17 }}
- >
- Add visualization
-
- }
- />
-
- {initialGoal ? (
-
- }
- title="Deep Research"
- description="Use deep research to analytics question."
- footer={
-
- createParagraph({
- index: 0,
- input: {
- inputText: initialGoal,
- inputType: DEEP_RESEARCH_PARAGRAPH_TYPE,
- },
- })
- }
- style={{ marginBottom: 17 }}
- >
- Add deep research
-
+
+
+
+ }
+ title="Query"
+ description="Write contents directly using markdown, SQL or PPL."
+ footer={
+
+ createParagraph({
+ index: 0,
+ input: {
+ inputText: '%ppl ',
+ inputType: 'CODE',
+ },
+ })
}
- />
-
- ) : null}
-
-
- )}
+ style={{ marginBottom: 17 }}
+ >
+ Add query
+
+ }
+ />
+
+
+ }
+ title="Visualization"
+ description="Import OpenSearch Dashboards or Observability visualizations to the notes."
+ footer={
+
+ createParagraph({
+ index: 0,
+ input: {
+ inputText: '',
+ inputType: 'VISUALIZATION',
+ },
+ })
+ }
+ style={{ marginBottom: 17 }}
+ >
+ Add visualization
+
+ }
+ />
+
+
+
+
)}
-
-
{isModalVisible && modalLayout}
>
);
}
-export const Notebook = ({ openedNoteId, ...rest }: NotebookProps) => {
+export const AgenticNotebook = ({ openedNoteId, ...rest }: AgenticNotebookProps) => {
const {
services: { dataSource },
} = useOpenSearchDashboards
();
diff --git a/public/components/notebooks/components/classic_notebook.tsx b/public/components/notebooks/components/classic_notebook.tsx
new file mode 100644
index 00000000..865e0c6f
--- /dev/null
+++ b/public/components/notebooks/components/classic_notebook.tsx
@@ -0,0 +1,337 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {
+ EuiCallOut,
+ EuiCard,
+ EuiEmptyPrompt,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiIcon,
+ EuiLoadingContent,
+ EuiOverlayMask,
+ EuiPage,
+ EuiPageBody,
+ EuiPageContent,
+ EuiPanel,
+ EuiSmallButton,
+ EuiSpacer,
+ EuiText,
+} from '@elastic/eui';
+import React, { useState, useEffect, useRef } from 'react';
+
+import { useContext } from 'react';
+import { useObservable } from 'react-use';
+import { useCallback } from 'react';
+import { NoteBookServices } from 'public/types';
+import { ParagraphState } from '../../../../common/state/paragraph_state';
+import { CREATE_NOTE_MESSAGE, NOTEBOOKS_API_PREFIX } from '../../../../common/constants/notebooks';
+import { NotebookComponentProps, NotebookType } from '../../../../common/types/notebooks';
+import { getCustomModal, getDeleteModal } from './helpers/modal_containers';
+import { Paragraphs } from './paragraph_components/paragraphs';
+import {
+ NotebookContextProvider,
+ NotebookReactContext,
+ getDefaultState,
+} from '../context_provider/context_provider';
+import { InputPanel } from './input_panel';
+import { isValidUUID } from './helpers/notebooks_parser';
+import { useNotebook } from '../../../hooks/use_notebook';
+import { useOpenSearchDashboards } from '../../../../../../src/plugins/opensearch_dashboards_react/public';
+import { NotebookHeader } from './notebook_header';
+import { useParagraphs } from '../../../hooks/use_paragraphs';
+
+export interface ClassicNotebookProps extends NotebookComponentProps {
+ openedNoteId: string;
+}
+
+export function NotebookComponent({ showPageHeader }: NotebookComponentProps) {
+ const {
+ services: { http, notifications },
+ } = useOpenSearchDashboards();
+
+ const [isModalVisible, setIsModalVisible] = useState(false);
+ const [modalLayout, setModalLayout] = useState();
+ const { createParagraph, deleteParagraph } = useParagraphs();
+ const { loadNotebook: loadNotebookHook } = useNotebook();
+
+ const notebookContext = useContext(NotebookReactContext);
+ const { id: openedNoteId, paragraphs: paragraphsStates, path, isLoading } = useObservable(
+ notebookContext.state.getValue$(),
+ notebookContext.state.value
+ );
+ const { notebookType } = useObservable(
+ notebookContext.state.value.context.getValue$(),
+ notebookContext.state.value.context.value
+ );
+ const isSavedObjectNotebook = isValidUUID(openedNoteId);
+ const paraDivRefs = useRef>([]);
+
+ const showDeleteParaModal = (index: number) => {
+ setModalLayout(
+ getDeleteModal(
+ () => setIsModalVisible(false),
+ () => {
+ deleteParagraph(index);
+ setIsModalVisible(false);
+ },
+ 'Delete paragraph',
+ 'Are you sure you want to delete the paragraph? The action cannot be undone.'
+ )
+ );
+ setIsModalVisible(true);
+ };
+
+ const migrateNotebook = async (
+ migrateNoteName: string,
+ migrateNoteID: string
+ ): Promise => {
+ if (migrateNoteName.length >= 50 || migrateNoteName.length === 0) {
+ notifications.toasts.addDanger('Invalid notebook name');
+ return Promise.reject();
+ }
+ const migrateNoteObject = {
+ name: migrateNoteName,
+ noteId: migrateNoteID,
+ };
+ return http
+ .post(`${NOTEBOOKS_API_PREFIX}/note/migrate`, {
+ body: JSON.stringify(migrateNoteObject),
+ })
+ .then((res) => {
+ notifications.toasts.addSuccess(`Notebook "${migrateNoteName}" successfully created!`);
+ return res.id;
+ })
+ .catch((err) => {
+ notifications.toasts.addDanger(
+ 'Error migrating notebook, please make sure you have the correct permission.'
+ );
+ console.error(err.body.message);
+ });
+ };
+
+ const showUpgradeModal = () => {
+ setModalLayout(
+ getCustomModal(
+ (newName: string) => {
+ migrateNotebook(newName, openedNoteId).then((id: string) => {
+ window.location.assign(`#/${id}`);
+ setTimeout(() => {
+ loadNotebook();
+ }, 300);
+ });
+ setIsModalVisible(false);
+ },
+ () => setIsModalVisible(false),
+ 'Name',
+ 'Upgrade notebook',
+ 'Cancel',
+ 'Upgrade',
+ path + ' (upgraded)',
+ CREATE_NOTE_MESSAGE
+ )
+ );
+ setIsModalVisible(true);
+ };
+
+ const scrollToPara = useCallback((index: number) => {
+ setTimeout(() => {
+ window.scrollTo({
+ left: 0,
+ top: paraDivRefs.current[index]?.offsetTop,
+ behavior: 'smooth',
+ });
+ }, 0);
+ }, []);
+
+ const handleInputPanelParagraphCreated = useCallback(() => {
+ scrollToPara(paraDivRefs.current.length - 1);
+ }, [scrollToPara]);
+
+ const loadNotebook = useCallback(() => {
+ loadNotebookHook()
+ .then(async (res) => {
+ if (res.context) {
+ notebookContext.state.updateContext(res.context);
+ }
+ notebookContext.state.updateValue({
+ dateCreated: res.dateCreated,
+ path: res.path,
+ vizPrefix: res.vizPrefix,
+ paragraphs: res.paragraphs.map((paragraph) => new ParagraphState(paragraph)),
+ owner: res.owner,
+ });
+ })
+ .catch((err) => {
+ notifications.toasts.addDanger(
+ 'Error fetching notebooks, please make sure you have the correct permission.'
+ );
+ console.error(err);
+ });
+ }, [loadNotebookHook, notifications.toasts, notebookContext.state]);
+
+ useEffect(() => {
+ loadNotebook();
+ }, [loadNotebook]);
+
+ if (!isLoading && notebookType === NotebookType.AGENTIC) {
+ return (
+
+
+ Error loading Notebook}
+ body={Incorrect notebook type
}
+ />
+
+
+ );
+ }
+
+ return (
+ <>
+
+
+ {showPageHeader && (
+
+ )}
+ {!isSavedObjectNotebook && (
+
+
+ Upgrade this notebook to take full advantage of the latest features
+
+ showUpgradeModal()}
+ >
+ Upgrade Notebook
+
+
+
+ )}
+
+ {isLoading ? (
+ } title={Loading Notebook
} />
+ ) : null}
+ {isLoading ? null : paragraphsStates.length > 0 ? (
+ paragraphsStates.map((paragraphState, index: number) => (
+ (paraDivRefs.current[index] = ref)}
+ key={`para_div_${paragraphState.value.id}`}
+ >
+ {index > 0 &&
}
+
+
+ ))
+ ) : (
+ // show default paragraph if no paragraphs in this notebook
+
+
+
+
+ No paragraphs
+
+ Add a paragraph to compose your document or story. Notebooks now support two
+ types of input:
+
+
+
+ {isSavedObjectNotebook && (
+
+
+
+ }
+ title="Query"
+ description="Write contents directly using markdown, SQL or PPL."
+ footer={
+
+ createParagraph({
+ index: 0,
+ input: {
+ inputText: '%ppl ',
+ inputType: 'CODE',
+ },
+ })
+ }
+ style={{ marginBottom: 17 }}
+ >
+ Add query
+
+ }
+ />
+
+
+ }
+ title="Visualization"
+ description="Import OpenSearch Dashboards or Observability visualizations to the notes."
+ footer={
+
+ createParagraph({
+ index: 0,
+ input: {
+ inputText: '',
+ inputType: 'VISUALIZATION',
+ },
+ })
+ }
+ style={{ marginBottom: 17 }}
+ >
+ Add visualization
+
+ }
+ />
+
+
+
+ )}
+
+
+
+ )}
+
+
+
+
+
+ {isModalVisible && modalLayout}
+ >
+ );
+}
+
+export const ClassicNotebook = ({ openedNoteId, ...rest }: ClassicNotebookProps) => {
+ const {
+ services: { dataSource },
+ } = useOpenSearchDashboards();
+ const stateRef = useRef(
+ getDefaultState({
+ id: openedNoteId,
+ dataSourceEnabled: !!dataSource,
+ })
+ );
+ return (
+
+
+
+ );
+};
diff --git a/public/components/notebooks/components/input_panel.tsx b/public/components/notebooks/components/input_panel.tsx
index d3bfa1a5..7c6228a7 100644
--- a/public/components/notebooks/components/input_panel.tsx
+++ b/public/components/notebooks/components/input_panel.tsx
@@ -70,8 +70,6 @@ export const InputPanel: React.FC = ({ onParagraphCreated }) =>
[paragraphs.length, dataSourceId, createParagraph, runParagraph, onParagraphCreated]
);
- return null;
-
return (
{
path={['/create', '/']}
render={(_props) =>
}
/>
+
{
+ return ;
+ }}
+ />
{
- return ;
+ return ;
}}
/>
diff --git a/public/components/notebooks/components/note_table.tsx b/public/components/notebooks/components/note_table.tsx
index 58653132..a711786f 100644
--- a/public/components/notebooks/components/note_table.tsx
+++ b/public/components/notebooks/components/note_table.tsx
@@ -382,7 +382,15 @@ export function NoteTable({ deleteNotebook }: NoteTableProps) {
sortable: true,
truncateText: true,
render: (value, record) => (
- {truncate(value, { length: 100 })}
+
+ {truncate(value, { length: 100 })}
+
),
},
{
diff --git a/public/plugin.tsx b/public/plugin.tsx
index 8733f473..23e86cd5 100644
--- a/public/plugin.tsx
+++ b/public/plugin.tsx
@@ -36,7 +36,10 @@ import {
setNotifications,
FindingService,
} from './services';
-import { Notebook, NotebookProps } from './components/notebooks/components/notebook';
+import {
+ ClassicNotebook,
+ ClassicNotebookProps,
+} from './components/notebooks/components/classic_notebook';
import { NOTEBOOK_APP_NAME } from '../common/constants/notebooks';
import { OpenSearchDashboardsContextProvider } from '../../../src/plugins/opensearch_dashboards_react/public';
import { paragraphRegistry } from './paragraphs';
@@ -170,12 +173,13 @@ export class InvestigationPlugin
}
);
- const getNotebook = async ({ openedNoteId }: Pick) => {
+ // TODO: check if we need to expose agentic notebook
+ const getNotebook = async ({ openedNoteId }: Pick) => {
const services = await getServices();
return (
-
+
);
};
diff --git a/test/notebooks_constants.ts b/test/notebooks_constants.ts
index b6ca46a0..7f87bb50 100644
--- a/test/notebooks_constants.ts
+++ b/test/notebooks_constants.ts
@@ -34,7 +34,7 @@ export const codeBlockNotebook = {
id: 'paragraph_de00ea2d-a8fb-45d1-8085-698f51c6b6be',
},
],
- context: { notebookType: 'Agentic' },
+ context: { notebookType: 'Classic' },
};
export const migrateBlockNotebook = {
@@ -77,7 +77,7 @@ export const emptyNotebook = {
dateCreated: '2023-12-14T18:49:43.375Z',
dateModified: '2023-12-15T06:13:23.463Z',
paragraphs: [],
- context: { notebookType: 'Agentic' },
+ context: { notebookType: 'Classic' },
};
// Sample notebook with all input and output