summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/actions/objects.js (renamed from src/actions/object-stores.js)0
-rw-r--r--src/actions/projects.js46
-rw-r--r--src/actions/simulations.js58
-rw-r--r--src/api/sagas/index.js9
-rw-r--r--src/api/sagas/objects.js24
-rw-r--r--src/api/sagas/simulations.js21
-rw-r--r--src/api/sagas/users.js17
-rw-r--r--src/auth/index.js2
-rw-r--r--src/components/modals/Modal.js11
-rw-r--r--src/components/modals/TextInputModal.js8
-rw-r--r--src/components/navigation/Navbar.js2
-rw-r--r--src/components/navigation/Navbar.sass9
-rw-r--r--src/components/projects/FilterPanel.js15
-rw-r--r--src/components/projects/NewProjectButton.js16
-rw-r--r--src/components/projects/NoProjectsAlert.js11
-rw-r--r--src/components/projects/ProjectActionButtons.js26
-rw-r--r--src/components/projects/ProjectAuth.js24
-rw-r--r--src/components/simulations/FilterButton.js (renamed from src/components/projects/FilterButton.js)2
-rw-r--r--src/components/simulations/FilterButton.sass (renamed from src/components/projects/FilterButton.sass)8
-rw-r--r--src/components/simulations/FilterPanel.js15
-rw-r--r--src/components/simulations/FilterPanel.sass (renamed from src/components/projects/FilterPanel.sass)2
-rw-r--r--src/components/simulations/NewSimulationButton.js16
-rw-r--r--src/components/simulations/NewSimulationButton.sass (renamed from src/components/projects/NewProjectButton.sass)6
-rw-r--r--src/components/simulations/NoSimulationsAlert.js11
-rw-r--r--src/components/simulations/NoSimulationsAlert.sass (renamed from src/components/projects/NoProjectsAlert.sass)2
-rw-r--r--src/components/simulations/SimulationActionButtons.js26
-rw-r--r--src/components/simulations/SimulationAuth.js24
-rw-r--r--src/components/simulations/SimulationAuthList.js (renamed from src/components/projects/ProjectAuthList.js)20
-rw-r--r--src/components/simulations/SimulationAuthList.sass (renamed from src/components/projects/ProjectAuthList.sass)16
-rw-r--r--src/containers/projects/ProjectActions.js24
-rw-r--r--src/containers/projects/VisibleProjectAuthList.js32
-rw-r--r--src/containers/simulations/FilterLink.js (renamed from src/containers/projects/FilterLink.js)4
-rw-r--r--src/containers/simulations/NewSimulationModal.js (renamed from src/containers/projects/NewProjectModal.js)18
-rw-r--r--src/containers/simulations/SimulationActions.js24
-rw-r--r--src/containers/simulations/VisibleSimulationAuthList.js32
-rw-r--r--src/pages/Projects.js50
-rw-r--r--src/pages/Projects.sass2
-rw-r--r--src/pages/Simulations.js51
-rw-r--r--src/pages/Simulations.sass2
-rw-r--r--src/reducers/index.js4
-rw-r--r--src/reducers/objects.js2
-rw-r--r--src/reducers/simulations.js (renamed from src/reducers/projects.js)26
-rw-r--r--src/routes/index.js6
43 files changed, 396 insertions, 328 deletions
diff --git a/src/actions/object-stores.js b/src/actions/objects.js
index 08f3f0bd..08f3f0bd 100644
--- a/src/actions/object-stores.js
+++ b/src/actions/objects.js
diff --git a/src/actions/projects.js b/src/actions/projects.js
deleted file mode 100644
index efbd15e9..00000000
--- a/src/actions/projects.js
+++ /dev/null
@@ -1,46 +0,0 @@
-export const SET_AUTH_VISIBILITY_FILTER = "SET_AUTH_VISIBILITY_FILTER";
-export const OPEN_NEW_PROJECT_MODAL = "OPEN_NEW_PROJECT_MODAL";
-export const CLOSE_NEW_PROJECT_MODAL = "CLOSE_PROJECT_POPUP";
-export const ADD_PROJECT = "ADD_PROJECT";
-export const DELETE_PROJECT = "DELETE_PROJECT";
-export const OPEN_PROJECT = "OPEN_PROJECT";
-
-export function setAuthVisibilityFilter(filter) {
- return {
- type: SET_AUTH_VISIBILITY_FILTER,
- filter: filter
- };
-}
-
-export function openNewProjectModal() {
- return {
- type: OPEN_NEW_PROJECT_MODAL
- };
-}
-
-export function closeNewProjectModal() {
- return {
- type: CLOSE_NEW_PROJECT_MODAL
- };
-}
-
-export function addProject(name) {
- return {
- type: ADD_PROJECT,
- name
- };
-}
-
-export function deleteProject(id) {
- return {
- type: DELETE_PROJECT,
- id
- };
-}
-
-export function openProject(id) {
- return {
- type: OPEN_PROJECT,
- id
- };
-}
diff --git a/src/actions/simulations.js b/src/actions/simulations.js
new file mode 100644
index 00000000..314a2492
--- /dev/null
+++ b/src/actions/simulations.js
@@ -0,0 +1,58 @@
+export const SET_AUTH_VISIBILITY_FILTER = "SET_AUTH_VISIBILITY_FILTER";
+export const OPEN_NEW_SIMULATION_MODAL = "OPEN_NEW_SIMULATION_MODAL";
+export const CLOSE_NEW_SIMULATION_MODAL = "CLOSE_SIMULATION_POPUP";
+export const ADD_SIMULATION = "ADD_SIMULATION";
+export const ADD_SIMULATION_SUCCEEDED = "ADD_SIMULATION_SUCCEEDED";
+export const DELETE_SIMULATION = "DELETE_SIMULATION";
+export const OPEN_SIMULATION = "OPEN_SIMULATION";
+
+export function setAuthVisibilityFilter(filter) {
+ return {
+ type: SET_AUTH_VISIBILITY_FILTER,
+ filter: filter
+ };
+}
+
+export function openNewSimulationModal() {
+ return {
+ type: OPEN_NEW_SIMULATION_MODAL
+ };
+}
+
+export function closeNewSimulationModal() {
+ return {
+ type: CLOSE_NEW_SIMULATION_MODAL
+ };
+}
+
+export function addSimulation(name) {
+ return (dispatch, getState) => {
+ const {auth} = getState();
+ dispatch({
+ type: ADD_SIMULATION,
+ name,
+ userId: auth.userId
+ });
+ };
+}
+
+export function addSimulationSucceeded(authorization) {
+ return {
+ type: ADD_SIMULATION_SUCCEEDED,
+ authorization
+ };
+}
+
+export function deleteSimulation(id) {
+ return {
+ type: DELETE_SIMULATION,
+ id
+ };
+}
+
+export function openSimulation(id) {
+ return {
+ type: OPEN_SIMULATION,
+ id
+ };
+}
diff --git a/src/api/sagas/index.js b/src/api/sagas/index.js
index ea92533a..f315f377 100644
--- a/src/api/sagas/index.js
+++ b/src/api/sagas/index.js
@@ -1,9 +1,12 @@
import {takeEvery} from "redux-saga/effects";
import {LOG_IN} from "../../actions/auth";
+import {ADD_SIMULATION} from "../../actions/simulations";
import {FETCH_AUTHORIZATIONS_OF_CURRENT_USER} from "../../actions/users";
-import {fetchAuthorizationsOfCurrentUser, fetchLoggedInUser} from "./users";
+import {onSimulationAdd} from "./simulations";
+import {onFetchAuthorizationsOfCurrentUser, onFetchLoggedInUser} from "./users";
export default function* rootSaga() {
- yield takeEvery(LOG_IN, fetchLoggedInUser);
- yield takeEvery(FETCH_AUTHORIZATIONS_OF_CURRENT_USER, fetchAuthorizationsOfCurrentUser);
+ yield takeEvery(LOG_IN, onFetchLoggedInUser);
+ yield takeEvery(FETCH_AUTHORIZATIONS_OF_CURRENT_USER, onFetchAuthorizationsOfCurrentUser);
+ yield takeEvery(ADD_SIMULATION, onSimulationAdd);
}
diff --git a/src/api/sagas/objects.js b/src/api/sagas/objects.js
new file mode 100644
index 00000000..9bc98e6f
--- /dev/null
+++ b/src/api/sagas/objects.js
@@ -0,0 +1,24 @@
+import {call, put, select} from "redux-saga/effects";
+import {addToSimulationStore, addToUserStore} from "../../actions/objects";
+import {getSimulation} from "../routes/simulations";
+import {getUser} from "../routes/users";
+
+const selectors = {
+ simulation: state => state.objects.simulations,
+ user: state => state.objects.users,
+ authorization: state => state.objects.authorizations,
+};
+
+function* fetchAndStoreObject(objectType, id, apiCall, addToStore) {
+ const objectStore = yield select(selectors[objectType]);
+ if (!objectStore[id]) {
+ const object = yield apiCall;
+ yield put(addToStore(object));
+ }
+}
+
+export const fetchAndStoreSimulation = (id) =>
+ fetchAndStoreObject("simulation", id, call(getSimulation, id), addToSimulationStore);
+
+export const fetchAndStoreUser = (id) =>
+ fetchAndStoreObject("user", id, call(getUser, id), addToUserStore);
diff --git a/src/api/sagas/simulations.js b/src/api/sagas/simulations.js
new file mode 100644
index 00000000..b824d8d5
--- /dev/null
+++ b/src/api/sagas/simulations.js
@@ -0,0 +1,21 @@
+import {call, put} from "redux-saga/effects";
+import {addToAuthorizationStore, addToSimulationStore} from "../../actions/objects";
+import {addSimulationSucceeded} from "../../actions/simulations";
+import {addSimulation} from "../routes/simulations";
+
+export function* onSimulationAdd(action) {
+ try {
+ const simulation = yield call(addSimulation, {name: action.name});
+ yield put(addToSimulationStore(simulation));
+
+ const authorization = {
+ simulationId: simulation.id,
+ userId: action.userId,
+ authorizationLevel: "OWN"
+ };
+ yield put(addToAuthorizationStore(authorization));
+ yield put(addSimulationSucceeded([authorization.userId, authorization.simulationId]));
+ } catch (error) {
+ console.log(error);
+ }
+}
diff --git a/src/api/sagas/users.js b/src/api/sagas/users.js
index b999b693..d3ef32a6 100644
--- a/src/api/sagas/users.js
+++ b/src/api/sagas/users.js
@@ -1,12 +1,12 @@
import {call, put} from "redux-saga/effects";
import {logInSucceeded} from "../../actions/auth";
-import {addToAuthorizationStore, addToSimulationStore, addToUserStore} from "../../actions/object-stores";
+import {addToAuthorizationStore} from "../../actions/objects";
import {fetchAuthorizationsOfCurrentUserSucceeded} from "../../actions/users";
import {performTokenSignIn} from "../routes/auth";
-import {getSimulation} from "../routes/simulations";
-import {addUser, getAuthorizationsByUser, getUser} from "../routes/users";
+import {addUser, getAuthorizationsByUser} from "../routes/users";
+import {fetchAndStoreSimulation, fetchAndStoreUser} from "./objects";
-export function* fetchLoggedInUser(action) {
+export function* onFetchLoggedInUser(action) {
try {
const tokenResponse = yield call(performTokenSignIn, action.payload.authToken);
let userId = tokenResponse.userId;
@@ -22,18 +22,15 @@ export function* fetchLoggedInUser(action) {
}
}
-export function* fetchAuthorizationsOfCurrentUser(action) {
+export function* onFetchAuthorizationsOfCurrentUser(action) {
try {
const authorizations = yield call(getAuthorizationsByUser, action.userId);
for (const authorization of authorizations) {
yield put(addToAuthorizationStore(authorization));
- const simulation = yield call(getSimulation, authorization.simulationId);
- yield put(addToSimulationStore(simulation));
-
- const user = yield call(getUser, authorization.userId);
- yield put(addToUserStore(user));
+ yield fetchAndStoreSimulation(authorization.simulationId);
+ yield fetchAndStoreUser(authorization.userId);
}
const authorizationIds = authorizations.map(authorization => (
diff --git a/src/auth/index.js b/src/auth/index.js
index 1bfc10c1..8950c529 100644
--- a/src/auth/index.js
+++ b/src/auth/index.js
@@ -40,7 +40,7 @@ export const authRedirectMiddleware = store => next => action => {
switch (action.type) {
case LOG_IN_SUCCEEDED:
saveAuthLocalStorage(action.payload);
- window.location.href = "/projects";
+ window.location.href = "/simulations";
break;
case LOG_OUT:
clearAuthLocalStorage();
diff --git a/src/components/modals/Modal.js b/src/components/modals/Modal.js
index c4f10b29..0b3301af 100644
--- a/src/components/modals/Modal.js
+++ b/src/components/modals/Modal.js
@@ -10,8 +10,10 @@ class Modal extends React.Component {
};
static idCounter = 0;
- // Local, up-to-date copy of modal visibility for time between close and props update (to prevent duplicate close
- // triggers)
+ /**
+ * Local, up-to-date copy of modal visibility for time between close event and a props update (to prevent duplicate
+ * close triggers).
+ */
visible = false;
constructor() {
@@ -23,6 +25,11 @@ class Modal extends React.Component {
this.visible = this.props.show;
this.openOrCloseModal();
+ // Trigger auto-focus
+ window["$"]("#" + this.id).on("shown.bs.modal", function () {
+ window["$"](this).find("input").first().focus();
+ });
+
window["$"]("#" + this.id).on("hide.bs.modal", () => {
if (this.visible) {
this.props.onCancel();
diff --git a/src/components/modals/TextInputModal.js b/src/components/modals/TextInputModal.js
index 4acf25b3..90a5db12 100644
--- a/src/components/modals/TextInputModal.js
+++ b/src/components/modals/TextInputModal.js
@@ -27,10 +27,14 @@ class TextInputModal extends React.Component {
show={this.props.show}
onSubmit={this.onSubmit.bind(this)}
onCancel={this.onCancel.bind(this)}>
- <form>
+ <form onSubmit={e => {
+ e.preventDefault();
+ this.onSubmit();
+ }}>
<div className="form-group">
<label className="form-control-label">{this.props.label}:</label>
- <input type="text" className="form-control" ref="textInput" value={this.props.initialValue}/>
+ <input type="text" className="form-control" ref="textInput" value={this.props.initialValue}
+ autoFocus/>
</div>
</form>
</Modal>
diff --git a/src/components/navigation/Navbar.js b/src/components/navigation/Navbar.js
index 96dd93b4..ce020fef 100644
--- a/src/components/navigation/Navbar.js
+++ b/src/components/navigation/Navbar.js
@@ -17,7 +17,7 @@ class Navbar extends Component {
</div>
</Link>
<div className="navigation navbar-button-group">
- <Link className="projects" title="Projects" to="/projects">Projects</Link>
+ <Link className="simulations" title="Simulations" to="/simulations">Simulations</Link>
</div>
<div className="user-controls navbar-button-group">
<Mailto className="support" title="Support" email="opendc.tudelft@gmail.com"
diff --git a/src/components/navigation/Navbar.sass b/src/components/navigation/Navbar.sass
index 8be622db..e9a83301 100644
--- a/src/components/navigation/Navbar.sass
+++ b/src/components/navigation/Navbar.sass
@@ -29,6 +29,9 @@
+transition(background, $transition-length)
+ &:hover
+ color: #fff
+
img
display: inline-block
float: left
@@ -73,16 +76,16 @@
.navigation
margin-left: 30px
- .projects
+ .simulations
float: left
padding: 0 20px
font-size: 12pt
- .projects:hover
+ .simulations:hover
background: #606060
- .projects:active
+ .simulations:active
background: #161616
.user-controls
diff --git a/src/components/projects/FilterPanel.js b/src/components/projects/FilterPanel.js
deleted file mode 100644
index 050bf0aa..00000000
--- a/src/components/projects/FilterPanel.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from 'react';
-import FilterLink from "../../containers/projects/FilterLink";
-import "./FilterPanel.css";
-
-const ProjectFilterPanel = () => (
- <div className="filter-menu">
- <div className="project-filters">
- <FilterLink filter="SHOW_ALL">All Projects</FilterLink>
- <FilterLink filter="SHOW_OWN">My Projects</FilterLink>
- <FilterLink filter="SHOW_SHARED">Projects shared with me</FilterLink>
- </div>
- </div>
-);
-
-export default ProjectFilterPanel;
diff --git a/src/components/projects/NewProjectButton.js b/src/components/projects/NewProjectButton.js
deleted file mode 100644
index 9eaf6df4..00000000
--- a/src/components/projects/NewProjectButton.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import './NewProjectButton.css';
-
-const NewProjectButton = ({onClick}) => (
- <div className="new-project-btn" onClick={onClick}>
- <span className="fa fa-plus"/>
- New Project
- </div>
-);
-
-NewProjectButton.propTypes = {
- onClick: PropTypes.func.isRequired,
-};
-
-export default NewProjectButton;
diff --git a/src/components/projects/NoProjectsAlert.js b/src/components/projects/NoProjectsAlert.js
deleted file mode 100644
index 957435c7..00000000
--- a/src/components/projects/NoProjectsAlert.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from 'react';
-import "./NoProjectsAlert.css";
-
-const NoProjectsAlert = () => (
- <div className="no-projects-alert alert alert-info">
- <span className="info-icon fa fa-2x fa-question-circle"/>
- <strong>No projects here yet...</strong> Add some with the 'New Project' button!
- </div>
-);
-
-export default NoProjectsAlert;
diff --git a/src/components/projects/ProjectActionButtons.js b/src/components/projects/ProjectActionButtons.js
deleted file mode 100644
index 66eb8bfa..00000000
--- a/src/components/projects/ProjectActionButtons.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import PropTypes from "prop-types";
-import React from 'react';
-
-const ProjectActionButtons = ({projectId, onOpen, onViewUsers, onDelete}) => (
- <div className="project-icons">
- <div className="open" title="Open this project" onClick={() => onOpen(projectId)}>
- <span className="fa fa-play"/>
- </div>
- <div className="users" title="View and edit collaborators on this project"
- onClick={() => onViewUsers(projectId)}>
- <span className="fa fa-users"/>
- </div>
- <div className="delete" title="Delete this project" onClick={() => onDelete(projectId)}>
- <span className="fa fa-trash"/>
- </div>
- </div>
-);
-
-ProjectActionButtons.propTypes = {
- projectId: PropTypes.number.isRequired,
- onOpen: PropTypes.func,
- onViewUsers: PropTypes.func,
- onDelete: PropTypes.func,
-};
-
-export default ProjectActionButtons;
diff --git a/src/components/projects/ProjectAuth.js b/src/components/projects/ProjectAuth.js
deleted file mode 100644
index 10cfd252..00000000
--- a/src/components/projects/ProjectAuth.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import classNames from 'classnames';
-import React from 'react';
-import ProjectActions from "../../containers/projects/ProjectActions";
-import Shapes from "../../shapes/index";
-import {AUTH_DESCRIPTION_MAP, AUTH_ICON_MAP} from "../../util/authorizations";
-import {parseAndFormatDateTime} from "../../util/date-time";
-
-const ProjectAuth = ({projectAuth}) => (
- <div className="project-row">
- <div>{projectAuth.simulation.name}</div>
- <div>{parseAndFormatDateTime(projectAuth.simulation.datetimeLastEdited)}</div>
- <div>
- <span className={classNames("fa", "fa-" + AUTH_ICON_MAP[projectAuth.authorizationLevel])}/>
- {AUTH_DESCRIPTION_MAP[projectAuth.authorizationLevel]}
- </div>
- <ProjectActions projectId={projectAuth.simulation.id}/>
- </div>
-);
-
-ProjectAuth.propTypes = {
- projectAuth: Shapes.Authorization.isRequired,
-};
-
-export default ProjectAuth;
diff --git a/src/components/projects/FilterButton.js b/src/components/simulations/FilterButton.js
index 8d6b7146..2105d281 100644
--- a/src/components/projects/FilterButton.js
+++ b/src/components/simulations/FilterButton.js
@@ -4,7 +4,7 @@ import React from 'react';
import "./FilterButton.css";
const FilterButton = ({active, children, onClick}) => (
- <div className={classNames("project-filter-button", {"active": active})}
+ <div className={classNames("simulation-filter-button", {"active": active})}
onClick={() => {
if (!active) {
onClick();
diff --git a/src/components/projects/FilterButton.sass b/src/components/simulations/FilterButton.sass
index 0cad68e3..60f6faca 100644
--- a/src/components/projects/FilterButton.sass
+++ b/src/components/simulations/FilterButton.sass
@@ -1,7 +1,7 @@
@import ../../style-globals/_mixins.sass
@import ../../style-globals/_variables.sass
-.project-filter-button
+.simulation-filter-button
display: inline-block
width: 33.3%
//margin-right: -4px
@@ -13,11 +13,11 @@
+clickable
+transition(background, $transition-length)
-.project-filter-button:last-of-type
+.simulation-filter-button:last-of-type
border: 0
-.project-filter-button:hover
+.simulation-filter-button:hover
background: #0c60bf
-.project-filter-button:active, .project-filter-button.active
+.simulation-filter-button:active, .simulation-filter-button.active
background: #073d7d
diff --git a/src/components/simulations/FilterPanel.js b/src/components/simulations/FilterPanel.js
new file mode 100644
index 00000000..b43139c1
--- /dev/null
+++ b/src/components/simulations/FilterPanel.js
@@ -0,0 +1,15 @@
+import React from 'react';
+import FilterLink from "../../containers/simulations/FilterLink";
+import "./FilterPanel.css";
+
+const FilterPanel = () => (
+ <div className="filter-menu">
+ <div className="simulation-filters">
+ <FilterLink filter="SHOW_ALL">All Simulations</FilterLink>
+ <FilterLink filter="SHOW_OWN">My Simulations</FilterLink>
+ <FilterLink filter="SHOW_SHARED">Simulations shared with me</FilterLink>
+ </div>
+ </div>
+);
+
+export default FilterPanel;
diff --git a/src/components/projects/FilterPanel.sass b/src/components/simulations/FilterPanel.sass
index a70c7a90..a59ffdfd 100644
--- a/src/components/projects/FilterPanel.sass
+++ b/src/components/simulations/FilterPanel.sass
@@ -15,7 +15,7 @@
margin-bottom: 20px
- .project-filters
+ .simulation-filters
display: block
overflow: hidden
margin: 0 -1px
diff --git a/src/components/simulations/NewSimulationButton.js b/src/components/simulations/NewSimulationButton.js
new file mode 100644
index 00000000..468f7678
--- /dev/null
+++ b/src/components/simulations/NewSimulationButton.js
@@ -0,0 +1,16 @@
+import PropTypes from 'prop-types';
+import React from 'react';
+import './NewSimulationButton.css';
+
+const NewSimulationButton = ({onClick}) => (
+ <div className="new-simulation-btn" onClick={onClick}>
+ <span className="fa fa-plus"/>
+ New Simulation
+ </div>
+);
+
+NewSimulationButton.propTypes = {
+ onClick: PropTypes.func.isRequired,
+};
+
+export default NewSimulationButton;
diff --git a/src/components/projects/NewProjectButton.sass b/src/components/simulations/NewSimulationButton.sass
index 89435902..9bf82c49 100644
--- a/src/components/projects/NewProjectButton.sass
+++ b/src/components/simulations/NewSimulationButton.sass
@@ -1,7 +1,7 @@
@import ../../style-globals/_mixins.sass
@import ../../style-globals/_variables.sass
-.new-project-btn
+.new-simulation-btn
$button-height: 35px
display: inline-block
@@ -24,8 +24,8 @@
span
margin-right: 10px
-.new-project-btn:hover
+.new-simulation-btn:hover
background: #73ac45
-.new-project-btn:active
+.new-simulation-btn:active
background: #5c8835
diff --git a/src/components/simulations/NoSimulationsAlert.js b/src/components/simulations/NoSimulationsAlert.js
new file mode 100644
index 00000000..d688ae56
--- /dev/null
+++ b/src/components/simulations/NoSimulationsAlert.js
@@ -0,0 +1,11 @@
+import React from 'react';
+import "./NoSimulationsAlert.css";
+
+const NoSimulationsAlert = () => (
+ <div className="no-simulations-alert alert alert-info">
+ <span className="info-icon fa fa-2x fa-question-circle"/>
+ <strong>No simulations here yet...</strong> Add some with the 'New Simulation' button!
+ </div>
+);
+
+export default NoSimulationsAlert;
diff --git a/src/components/projects/NoProjectsAlert.sass b/src/components/simulations/NoSimulationsAlert.sass
index a526f9ad..10e89e51 100644
--- a/src/components/projects/NoProjectsAlert.sass
+++ b/src/components/simulations/NoSimulationsAlert.sass
@@ -1,4 +1,4 @@
-.no-projects-alert
+.no-simulations-alert
position: relative
padding-left: 50px
diff --git a/src/components/simulations/SimulationActionButtons.js b/src/components/simulations/SimulationActionButtons.js
new file mode 100644
index 00000000..d48b4bcf
--- /dev/null
+++ b/src/components/simulations/SimulationActionButtons.js
@@ -0,0 +1,26 @@
+import PropTypes from "prop-types";
+import React from 'react';
+
+const SimulationActionButtons = ({simulationId, onOpen, onViewUsers, onDelete}) => (
+ <div className="simulation-icons">
+ <div className="open" title="Open this simulation" onClick={() => onOpen(simulationId)}>
+ <span className="fa fa-play"/>
+ </div>
+ <div className="users" title="View and edit collaborators on this simulation"
+ onClick={() => onViewUsers(simulationId)}>
+ <span className="fa fa-users"/>
+ </div>
+ <div className="delete" title="Delete this simulation" onClick={() => onDelete(simulationId)}>
+ <span className="fa fa-trash"/>
+ </div>
+ </div>
+);
+
+SimulationActionButtons.propTypes = {
+ simulationId: PropTypes.number.isRequired,
+ onOpen: PropTypes.func,
+ onViewUsers: PropTypes.func,
+ onDelete: PropTypes.func,
+};
+
+export default SimulationActionButtons;
diff --git a/src/components/simulations/SimulationAuth.js b/src/components/simulations/SimulationAuth.js
new file mode 100644
index 00000000..9b887991
--- /dev/null
+++ b/src/components/simulations/SimulationAuth.js
@@ -0,0 +1,24 @@
+import classNames from 'classnames';
+import React from 'react';
+import SimulationActions from "../../containers/simulations/SimulationActions";
+import Shapes from "../../shapes/index";
+import {AUTH_DESCRIPTION_MAP, AUTH_ICON_MAP} from "../../util/authorizations";
+import {parseAndFormatDateTime} from "../../util/date-time";
+
+const SimulationAuth = ({simulationAuth}) => (
+ <div className="simulation-row">
+ <div>{simulationAuth.simulation.name}</div>
+ <div>{parseAndFormatDateTime(simulationAuth.simulation.datetimeLastEdited)}</div>
+ <div>
+ <span className={classNames("fa", "fa-" + AUTH_ICON_MAP[simulationAuth.authorizationLevel])}/>
+ {AUTH_DESCRIPTION_MAP[simulationAuth.authorizationLevel]}
+ </div>
+ <SimulationActions simulationId={simulationAuth.simulation.id}/>
+ </div>
+);
+
+SimulationAuth.propTypes = {
+ simulationAuth: Shapes.Authorization.isRequired,
+};
+
+export default SimulationAuth;
diff --git a/src/components/projects/ProjectAuthList.js b/src/components/simulations/SimulationAuthList.js
index 94e7abad..7653f178 100644
--- a/src/components/projects/ProjectAuthList.js
+++ b/src/components/simulations/SimulationAuthList.js
@@ -1,33 +1,33 @@
import PropTypes from 'prop-types';
import React from 'react';
import Shapes from "../../shapes/index";
-import NoProjectsAlert from "./NoProjectsAlert";
-import ProjectAuth from "./ProjectAuth";
-import "./ProjectAuthList.css";
+import NoSimulationsAlert from "./NoSimulationsAlert";
+import SimulationAuth from "./SimulationAuth";
+import "./SimulationAuthList.css";
-const ProjectAuthList = ({authorizations}) => {
+const SimulationAuthList = ({authorizations}) => {
if (authorizations.length === 0) {
- return <NoProjectsAlert/>;
+ return <NoSimulationsAlert/>;
}
return (
- <div className="project-list">
+ <div className="simulation-list">
<div className="list-head">
- <div>Project name</div>
+ <div>Simulation name</div>
<div>Last edited</div>
<div>Access rights</div>
</div>
<div className="list-body">
{authorizations.map(authorization => (
- <ProjectAuth projectAuth={authorization} key={authorization.simulation.id}/>
+ <SimulationAuth simulationAuth={authorization} key={authorization.simulation.id}/>
))}
</div>
</div>
);
};
-ProjectAuthList.propTypes = {
+SimulationAuthList.propTypes = {
authorizations: PropTypes.arrayOf(Shapes.Authorization).isRequired,
};
-export default ProjectAuthList;
+export default SimulationAuthList;
diff --git a/src/components/projects/ProjectAuthList.sass b/src/components/simulations/SimulationAuthList.sass
index 5cdfacaa..4edfb99e 100644
--- a/src/components/projects/ProjectAuthList.sass
+++ b/src/components/simulations/SimulationAuthList.sass
@@ -1,16 +1,16 @@
@import ../../style-globals/_mixins.sass
@import ../../style-globals/_variables.sass
-.project-list
+.simulation-list
display: block
font-size: 12pt
border: 0
- .list-head, .list-body .project-row
+ .list-head, .list-body .simulation-row
display: block
position: relative
- .list-head div, .list-body .project-row div
+ .list-head div, .list-body .simulation-row div
padding: 0 10px
display: inline-block
@@ -21,18 +21,18 @@
div
margin-right: -4px
-.project-row
+.simulation-row
background: #f8f8f8
border: 1px solid #b6b6b6
height: 40px
line-height: 40px
clear: both
-.project-row:not(:first-of-type)
+.simulation-row:not(:first-of-type)
margin-top: -1px
// Sizing of table columns
-.project-row, .project-list .list-head
+.simulation-row, .simulation-list .list-head
div:first-of-type
width: 40%
@@ -48,10 +48,10 @@
span
margin-right: 10px
-.project-row .project-icons
+.simulation-row .simulation-icons
text-align: right
-.project-row .project-icons div
+.simulation-row .simulation-icons div
display: inline
position: relative
top: 4px
diff --git a/src/containers/projects/ProjectActions.js b/src/containers/projects/ProjectActions.js
deleted file mode 100644
index 098d9966..00000000
--- a/src/containers/projects/ProjectActions.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import {connect} from "react-redux";
-import {deleteProject, openProject} from "../../actions/projects";
-import ProjectActionButtons from "../../components/projects/ProjectActionButtons";
-
-const mapStateToProps = (state, ownProps) => {
- return {
- projectId: ownProps.projectId
- };
-};
-
-const mapDispatchToProps = dispatch => {
- return {
- onOpen: (id) => dispatch(openProject(id)),
- onViewUsers: (id) => {},
- onDelete: (id) => dispatch(deleteProject(id)),
- };
-};
-
-const ProjectActions = connect(
- mapStateToProps,
- mapDispatchToProps
-)(ProjectActionButtons);
-
-export default ProjectActions;
diff --git a/src/containers/projects/VisibleProjectAuthList.js b/src/containers/projects/VisibleProjectAuthList.js
deleted file mode 100644
index f668711e..00000000
--- a/src/containers/projects/VisibleProjectAuthList.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import {connect} from "react-redux";
-import ProjectList from "../../components/projects/ProjectAuthList";
-
-const getVisibleProjectAuths = (projectAuths, filter) => {
- switch (filter) {
- case 'SHOW_ALL':
- return projectAuths;
- case 'SHOW_OWN':
- return projectAuths.filter(projectAuth => projectAuth.authorizationLevel === "OWN");
- case 'SHOW_SHARED':
- return projectAuths.filter(projectAuth => projectAuth.authorizationLevel !== "OWN");
- default:
- return projectAuths;
- }
-};
-
-const mapStateToProps = state => {
- const denormalizedAuthorizations = state.authorizationsOfCurrentUser.map(authorizationIds => {
- const authorization = Object.assign({}, state.objects.authorizations[authorizationIds]);
- authorization.simulation = state.objects.simulations[authorization.simulationId];
- authorization.user = state.objects.users[authorization.userId];
- return authorization;
- });
-
- return {
- authorizations: getVisibleProjectAuths(denormalizedAuthorizations, state.authVisibilityFilter)
- };
-};
-
-const VisibleProjectAuthList = connect(mapStateToProps)(ProjectList);
-
-export default VisibleProjectAuthList;
diff --git a/src/containers/projects/FilterLink.js b/src/containers/simulations/FilterLink.js
index e9a13436..dff01ab2 100644
--- a/src/containers/projects/FilterLink.js
+++ b/src/containers/simulations/FilterLink.js
@@ -1,6 +1,6 @@
import {connect} from "react-redux";
-import {setAuthVisibilityFilter} from "../../actions/projects";
-import FilterButton from "../../components/projects/FilterButton";
+import {setAuthVisibilityFilter} from "../../actions/simulations";
+import FilterButton from "../../components/simulations/FilterButton";
const mapStateToProps = (state, ownProps) => {
return {
diff --git a/src/containers/projects/NewProjectModal.js b/src/containers/simulations/NewSimulationModal.js
index affaf794..a4a3d2a8 100644
--- a/src/containers/projects/NewProjectModal.js
+++ b/src/containers/simulations/NewSimulationModal.js
@@ -1,17 +1,17 @@
import React from "react";
import {connect} from "react-redux";
-import {addProject, closeNewProjectModal} from "../../actions/projects";
+import {addSimulation, closeNewSimulationModal} from "../../actions/simulations";
import TextInputModal from "../../components/modals/TextInputModal";
-const NewProjectModalComponent = ({visible, callback}) => (
- <TextInputModal title="New Project" label="Project title"
+const NewSimulationModalComponent = ({visible, callback}) => (
+ <TextInputModal title="New Simulation" label="Simulation title"
show={visible}
callback={callback}/>
);
const mapStateToProps = state => {
return {
- visible: state.newProjectModalVisible
+ visible: state.newSimulationModalVisible
};
};
@@ -19,16 +19,16 @@ const mapDispatchToProps = dispatch => {
return {
callback: (text) => {
if (text) {
- dispatch(addProject(text));
+ dispatch(addSimulation(text));
}
- dispatch(closeNewProjectModal());
+ dispatch(closeNewSimulationModal());
}
};
};
-const NewProjectModal = connect(
+const NewSimulationModal = connect(
mapStateToProps,
mapDispatchToProps
-)(NewProjectModalComponent);
+)(NewSimulationModalComponent);
-export default NewProjectModal;
+export default NewSimulationModal;
diff --git a/src/containers/simulations/SimulationActions.js b/src/containers/simulations/SimulationActions.js
new file mode 100644
index 00000000..e2ca2795
--- /dev/null
+++ b/src/containers/simulations/SimulationActions.js
@@ -0,0 +1,24 @@
+import {connect} from "react-redux";
+import {deleteSimulation, openSimulation} from "../../actions/simulations";
+import SimulationActionButtons from "../../components/simulations/SimulationActionButtons";
+
+const mapStateToProps = (state, ownProps) => {
+ return {
+ simulationId: ownProps.simulationId
+ };
+};
+
+const mapDispatchToProps = dispatch => {
+ return {
+ onOpen: (id) => dispatch(openSimulation(id)),
+ onViewUsers: (id) => {},
+ onDelete: (id) => dispatch(deleteSimulation(id)),
+ };
+};
+
+const SimulationActions = connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(SimulationActionButtons);
+
+export default SimulationActions;
diff --git a/src/containers/simulations/VisibleSimulationAuthList.js b/src/containers/simulations/VisibleSimulationAuthList.js
new file mode 100644
index 00000000..07740435
--- /dev/null
+++ b/src/containers/simulations/VisibleSimulationAuthList.js
@@ -0,0 +1,32 @@
+import {connect} from "react-redux";
+import SimulationList from "../../components/simulations/SimulationAuthList";
+
+const getVisibleSimulationAuths = (simulationAuths, filter) => {
+ switch (filter) {
+ case 'SHOW_ALL':
+ return simulationAuths;
+ case 'SHOW_OWN':
+ return simulationAuths.filter(simulationAuth => simulationAuth.authorizationLevel === "OWN");
+ case 'SHOW_SHARED':
+ return simulationAuths.filter(simulationAuth => simulationAuth.authorizationLevel !== "OWN");
+ default:
+ return simulationAuths;
+ }
+};
+
+const mapStateToProps = state => {
+ const denormalizedAuthorizations = state.authorizationsOfCurrentUser.map(authorizationIds => {
+ const authorization = Object.assign({}, state.objects.authorizations[authorizationIds]);
+ authorization.simulation = state.objects.simulations[authorization.simulationId];
+ authorization.user = state.objects.users[authorization.userId];
+ return authorization;
+ });
+
+ return {
+ authorizations: getVisibleSimulationAuths(denormalizedAuthorizations, state.authVisibilityFilter)
+ };
+};
+
+const VisibleSimulationAuthList = connect(mapStateToProps)(SimulationList);
+
+export default VisibleSimulationAuthList;
diff --git a/src/pages/Projects.js b/src/pages/Projects.js
deleted file mode 100644
index 06655768..00000000
--- a/src/pages/Projects.js
+++ /dev/null
@@ -1,50 +0,0 @@
-import React from 'react';
-import {connect} from "react-redux";
-import {addProject, openNewProjectModal} from "../actions/projects";
-import {fetchAuthorizationsOfCurrentUser} from "../actions/users";
-import Navbar from "../components/navigation/Navbar";
-import ProjectFilterPanel from "../components/projects/FilterPanel";
-import NewProjectButton from "../components/projects/NewProjectButton";
-import Login from "../containers/auth/Login";
-import NewProjectModal from "../containers/projects/NewProjectModal";
-import VisibleProjectList from "../containers/projects/VisibleProjectAuthList";
-import "./Projects.css";
-
-class ProjectsContainer extends React.Component {
- componentDidMount() {
- this.props.fetchAuthorizationsOfCurrentUser();
- }
-
- onInputSubmission(text) {
- this.props.dispatch(addProject(text));
- }
-
- render() {
- return (
- <div className="full-height">
- <Navbar/>
- <div className="container project-page-container full-height">
- <ProjectFilterPanel/>
- <VisibleProjectList/>
- <NewProjectButton onClick={() => {this.props.openNewProjectModal()}}/>
- </div>
- <NewProjectModal/>
- <Login visible={false}/>
- </div>
- );
- }
-}
-
-const mapDispatchToProps = dispatch => {
- return {
- fetchAuthorizationsOfCurrentUser: () => dispatch(fetchAuthorizationsOfCurrentUser()),
- openNewProjectModal: () => dispatch(openNewProjectModal()),
- };
-};
-
-const Projects = connect(
- undefined,
- mapDispatchToProps
-)(ProjectsContainer);
-
-export default Projects;
diff --git a/src/pages/Projects.sass b/src/pages/Projects.sass
deleted file mode 100644
index 11a52e1a..00000000
--- a/src/pages/Projects.sass
+++ /dev/null
@@ -1,2 +0,0 @@
-.project-page-container
- padding-top: 2rem
diff --git a/src/pages/Simulations.js b/src/pages/Simulations.js
new file mode 100644
index 00000000..c46cb621
--- /dev/null
+++ b/src/pages/Simulations.js
@@ -0,0 +1,51 @@
+import React from 'react';
+import {connect} from "react-redux";
+import {addSimulation, openNewSimulationModal} from "../actions/simulations";
+import {fetchAuthorizationsOfCurrentUser} from "../actions/users";
+import Navbar from "../components/navigation/Navbar";
+import SimulationFilterPanel from "../components/simulations/FilterPanel";
+import NewSimulationButton from "../components/simulations/NewSimulationButton";
+import Login from "../containers/auth/Login";
+import NewSimulationModal from "../containers/simulations/NewSimulationModal";
+import VisibleSimulationList from "../containers/simulations/VisibleSimulationAuthList";
+import "./Simulations.css";
+
+class SimulationsContainer extends React.Component {
+ componentDidMount() {
+ this.props.fetchAuthorizationsOfCurrentUser();
+ }
+
+ onInputSubmission(text) {
+ this.props.addSimulation(text);
+ }
+
+ render() {
+ return (
+ <div className="full-height">
+ <Navbar/>
+ <div className="container simulation-page-container full-height">
+ <SimulationFilterPanel/>
+ <VisibleSimulationList/>
+ <NewSimulationButton onClick={() => {this.props.openNewSimulationModal()}}/>
+ </div>
+ <NewSimulationModal/>
+ <Login visible={false}/>
+ </div>
+ );
+ }
+}
+
+const mapDispatchToProps = dispatch => {
+ return {
+ fetchAuthorizationsOfCurrentUser: () => dispatch(fetchAuthorizationsOfCurrentUser()),
+ openNewSimulationModal: () => dispatch(openNewSimulationModal()),
+ addSimulation: (text) => dispatch(addSimulation(text)),
+ };
+};
+
+const Simulations = connect(
+ undefined,
+ mapDispatchToProps
+)(SimulationsContainer);
+
+export default Simulations;
diff --git a/src/pages/Simulations.sass b/src/pages/Simulations.sass
new file mode 100644
index 00000000..d639ba83
--- /dev/null
+++ b/src/pages/Simulations.sass
@@ -0,0 +1,2 @@
+.simulation-page-container
+ padding-top: 2rem
diff --git a/src/reducers/index.js b/src/reducers/index.js
index 71379a6f..bfafaedd 100644
--- a/src/reducers/index.js
+++ b/src/reducers/index.js
@@ -1,13 +1,13 @@
import {combineReducers} from "redux";
import {auth} from "./auth";
import {objects} from "./objects";
-import {authorizationsOfCurrentUser, authVisibilityFilter, newProjectModalVisible} from "./projects";
+import {authorizationsOfCurrentUser, authVisibilityFilter, newSimulationModalVisible} from "./simulations";
const rootReducer = combineReducers({
auth,
objects,
authorizationsOfCurrentUser,
- newProjectModalVisible,
+ newSimulationModalVisible,
authVisibilityFilter,
});
diff --git a/src/reducers/objects.js b/src/reducers/objects.js
index 69e68ca7..40bd3524 100644
--- a/src/reducers/objects.js
+++ b/src/reducers/objects.js
@@ -1,5 +1,5 @@
import {combineReducers} from "redux";
-import {ADD_TO_AUTHORIZATION_STORE, ADD_TO_SIMULATION_STORE, ADD_TO_USER_STORE} from "../actions/object-stores";
+import {ADD_TO_AUTHORIZATION_STORE, ADD_TO_SIMULATION_STORE, ADD_TO_USER_STORE} from "../actions/objects";
export const objects = combineReducers({
simulations,
diff --git a/src/reducers/projects.js b/src/reducers/simulations.js
index ba3c792d..5a34ee7f 100644
--- a/src/reducers/projects.js
+++ b/src/reducers/simulations.js
@@ -1,37 +1,33 @@
import {
- ADD_PROJECT,
- CLOSE_NEW_PROJECT_MODAL,
- DELETE_PROJECT,
- OPEN_NEW_PROJECT_MODAL,
+ ADD_SIMULATION_SUCCEEDED,
+ CLOSE_NEW_SIMULATION_MODAL,
+ DELETE_SIMULATION,
+ OPEN_NEW_SIMULATION_MODAL,
SET_AUTH_VISIBILITY_FILTER
-} from "../actions/projects";
+} from "../actions/simulations";
import {FETCH_AUTHORIZATIONS_OF_CURRENT_USER_SUCCEEDED} from "../actions/users";
export function authorizationsOfCurrentUser(state = [], action) {
switch (action.type) {
case FETCH_AUTHORIZATIONS_OF_CURRENT_USER_SUCCEEDED:
return action.authorizationsOfCurrentUser;
- case ADD_PROJECT:
+ case ADD_SIMULATION_SUCCEEDED:
return [
...state,
- {
- userId: -1,
- simulation: {name: action.name, datetimeLastEdited: "2017-08-06T12:43:00", id: state.length},
- authorizationLevel: "OWN"
- }
+ action.authorization
];
- case DELETE_PROJECT:
+ case DELETE_SIMULATION:
return [];
default:
return state;
}
}
-export function newProjectModalVisible(state = false, action) {
+export function newSimulationModalVisible(state = false, action) {
switch (action.type) {
- case OPEN_NEW_PROJECT_MODAL:
+ case OPEN_NEW_SIMULATION_MODAL:
return true;
- case CLOSE_NEW_PROJECT_MODAL:
+ case CLOSE_NEW_SIMULATION_MODAL:
return false;
default:
return state;
diff --git a/src/routes/index.js b/src/routes/index.js
index 6b3a454d..6257017e 100644
--- a/src/routes/index.js
+++ b/src/routes/index.js
@@ -3,15 +3,15 @@ import {BrowserRouter, Redirect, Route, Switch} from "react-router-dom";
import {userIsLoggedIn} from "../auth/index";
import Home from "../pages/Home";
import NotFound from "../pages/NotFound";
-import Projects from "../pages/Projects";
+import Simulations from "../pages/Simulations";
const Routes = () => (
<BrowserRouter>
<Switch>
<Route exact path="/" component={Home}/>
- <Route exact path="/projects" render={() => (
+ <Route exact path="/simulations" render={() => (
userIsLoggedIn() ? (
- <Projects/>
+ <Simulations/>
) : (
<Redirect to="/"/>
)