Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/new dashboard load UI #657

Merged
merged 19 commits into from
Oct 27, 2023
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
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 { 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 @@
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 @@
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 @@
) {
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 @@
);
} else {
dispatch(
loadDashboardFromNeo4jByUUIDThunk(
loadDashboardFromNeo4jThunk(
driver,
application.standaloneDashboardDatabase,
application.dashboardToLoadAfterConnecting,
Expand Down Expand Up @@ -215,13 +220,13 @@
const dashboardDatabase = urlParams.get('dashboardDatabase');
if (urlParams.get('credentials')) {
const connection = decodeURIComponent(urlParams.get('credentials'));
const protocol = connection.split('://')[0];

Check warning on line 223 in src/application/ApplicationThunks.ts

View workflow job for this annotation

GitHub Actions / build-test (18.x)

Use array destructuring
const username = connection.split('://')[1].split(':')[0];

Check warning on line 224 in src/application/ApplicationThunks.ts

View workflow job for this annotation

GitHub Actions / build-test (18.x)

Use array destructuring
const password = connection.split('://')[1].split(':')[1].split('@')[0];

Check warning on line 225 in src/application/ApplicationThunks.ts

View workflow job for this annotation

GitHub Actions / build-test (18.x)

Use array destructuring

const database = connection.split('@')[1].split(':')[0];

Check warning on line 227 in src/application/ApplicationThunks.ts

View workflow job for this annotation

GitHub Actions / build-test (18.x)

Use array destructuring
const url = connection.split('@')[1].split(':')[1];

Check warning on line 228 in src/application/ApplicationThunks.ts

View workflow job for this annotation

GitHub Actions / build-test (18.x)

Use array destructuring
const port = connection.split('@')[1].split(':')[2];

Check warning on line 229 in src/application/ApplicationThunks.ts

View workflow job for this annotation

GitHub Actions / build-test (18.x)

Use array destructuring
if (url == password) {
// Special case where a connect link is generated without a password.
// Here, the format is parsed incorrectly and we open the connection window instead.
Expand Down Expand Up @@ -507,7 +512,7 @@
dispatch(initializeApplicationAsEditorThunk(config, paramsToSetAfterConnecting));
}
} catch (e) {
console.log(e);

Check warning on line 515 in src/application/ApplicationThunks.ts

View workflow job for this annotation

GitHub Actions / build-test (18.x)

Unexpected console statement
dispatch(setWelcomeScreenOpen(false));
dispatch(
createNotificationThunk(
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
Loading