Skip to content
Draft
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
2 changes: 1 addition & 1 deletion packages/core/src/guided-answers-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import type {
import { HTML_ENHANCEMENT_DATA_ATTR_MARKER } from '@sap/guided-answers-extension-types';

const API_HOST = 'https://ga.support.sap.com';
const VERSION = 'v6';
const VERSION = 'v7';
const NODE_PATH = `/dtp/api/${VERSION}/nodes/`;
const TREE_PATH = `/dtp/api/${VERSION}/trees/`;
const IMG_PREFIX = '/dtp/viewer/';
Expand Down
18 changes: 12 additions & 6 deletions packages/core/test/guided-answers-api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ describe('Guided Answers Api: getTrees()', () => {
FIRST_NODE_ID: 100,
SCORE: 0.1,
COMPONENT: 'C1',
PRODUCT: 'P_one'
PRODUCT: 'P_one',
ACTIONS: []
},
{
TREE_ID: 2,
Expand All @@ -50,7 +51,8 @@ describe('Guided Answers Api: getTrees()', () => {
FIRST_NODE_ID: 200,
SCORE: 0.2,
COMPONENT: 'C2',
PRODUCT: 'P_two'
PRODUCT: 'P_two',
ACTIONS: []
},
{
TREE_ID: 3,
Expand All @@ -60,7 +62,8 @@ describe('Guided Answers Api: getTrees()', () => {
FIRST_NODE_ID: 300,
SCORE: 0.3,
COMPONENT: 'C3',
PRODUCT: 'P_three'
PRODUCT: 'P_three',
ACTIONS: []
}
],
resultSize: 3,
Expand Down Expand Up @@ -90,7 +93,8 @@ describe('Guided Answers Api: getTrees()', () => {
FIRST_NODE_ID: 100,
SCORE: 0.1,
COMPONENT: 'C1',
PRODUCT: 'P_one'
PRODUCT: 'P_one',
ACTIONS: []
},
{
TREE_ID: 2,
Expand All @@ -100,7 +104,8 @@ describe('Guided Answers Api: getTrees()', () => {
FIRST_NODE_ID: 200,
SCORE: 0.2,
COMPONENT: 'C2',
PRODUCT: 'P_two'
PRODUCT: 'P_two',
ACTIONS: []
},
{
TREE_ID: 3,
Expand All @@ -110,7 +115,8 @@ describe('Guided Answers Api: getTrees()', () => {
FIRST_NODE_ID: 300,
SCORE: 0.3,
COMPONENT: 'C3',
PRODUCT: 'P_three'
PRODUCT: 'P_three',
ACTIONS: []
}
],
resultSize: 3,
Expand Down
11 changes: 10 additions & 1 deletion packages/types/src/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ import {
GET_LAST_VISITED_GUIDES,
UPDATE_LAST_VISITED_GUIDES,
GO_TO_HOME_PAGE,
SET_QUICK_FILTERS
SET_QUICK_FILTERS,
EXPAND_ALL_SEARCH_NODES,
COLLAPSE_ALL_SEARCH_NODES,
EXPAND_SEARCH_NODES_FOR_TREE
} from './types';

export const updateGuidedAnswerTrees = (payload: UpdateGuidedAnswerTrees['payload']): UpdateGuidedAnswerTrees => ({
Expand Down Expand Up @@ -247,3 +250,9 @@ export const setQuickFilters = (payload: GuidedAnswersQueryFilterOptions[]): Set
type: SET_QUICK_FILTERS,
payload
});

export const expandAllSearchNodes = () => ({ type: EXPAND_ALL_SEARCH_NODES });

export const collapseAllSearchNodes = () => ({ type: COLLAPSE_ALL_SEARCH_NODES });

export const expandSearchNodesForTree = (payload: number) => ({ type: EXPAND_SEARCH_NODES_FOR_TREE, payload });
32 changes: 30 additions & 2 deletions packages/types/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,15 @@ export interface GuidedAnswersQueryPagingOptions {
offset: number;
}

export type GuidedAnswerTreeSearchHit = GuidedAnswerTree & { SCORE: number };
export interface GuidedAnswerTreeSearchAction {
DETAIL: string;
NODE_ID: GuidedAnswerNodeId;
SCORE: number;
TITLE: string;
TREE_ID: GuidedAnswerTreeId;
}

export type GuidedAnswerTreeSearchHit = GuidedAnswerTree & { ACTIONS: GuidedAnswerTreeSearchAction[]; SCORE: number };

export type ProductFilter = { PRODUCT: string; COUNT: number };

Expand Down Expand Up @@ -219,7 +227,10 @@ export type GuidedAnswerActions =
| UpdateGuidedAnswerTrees
| UpdateNetworkStatus
| WebviewReady
| SetQuickFilters;
| SetQuickFilters
| ExpandAllSearchNodes
| CollapseAllSearchNodes
| ExpandSearchNodesForTree;

export type NetworkStatus = 'OK' | 'LOADING' | 'ERROR';

Expand All @@ -241,6 +252,7 @@ export interface AppState {
activeScreen: 'HOME' | 'SEARCH' | 'NODE';
lastVisitedGuides: LastVisitedGuide[];
quickFilters: GuidedAnswersQueryFilterOptions[];
searchResultVisibleNodeCount: Record<GuidedAnswerTreeId, number>;
}

export const UPDATE_GUIDED_ANSWER_TREES = 'UPDATE_GUIDED_ANSWER_TREES';
Expand Down Expand Up @@ -452,3 +464,19 @@ export interface SetQuickFilters {
type: typeof SET_QUICK_FILTERS;
payload: GuidedAnswersQueryFilterOptions[];
}

export const EXPAND_ALL_SEARCH_NODES = 'EXPAND_ALL_SEARCH_NODES';
export interface ExpandAllSearchNodes {
type: typeof EXPAND_ALL_SEARCH_NODES;
}

export const COLLAPSE_ALL_SEARCH_NODES = 'COLLAPSE_ALL_SEARCH_NODES';
export interface CollapseAllSearchNodes {
type: typeof COLLAPSE_ALL_SEARCH_NODES;
}

export const EXPAND_SEARCH_NODES_FOR_TREE = 'EXPAND_SEARCH_NODES_FOR_TREE';
export interface ExpandSearchNodesForTree {
type: typeof EXPAND_SEARCH_NODES_FOR_TREE;
payload: number;
}
5 changes: 4 additions & 1 deletion packages/webapp/src/webview/i18n/i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,8 @@
"COPIED_TO_CLIPBOARD_DESC": "Copy this link to share via email or messages. When pasted into the search input field of the Guided Answers extension, it will navigate you straight to this node.",
"VIEW_ON_WEBSITE": "View on the GA website",
"COPY_WITH_INSTRUCTIONS": "Copy link with instructions",
"COPY_WITH_INSTRUCTIONS_TEXT": "To resolve your reported issue, open Guided Answers extension by SAP, and follow the steps mentioned in the guide.\n If your IDE is VSCode, please click on the following link: {{-extensionLink}} .\n If your IDE is SAP Business Application Studio, then launch Guided Answers via the command \"SAP: Open Guided Answers\" and paste the following guide shortlink into the search input field: {{-extensionLink}} ."
"COPY_WITH_INSTRUCTIONS_TEXT": "To resolve your reported issue, open Guided Answers extension by SAP, and follow the steps mentioned in the guide.\n If your IDE is VSCode, please click on the following link: {{-extensionLink}} .\n If your IDE is SAP Business Application Studio, then launch Guided Answers via the command \"SAP: Open Guided Answers\" and paste the following guide shortlink into the search input field: {{-extensionLink}} .",
"VIEW_MORE_RESULTS": "View {{-count}} more results",
"EXPAND_ALL_TREES": "Expand all trees",
"COLLAPSE_ALL_TREES": "Collapse all trees"
}
5 changes: 4 additions & 1 deletion packages/webapp/src/webview/state/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,8 @@ export {
getLastVisitedGuides,
updateBookmark,
synchronizeBookmark,
updateLastVisitedGuide
updateLastVisitedGuide,
expandAllSearchNodes,
collapseAllSearchNodes,
expandSearchNodesForTree
} from '@sap/guided-answers-extension-types';
56 changes: 53 additions & 3 deletions packages/webapp/src/webview/state/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import type {
FeedbackStatus,
GuidedAnswerNode,
GetLastVisitedGuides,
SetQuickFilters
SetQuickFilters,
ExpandSearchNodesForTree
} from '@sap/guided-answers-extension-types';
import i18next from 'i18next';
import type { Reducer } from 'redux';
Expand Down Expand Up @@ -52,7 +53,8 @@ export function getInitialState(): AppState {
bookmarks: {},
activeScreen: 'HOME',
lastVisitedGuides: [],
quickFilters: []
quickFilters: [],
searchResultVisibleNodeCount: {}
};
}

Expand Down Expand Up @@ -99,7 +101,10 @@ const reducers: Partial<Reducers> = {
GET_BOOKMARKS: getBookmarksReducer,
UPDATE_BOOKMARKS: updateBookmarksReducer,
GET_LAST_VISITED_GUIDES: getLastVisitedGuidesReducer,
SET_QUICK_FILTERS: setQuickFiltersReducer
SET_QUICK_FILTERS: setQuickFiltersReducer,
EXPAND_ALL_SEARCH_NODES: expandAllSearchNodes,
COLLAPSE_ALL_SEARCH_NODES: collapseAllSearchNodes,
EXPAND_SEARCH_NODES_FOR_TREE: expandSearchNodesForTree
};

/**
Expand Down Expand Up @@ -143,6 +148,9 @@ function updateGuidedAnswerTreesReducer(newState: AppState, action: UpdateGuided
if ((action.payload?.pagingOptions?.offset ?? 0) > 0) {
newState.guidedAnswerTreeSearchResult.trees.unshift(...trees);
}
for (const tree of newState.guidedAnswerTreeSearchResult.trees) {
newState.searchResultVisibleNodeCount[tree.TREE_ID] = Math.min(tree.ACTIONS.length, 2);
}
delete newState.activeGuidedAnswer;
newState.activeScreen = 'SEARCH';
return newState;
Expand Down Expand Up @@ -289,6 +297,7 @@ function setQueryValueReducer(newState: AppState, action: SetQueryValue): AppSta
productFilters: [],
trees: []
};
newState.searchResultVisibleNodeCount = {};
newState.query = action.payload;
return newState;
}
Expand Down Expand Up @@ -463,6 +472,47 @@ function setQuickFiltersReducer(newState: AppState, action: SetQuickFilters): Ap
return newState;
}

/**
* Expand all search nodes.
*
* @param newState - already cloned state that is modified and returned
* @returns new state with changes
*/
function expandAllSearchNodes(newState: AppState): AppState {
for (const tree of newState.guidedAnswerTreeSearchResult.trees) {
newState.searchResultVisibleNodeCount[tree.TREE_ID] = tree.ACTIONS.length;
}
return newState;
}

/**
* Collapse all search nodes.
*
* @param newState - already cloned state that is modified and returned
* @returns new state with changes
*/
function collapseAllSearchNodes(newState: AppState): AppState {
for (const tree of newState.guidedAnswerTreeSearchResult.trees) {
newState.searchResultVisibleNodeCount[tree.TREE_ID] = 0;
}
return newState;
}

/**
* Expand search nodes for a specific tree.
*
* @param newState - already cloned state that is modified and returned
* @param action - action with payload
* @returns new state with changes
*/
function expandSearchNodesForTree(newState: AppState, action: ExpandSearchNodesForTree): AppState {
const tree = newState.guidedAnswerTreeSearchResult.trees.find((t) => t.TREE_ID === action.payload);
if (tree) {
newState.searchResultVisibleNodeCount[tree.TREE_ID] = tree.ACTIONS.length;
}
return newState;
}

/**
* Restore the state, happens after deserializing of the webview panel.
*
Expand Down
6 changes: 5 additions & 1 deletion packages/webapp/src/webview/types.ts
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export type { AppState } from '@sap/guided-answers-extension-types';
export type {
AppState,
GuidedAnswerTreeSearchHit,
GuidedAnswerTreeSearchAction
} from '@sap/guided-answers-extension-types';
83 changes: 4 additions & 79 deletions packages/webapp/src/webview/ui/components/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,12 @@ import { GuidedAnswerNode } from '../GuidedAnswerNode';
import { Header } from '../Header';
import { ErrorScreen } from '../ErrorScreen';
import { FiltersRibbon } from '../Header/Filters';
import { FocusZone, FocusZoneDirection } from '@fluentui/react-focus';
import './App.scss';
import { initIcons, UILoader, UIIcon, UiIcons } from '@sap-ux/ui-components';
import InfiniteScroll from 'react-infinite-scroll-component';
import { initIcons, UILoader } from '@sap-ux/ui-components';
import { SpinnerSize } from '@fluentui/react';
import i18next from 'i18next';
import { TreeItemBottomSection } from '../TreeItemBottomSection';
import { HomeGrid } from '../HomeGrid';
import { SearchResults } from '../SearchResults';
import './App.scss';

initIcons();

Expand Down Expand Up @@ -50,33 +48,6 @@ export function App(): ReactElement {
};
}, []);

function fetchData() {
if (appState.guidedAnswerTreeSearchResult.resultSize > appState.pageSize) {
actions.searchTree({
query: appState.query,
filters: {
product: appState.selectedProductFilters,
component: appState.selectedComponentFilters
},
paging: {
responseSize: appState.pageSize,
offset: appState.guidedAnswerTreeSearchResult.trees.length
}
});
}
}

/**
* Check if a tree is bookmarked.
*
* @param treeId - id of the tree
* @param treeId.toString - id a string
* @returns boolean
*/
function isBookmark(treeId: { toString: () => string }): boolean {
return !!Object.keys(appState.bookmarks).find((bookmarkKey) => bookmarkKey.startsWith(`${treeId}-`));
}

let content;
if (appState.networkStatus === 'LOADING') {
content = <UILoader id="loading-indicator" size={SpinnerSize.large} />;
Expand All @@ -87,53 +58,7 @@ export function App(): ReactElement {
} else if (appState.activeScreen === 'HOME') {
content = <HomeGrid />;
} else {
content =
appState.guidedAnswerTreeSearchResult.resultSize === 0 ? (
<ErrorScreen title={i18next.t('NO_ANSWERS_FOUND')} subtitle={i18next.t('PLEASE_MODIFY_SEARCH')} />
) : (
<FocusZone direction={FocusZoneDirection.bidirectional} isCircularNavigation={true}>
<ul className="striped-list" role="listbox">
<InfiniteScroll
dataLength={appState.guidedAnswerTreeSearchResult.trees.length} //This is important field to render the next data
next={fetchData}
loader={<UILoader id="loading-indicator" size={SpinnerSize.large} />}
hasMore={
appState.guidedAnswerTreeSearchResult.trees.length <
appState.guidedAnswerTreeSearchResult.resultSize
}>
{appState.guidedAnswerTreeSearchResult.trees.map((tree) => {
return (
<li key={`tree-item-${tree.TITLE}`} className="tree-item" role="option">
<button
className="guided-answer__tree"
onClick={(): void => {
actions.setActiveTree(tree);
actions.selectNode(tree.FIRST_NODE_ID);
document.body.focus();
}}>
<div className="guided-answer__tree__ul">
<h3 className="guided-answer__tree__title">
{isBookmark(tree.TREE_ID) ? (
<UIIcon iconName={UiIcons.StarActive} />
) : (
''
)}{' '}
{tree.TITLE}
</h3>
<TreeItemBottomSection
description={tree.DESCRIPTION}
product={tree.PRODUCT}
component={tree.COMPONENT}
/>
</div>
</button>
</li>
);
})}
</InfiniteScroll>
</ul>
</FocusZone>
);
content = <SearchResults />;
}
return (
<div className="guided-answer">
Expand Down
Loading