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

2553 restructure databases #13

Merged
merged 6 commits into from
Feb 17, 2020
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
6 changes: 6 additions & 0 deletions src/renderer/actions/databaseEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ export const fetchDatabaseGlossary = () => async dispatch => {
}
};

export const COPY_SCHEDULE_DATA = 'COPY_SCHEDULE_DATA';
export const copyScheduleData = (name, copy) => ({
type: COPY_SCHEDULE_DATA,
payload: { name, copy }
});

export const UPDATE_DATABASE_VALIDATION = 'UPDATE_DATABASE_VALIDATION';
export const updateDatabaseValidation = validation => ({
type: UPDATE_DATABASE_VALIDATION,
Expand Down
156 changes: 124 additions & 32 deletions src/renderer/components/DatabaseEditor/DatabaseEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,32 @@ import {
resetDatabaseState,
initDatabaseState,
setActiveDatabase,
resetDatabaseChanges
resetDatabaseChanges,
copyScheduleData
} from '../../actions/databaseEditor';
import Table, { TableButtons, useTableUpdateRedux } from './Table';
import { months_short } from '../../constants/months';
import { AsyncError } from '../../utils';
import routes from '../../constants/routes';
import { useChangeRoute } from '../Project/Project';
import Handsontable from 'handsontable';
import NewScheduleModal from './NewScheduleModal';

const useValidateDatabasePath = () => {
const [valid, setValid] = useState(null);
const [error, setError] = useState(null);

const checkDBPathValidity = async () => {
try {
setValid(null);
setError(null);
const resp = await axios.get(
'http://localhost:5050/api/inputs/databases/check'
);
setValid(true);
} catch (err) {
console.log(err);
if (err.response) setError(err.response.data.message);
else setError('Could not read and verify databases.');
setValid(false);
}
};
Expand All @@ -40,7 +45,7 @@ const useValidateDatabasePath = () => {
checkDBPathValidity();
}, []);

return [valid, checkDBPathValidity];
return [valid, error, checkDBPathValidity];
};

const ValidationErrors = ({ databaseName }) => {
Expand Down Expand Up @@ -131,7 +136,7 @@ const SavingDatabaseModal = ({ visible, hideModal, error, success }) => {
};

const DatabaseEditor = () => {
const [valid, checkDBPathValidity] = useValidateDatabasePath();
const [valid, error, checkDBPathValidity] = useValidateDatabasePath();
const goToScript = useChangeRoute(`${routes.TOOLS}/data-initializer`);

if (valid === null)
Expand All @@ -158,7 +163,13 @@ const DatabaseEditor = () => {
<DatabaseContent />
) : (
<div>
<div>Could not find or validate databases. Try assigning one</div>
<div style={{ margin: 20 }}>
<p>
Could not find or validate databases. Try assigning a new
database
</p>
{error !== null && <details>{error}</details>}
</div>
<Button onClick={checkDBPathValidity}>Try Again</Button>
</div>
)}
Expand Down Expand Up @@ -347,43 +358,124 @@ const DatabaseContainer = () => {

return (
<div className="cea-database-editor-database-container">
<Database
name={name}
data={data[category][name]}
schema={schema[category][name]}
/>
{category === 'archetypes' && (
<div style={{ margin: 10 }}>
<Alert
message={
<div>
If you want to add a new <i>archetype</i>, you would need to
include it in all sheets of <b>CONSTRUCTION</b> and create its
schedule in <b>SCHEDULES</b>
</div>
}
type="info"
showIcon
/>
</div>
)}
<div className="cea-database-editor-database">
<h2>{name}</h2>
<ValidationErrors databaseName={name} />
{name === 'SCHEDULES' ? (
<SchedulesDatabase
name={name}
data={data[category][name]}
schema={schema[category][name]}
/>
) : (
<Database
name={name}
data={data[category][name]}
schema={schema[category][name]}
/>
)}
</div>
<SaveDatabaseButton />
</div>
);
};

const Database = ({ name, data, schema }) => {
return (
<div className="cea-database-editor-database">
<h2>{name}</h2>
<ValidationErrors databaseName={name} />
<Tabs className="cea-database-editor-tabs" type="card">
{Object.keys(data).map(sheetName => (
<Tabs.TabPane key={sheetName} tab={sheetName}>
{name !== 'schedules' ? (
<DatabaseTable
databaseName={name}
sheetName={sheetName}
sheetData={data[sheetName]}
schema={schema[sheetName]}
/>
) : (
<SchedulesTable
databaseName={name}
sheetName={sheetName}
sheetData={data[sheetName]}
schema={schema[sheetName]}
/>
)}
<Tabs className="cea-database-editor-tabs" type="card">
{Object.keys(data).map(sheetName => (
<Tabs.TabPane key={sheetName} tab={sheetName}>
<DatabaseTable
databaseName={name}
sheetName={sheetName}
sheetData={data[sheetName]}
schema={schema[sheetName]}
/>
</Tabs.TabPane>
))}
</Tabs>
);
};

const SchedulesDatabase = ({ name, data, schema }) => {
const scheduleNames = Object.keys(data);
const [modalVisible, setModalVisible] = useState(false);
const [selectedKey, setSelected] = useState(scheduleNames[0]);
const [panes, setPanes] = useState(scheduleNames);
const dispatch = useDispatch();

const showModal = () => {
setModalVisible(true);
};

const onEdit = (targetKey, action) => {
if (action === 'remove') {
Modal.confirm({
title: `Do you want to delete ${targetKey} schedule?`,
content: 'This action cannot be undone.',
onOk() {
setSelected(null);
setPanes(oldValue => oldValue.filter(pane => pane != targetKey));
}
});
}
};

const addSchedule = values => {
const { name, copy } = values;
dispatch(copyScheduleData(name, copy));
setPanes(oldValue => [...oldValue, name]);
setSelected(name);
};

return (
<React.Fragment>
<Tabs
className="cea-database-editor-tabs"
type="editable-card"
onEdit={onEdit}
hideAdd
activeKey={selectedKey}
onChange={setSelected}
tabBarExtraContent={
<Button onClick={showModal}>Add new Archetype Schedule</Button>
}
>
{panes.map(pane => (
<Tabs.TabPane key={pane} tab={pane}>
<SchedulesTable
databaseName={name}
sheetName={pane}
sheetData={data[pane]}
schema={schema[pane]}
/>
</Tabs.TabPane>
))}
</Tabs>
</div>
<NewScheduleModal
scheduleNames={scheduleNames}
onSuccess={values => {
addSchedule(values);
}}
visible={modalVisible}
setVisible={setModalVisible}
/>
</React.Fragment>
);
};

Expand Down
84 changes: 84 additions & 0 deletions src/renderer/components/DatabaseEditor/NewScheduleModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React, { useState, useRef } from 'react';
import { Modal, Form, Select } from 'antd';
import { FormItemWrapper } from '../Tools/Parameter';

const NewScheduleModal = ({
scheduleNames,
onSuccess,
visible,
setVisible
}) => {
const formRef = useRef();

const handleOk = () => {
formRef.current.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
onSuccess(values);
setVisible(false);
}
});
};

const handleCancel = e => {
setVisible(false);
};

return (
<Modal
title="Add new Archetype Schedule"
visible={visible}
width={800}
okText="Add"
onOk={handleOk}
onCancel={handleCancel}
destroyOnClose
>
<NewProjectForm ref={formRef} scheduleNames={scheduleNames} />
</Modal>
);
};

const NewProjectForm = Form.create()(({ form, scheduleNames }) => {
return (
<Form layout="horizontal">
<FormItemWrapper
form={form}
name="name"
initialValue=""
help="Name of new Archetype Schedule"
required={true}
rules={[
{
validator: (rule, value, callback) => {
if (scheduleNames.includes(value)) {
callback('Schedule with name already exists');
} else if (value !== value.toUpperCase()) {
callback('Name must be in uppercase');
} else {
callback();
}
}
}
]}
/>
<FormItemWrapper
form={form}
name="copy"
initialValue={scheduleNames[0]}
help="Copy from existing schedule"
inputComponent={
<Select disabled={!scheduleNames.length}>
{scheduleNames.map(name => (
<Select.Option key={name} value={name}>
{name}
</Select.Option>
))}
</Select>
}
/>
</Form>
);
});

export default NewScheduleModal;
2 changes: 1 addition & 1 deletion src/renderer/components/DatabaseEditor/Table.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const useTableUpdateRedux = (tableRef, database, sheet) => {
// ? colHeaders[prop]
// : prop;
// });
dispatch(updateDatabaseChanges(changes));
dispatch(updateDatabaseChanges({ database, sheet, changes }));
}
}
};
Expand Down
17 changes: 16 additions & 1 deletion src/renderer/reducers/databaseEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
FETCH_DATABASE_DATA_FAILURE,
FETCH_DATABASE_SCHEMA_FAILURE,
FETCH_DATABASE_GLOSSARY_FAILURE,
RESET_DATABASE_CHANGES
RESET_DATABASE_CHANGES,
COPY_SCHEDULE_DATA
} from '../actions/databaseEditor';
import { combineReducers } from 'redux';
import { checkNestedProp, createNestedProp, deleteNestedProp } from '../utils';
Expand Down Expand Up @@ -61,6 +62,20 @@ const databaseData = (state = {}, { type, payload }) => {
return payload;
case RESET_DATABASE_STATE:
return {};
// FIXME: Hardcoded location of schedules database
case COPY_SCHEDULE_DATA:
return {
...state,
archetypes: {
...state.archetypes,
schedules: {
...state.archetypes.schedules,
[payload.name]: {
...state.archetypes.schedules[payload.copy]
}
}
}
};
default:
return state;
}
Expand Down