Skip to content

Commit

Permalink
Feature/new dashboard load UI (#657)
Browse files Browse the repository at this point in the history
* Added sidebar prototype

* Return of the sidebar

* Add database selector to sidebar

* Iterating on the dashboard sidebar interface

* Fixed usage of hardcoded color

* Updated dashboard loading mechanism, iterating

* Updated dashboard loading mechanism, iterating

* Updated file structure for dashboard sidebar

* Improved modal/menu handling for dashboard load

* Import/export dashboards

* New dashboard sharing interface

* Removed old save/load modal

* Finalized v1 of the new multi-dashboard UI

* removing useless imports

* Removed old isLoaded in state of dashboard load screen. Updated warning buttons

---------

Co-authored-by: Alfred Rubin <alfredo.rubin@neo4j.com>
  • Loading branch information
nielsdejong and Alfred Rubin committed Oct 27, 2023
1 parent 3a2cf4d commit 5e27a02
Show file tree
Hide file tree
Showing 36 changed files with 1,712 additions and 949 deletions.
4 changes: 2 additions & 2 deletions src/application/Application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,9 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(setConnected(false));
dispatch(createConnectionFromDesktopIntegrationThunk());
},
loadDashboard: (text) => {
loadDashboard: (uuid, text) => {
dispatch(clearNotification());
dispatch(loadDashboardThunk(text));
dispatch(loadDashboardThunk(uuid, text));
},
resetDashboard: () => dispatch(resetDashboardState()),
clearOldDashboard: () => dispatch(setOldDashboard(null)),
Expand Down
6 changes: 6 additions & 0 deletions src/application/ApplicationActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ export const setConnected = (connected: boolean) => ({
payload: { connected },
});

export const SET_DRAFT = 'APPLICATION/SET_DRAFT';
export const setDraft = (draft: boolean) => ({
type: SET_DRAFT,
payload: { draft },
});

export const SET_CONNECTION_MODAL_OPEN = 'APPLICATION/SET_CONNECTION_MODAL_OPEN';
export const setConnectionModalOpen = (open: boolean) => ({
type: SET_CONNECTION_MODAL_OPEN,
Expand Down
32 changes: 29 additions & 3 deletions src/application/ApplicationReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
* Reducers define changes to the application state when a given action is taken.
*/

import { HARD_RESET_CARD_SETTINGS, UPDATE_ALL_SELECTIONS, UPDATE_FIELDS, UPDATE_SCHEMA } from '../card/CardActions';
import { DEFAULT_NEO4J_URL } from '../config/ApplicationConfig';
import { SET_DASHBOARD, SET_DASHBOARD_UUID } from '../dashboard/DashboardActions';
import { UPDATE_DASHBOARD_SETTING } from '../settings/SettingsActions';
import {
CLEAR_DESKTOP_CONNECTION_PROPERTIES,
CLEAR_NOTIFICATION,
Expand All @@ -15,6 +18,7 @@ import {
SET_CONNECTION_PROPERTIES,
SET_DASHBOARD_TO_LOAD_AFTER_CONNECTING,
SET_DESKTOP_CONNECTION_PROPERTIES,
SET_DRAFT,
SET_OLD_DASHBOARD,
SET_PARAMETERS_TO_LOAD_AFTER_CONNECTING,
SET_REPORT_HELP_MODAL_OPEN,
Expand All @@ -36,6 +40,7 @@ const initialState = {
notificationMessage: null,
connectionModalOpen: false,
welcomeScreenOpen: true,
draft: false,
aboutModalOpen: false,
connection: {
protocol: 'neo4j',
Expand All @@ -55,6 +60,25 @@ const initialState = {
export const applicationReducer = (state = initialState, action: { type: any; payload: any }) => {
const { type, payload } = action;

// This is a special application-level flag used to determine whether the dashboard needs to be saved to the database.
if (action.type.startsWith('DASHBOARD/') || action.type.startsWith('PAGE/') || action.type.startsWith('CARD/')) {
// if anything changes EXCEPT for the selected page, we flag that we are drafting a dashboard.
const NON_TRANSFORMATIVE_ACTIONS = [
UPDATE_DASHBOARD_SETTING,
UPDATE_SCHEMA,
HARD_RESET_CARD_SETTINGS,
SET_DASHBOARD,
UPDATE_ALL_SELECTIONS,
UPDATE_FIELDS,
SET_DASHBOARD_UUID,
];
if (!state.draft && !NON_TRANSFORMATIVE_ACTIONS.includes(type)) {
state = update(state, { draft: true });
return state;
}
}

// Ignore any non-application actions.
if (!action.type.startsWith('APPLICATION/')) {
return state;
}
Expand All @@ -75,16 +99,18 @@ export const applicationReducer = (state = initialState, action: { type: any; pa
state = update(state, { connected: connected });
return state;
}
case SET_DRAFT: {
const { draft } = payload;
state = update(state, { draft: draft });
return state;
}
case SET_CONNECTION_MODAL_OPEN: {
const { open } = payload;
state = update(state, { connectionModalOpen: open });
return state;
}
case SET_ABOUT_MODAL_OPEN: {
const { open } = payload;
if (!open) {
console.log('');
}
state = update(state, { aboutModalOpen: open });
return state;
}
Expand Down
4 changes: 4 additions & 0 deletions src/application/ApplicationSelectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export const getNotificationTitle = (state: any) => {
return state.application.notificationTitle;
};

export const dashboardIsDraft = (state: any) => {
return state.application.draft;
};

export const applicationIsConnected = (state: any) => {
return state.application.connected;
};
Expand Down
13 changes: 9 additions & 4 deletions src/application/ApplicationThunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { DEFAULT_SCREEN, Screens } from '../config/ApplicationConfig';
import { setDashboard } from '../dashboard/DashboardActions';
import { NEODASH_VERSION } from '../dashboard/DashboardReducer';
import {
assignDashboardUuidIfNotPresentThunk,
loadDashboardFromNeo4jByNameThunk,
loadDashboardFromNeo4jByUUIDThunk,
loadDashboardFromNeo4jThunk,
loadDashboardThunk,
upgradeDashboardVersion,
} from '../dashboard/DashboardThunks';
Expand Down Expand Up @@ -40,6 +41,7 @@ import {
setReportHelpModalOpen,
} from './ApplicationActions';
import { version } from '../modal/AboutModal';
import { createUUID } from '../utils/uuid';

/**
* Application Thunks (https://redux.js.org/usage/writing-logic-thunks) handle complex state manipulations.
Expand Down Expand Up @@ -67,9 +69,12 @@ export const createConnectionThunk =
if (records && records[0] && records[0].error) {
dispatch(createNotificationThunk('Unable to establish connection', records[0].error));
} else if (records && records[0] && records[0].keys[0] == 'connected') {
// Connected to Neo4j. Set state accordingly.
dispatch(setConnectionProperties(protocol, url, port, database, username, password));
dispatch(setConnectionModalOpen(false));
dispatch(setConnected(true));
// An old dashboard (pre-2.3.5) may not always have a UUID. We catch this case here.
dispatch(assignDashboardUuidIfNotPresentThunk());
dispatch(updateSessionParameterThunk('session_uri', `${protocol}://${url}:${port}`));
dispatch(updateSessionParameterThunk('session_database', database));
dispatch(updateSessionParameterThunk('session_username', username));
Expand All @@ -83,11 +88,11 @@ export const createConnectionThunk =
) {
fetch(application.dashboardToLoadAfterConnecting)
.then((response) => response.text())
.then((data) => dispatch(loadDashboardThunk(data)));
.then((data) => dispatch(loadDashboardThunk(createUUID(), data)));
dispatch(setDashboardToLoadAfterConnecting(null));
} else if (application.dashboardToLoadAfterConnecting) {
const setDashboardAfterLoadingFromDatabase = (value) => {
dispatch(loadDashboardThunk(value));
dispatch(loadDashboardThunk(createUUID(), value));
};

// If we specify a dashboard by name, load the latest version of it.
Expand All @@ -103,7 +108,7 @@ export const createConnectionThunk =
);
} else {
dispatch(
loadDashboardFromNeo4jByUUIDThunk(
loadDashboardFromNeo4jThunk(
driver,
application.standaloneDashboardDatabase,
application.dashboardToLoadAfterConnecting,
Expand Down
2 changes: 1 addition & 1 deletion src/config/ApplicationConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const styleConfig = await StyleConfig.getInstance();

export const DEFAULT_SCREEN = Screens.WELCOME_SCREEN; // WELCOME_SCREEN
export const DEFAULT_NEO4J_URL = 'localhost'; // localhost
export const DEFAULT_DASHBOARD_TITLE = 'My dashboard'; // ''
export const DEFAULT_DASHBOARD_TITLE = 'New dashboard';

export const DASHBOARD_HEADER_COLOR = styleConfig?.style?.DASHBOARD_HEADER_COLOR || '#0B297D'; // '#0B297D'

Expand Down
57 changes: 39 additions & 18 deletions src/dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { forceRefreshPage } from '../page/PageActions';
import { getPageNumber } from '../settings/SettingsSelectors';
import { createNotificationThunk } from '../page/PageThunks';
import { version } from '../modal/AboutModal';
import NeoDashboardSidebar from './sidebar/DashboardSidebar';

const Dashboard = ({
pagenumber,
Expand Down Expand Up @@ -43,8 +44,12 @@ const Dashboard = ({
connection={connection}
onConnectionUpdate={onConnectionUpdate}
/>

{/* Navigation Bar */}
<div className='n-w-screen n-flex n-flex-row n-items-center n-bg-neutral-bg-weak n-border-b n-border-neutral-border-weak'>
<div
className='n-w-screen n-flex n-flex-row n-items-center n-bg-neutral-bg-weak n-border-b'
style={{ borderColor: 'lightgrey' }}
>
<NeoDashboardHeader
connection={connection}
onDownloadImage={onDownloadDashboardAsImage}
Expand All @@ -53,25 +58,41 @@ const Dashboard = ({
></NeoDashboardHeader>
</div>
{/* Main Page */}
<div className='n-w-full n-h-full n-overflow-y-scroll n-flex n-flex-row'>
{/* Main Content */}
<main className='n-flex-1 n-relative n-z-0 n-scroll-smooth n-w-full'>
<div className='n-absolute n-inset-0 page-spacing'>
<div className='page-spacing-overflow'>
{/* The main content of the page */}
{applicationSettings.standalonePassword ? (
<div style={{ textAlign: 'center', color: 'red', paddingTop: 60, marginBottom: -50 }}>
Warning: NeoDash is running with a plaintext password in config.json.
<div
style={{
display: 'flex',
height: 'calc(40vh - 32px)',
minHeight: window.innerHeight - 62,
overflow: 'hidden',
position: 'relative',
}}
>
<NeoDashboardSidebar />
<div className='n-w-full n-h-full n-flex n-flex-col n-items-center n-justify-center n-rounded-md'>
<div className='n-w-full n-h-full n-overflow-y-scroll n-flex n-flex-row'>
{/* Main Content */}
<main className='n-flex-1 n-relative n-z-0 n-scroll-smooth n-w-full'>
<div className='n-absolute n-inset-0 page-spacing'>
<div className='page-spacing-overflow'>
{/* The main content of the page */}

<div>
{applicationSettings.standalonePassword ? (
<div style={{ textAlign: 'center', color: 'red', paddingTop: 60, marginBottom: -50 }}>
Warning: NeoDash is running with a plaintext password in config.json.
</div>
) : (
<></>
)}
<NeoDashboardTitle />
<NeoDashboardHeaderPageList />
<NeoPage></NeoPage>
</div>
</div>
) : (
<></>
)}
<NeoDashboardTitle />
<NeoDashboardHeaderPageList />
<NeoPage></NeoPage>
</div>
</div>
</main>
</div>
</main>
</div>
</div>
</Neo4jProvider>
);
Expand Down
6 changes: 6 additions & 0 deletions src/dashboard/DashboardActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ export const setDashboard = (dashboard: any) => ({
payload: { dashboard },
});

export const SET_DASHBOARD_UUID = 'DASHBOARD/SET_DASHBOARD_UUID';
export const setDashboardUuid = (uuid: any) => ({
type: SET_DASHBOARD_UUID,
payload: { uuid },
});

export const SET_DASHBOARD_TITLE = 'DASHBOARD/SET_DASHBOARD_TITLE';
export const setDashboardTitle = (title: any) => ({
type: SET_DASHBOARD_TITLE,
Expand Down
21 changes: 17 additions & 4 deletions src/dashboard/DashboardReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { DEFAULT_DASHBOARD_TITLE } from '../config/ApplicationConfig';
import { extensionsReducer, INITIAL_EXTENSIONS_STATE } from '../extensions/state/ExtensionReducer';
import { FIRST_PAGE_INITIAL_STATE, pageReducer, PAGE_INITIAL_STATE } from '../page/PageReducer';
import { PAGE_EXAMPLE_STATE, pageReducer, PAGE_EMPTY_STATE } from '../page/PageReducer';
import { settingsReducer, SETTINGS_INITIAL_STATE } from '../settings/SettingsReducer';

import {
Expand All @@ -15,16 +15,25 @@ import {
SET_DASHBOARD,
MOVE_PAGE,
SET_EXTENSION_ENABLED,
SET_DASHBOARD_UUID,
} from './DashboardActions';

export const NEODASH_VERSION = '2.3';

export const initialState = {
title: DEFAULT_DASHBOARD_TITLE,
version: NEODASH_VERSION,
settings: SETTINGS_INITIAL_STATE,
pages: [PAGE_EXAMPLE_STATE],
parameters: {},
extensions: INITIAL_EXTENSIONS_STATE,
};

export const emptyDashboardState = {
title: DEFAULT_DASHBOARD_TITLE,
version: NEODASH_VERSION,
settings: SETTINGS_INITIAL_STATE,
pages: [FIRST_PAGE_INITIAL_STATE],
pages: [PAGE_EMPTY_STATE],
parameters: {},
extensions: INITIAL_EXTENSIONS_STATE,
};
Expand Down Expand Up @@ -68,12 +77,16 @@ export const dashboardReducer = (state = initialState, action: { type: any; payl
// Global dashboard updates are handled here.
switch (type) {
case RESET_DASHBOARD_STATE: {
return { ...initialState };
return { ...emptyDashboardState };
}
case SET_DASHBOARD: {
const { dashboard } = payload;
return { ...dashboard };
}
case SET_DASHBOARD_UUID: {
const { uuid } = payload;
return { uuid: uuid, ...state };
}
case SET_DASHBOARD_TITLE: {
const { title } = payload;
return { ...state, title: title };
Expand All @@ -86,7 +99,7 @@ export const dashboardReducer = (state = initialState, action: { type: any; payl
return { ...state, extensions: extensions };
}
case CREATE_PAGE: {
return { ...state, pages: [...state.pages, PAGE_INITIAL_STATE] };
return { ...state, pages: [...state.pages, PAGE_EMPTY_STATE] };
}
case REMOVE_PAGE: {
// Removes the card at a given index on a selected page number.
Expand Down
2 changes: 2 additions & 0 deletions src/dashboard/DashboardSelectors.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export const getDashboardUuid = (state: any) => state.dashboard.uuid;

export const getDashboardTitle = (state: any) => state.dashboard.title;

export const getDashboardSettings = (state: any) => state.dashboard.settings;
Expand Down
Loading

0 comments on commit 5e27a02

Please sign in to comment.