front/src/routes/dashboard/edit-dashboard/index.js
import { Component } from 'preact';
import { connect } from 'unistore/preact';
import { route } from 'preact-router';
import update from 'immutability-helper';
import EditDashboardPage from './EditDashboard';
import get from 'get-value';
class EditDashboard extends Component {
getDashboards = async () => {
try {
await this.setState({
getDashboardsError: false,
loading: true
});
const dashboards = await this.props.httpClient.get('/api/v1/dashboard');
let currentDashboardSelector;
if (this.props.dashboardSelector) {
currentDashboardSelector = this.props.dashboardSelector;
} else if (dashboards.length > 0) {
currentDashboardSelector = dashboards[0].selector;
}
await this.setState({
dashboards,
currentDashboardSelector,
getDashboardsError: false,
loading: false
});
} catch (e) {
console.error(e);
this.setState({ loading: false });
const status = get(e, 'response.status');
const errorMessage = get(e, 'response.error_message');
// in case we are on the gateway (Gladys Plus)
if (status === 404 && errorMessage === 'NO_INSTANCE_FOUND') {
this.setState({
gatewayInstanceNotFound: true
});
} else {
this.setState({
getDashboardsError: true
});
}
}
};
getCurrentDashboard = async () => {
try {
await this.setState({ loading: true });
const currentDashboard = await this.props.httpClient.get(
`/api/v1/dashboard/${this.state.currentDashboardSelector}`
);
this.setState({
currentDashboard,
loading: false
});
} catch (e) {
this.setState({
loading: false
});
console.error(e);
}
};
init = async () => {
await this.getDashboards();
if (this.state.currentDashboardSelector) {
await this.getCurrentDashboard();
}
};
cancelDashboardEdit = async () => {
route(`/dashboard/${this.state.currentDashboardSelector}`);
};
moveCard = async (originalX, originalY, destX, destY) => {
// incorrect coordinates
if (destX < 0 || destY < 0) {
return null;
}
if (destX >= this.state.currentDashboard.boxes.length || destY > this.state.currentDashboard.boxes[destX].length) {
return null;
}
const element = this.state.currentDashboard.boxes[originalX][originalY];
const newStateWithoutElement = update(this.state, {
currentDashboard: {
boxes: {
[originalX]: {
$splice: [[originalY, 1]]
}
}
}
});
const newState = update(newStateWithoutElement, {
currentDashboard: {
boxes: {
[destX]: {
$splice: [[destY, 0, element]]
}
}
}
});
await this.setState(newState);
};
addBox = x => {
const newState = update(this.state, {
currentDashboard: {
boxes: {
[x]: {
$push: [{}]
}
}
}
});
this.setState(newState);
};
removeBox = async (x, y) => {
const newState = update(this.state, {
currentDashboard: {
boxes: {
[x]: {
$splice: [[y, 1]]
}
}
}
});
await this.setState(newState);
};
updateCurrentDashboardName = e => {
const newState = update(this.state, {
currentDashboard: {
name: {
$set: e.target.value
}
}
});
this.setState(newState);
};
updateBoxConfig = (x, y, data) => {
const newState = update(this.state, {
currentDashboard: {
boxes: {
[x]: {
[y]: {
$merge: data
}
}
}
}
});
this.setState(newState);
};
updateNewSelectedBox = (x, y, type) => {
const newState = update(this.state, {
currentDashboard: {
boxes: {
[x]: {
[y]: {
type: {
$set: type
}
}
}
}
}
});
this.setState(newState);
};
removeEmptyBoxes = async () => {
const { currentDashboard } = this.state;
// new boxes without empty boxes
const newBoxes = currentDashboard.boxes.map(column => {
return column.filter(box => {
return box.type !== undefined;
});
});
const newDashboard = update(currentDashboard, {
boxes: {
$set: newBoxes
}
});
await this.setState({
currentDashboard: newDashboard
});
};
saveDashboard = async () => {
this.setState({
loading: true,
dashboardValidationError: false,
dashboardAlreadyExistError: false,
unknownError: false
});
try {
// We purge all empty boxes
await this.removeEmptyBoxes();
const { currentDashboard: selectedDashboard, dashboards } = this.state;
const { selector } = selectedDashboard;
const currentDashboard = await this.props.httpClient.patch(
`/api/v1/dashboard/${selector}`,
this.state.currentDashboard
);
const currentDashboardIndex = dashboards.findIndex(d => d.selector === selector);
const updatedDashboards = update(dashboards, {
[currentDashboardIndex]: {
$set: currentDashboard
}
});
await this.setState({
currentDashboard,
loading: false,
dashboards: updatedDashboards
});
route(`/dashboard/${currentDashboard.selector}`);
} catch (e) {
console.error(e);
if (e.response && e.response.status === 422) {
this.setState({
dashboardValidationError: true
});
} else if (e.response && e.response.status === 409) {
this.setState({
dashboardAlreadyExistError: true
});
} else {
this.setState({
unknownError: true
});
}
}
};
askDeleteCurrentDashboard = async () => {
await this.setState({
askDeleteDashboard: true
});
};
cancelDeleteCurrentDashboard = async () => {
await this.setState({
askDeleteDashboard: false
});
};
deleteCurrentDashboard = async () => {
try {
await this.props.httpClient.delete(`/api/v1/dashboard/${this.state.currentDashboard.selector}`);
const dashboardIndex = this.state.dashboards.findIndex(d => d.id === this.state.currentDashboard.id);
const dashboards = update(this.state.dashboards, {
$splice: [[dashboardIndex, 1]]
});
const currentDashboard = dashboards.length > 0 ? dashboards[0] : null;
await this.setState({
askDeleteDashboard: false
});
if (currentDashboard === null) {
route('/dashboard');
} else {
route(`/dashboard/${currentDashboard.selector}/edit`);
}
} catch (e) {
console.error(e);
}
};
updateDashboardList = async newDashboards => {
await this.setState({
savingNewDashboardList: true,
dashboards: newDashboards
});
try {
const dashboardSelectors = this.state.dashboards.map(d => d.selector);
await this.props.httpClient.post('/api/v1/dashboard/order', dashboardSelectors);
} catch (e) {
console.error(e);
}
this.setState({
savingNewDashboardList: false
});
};
toggleMobileReorder = () => {
this.setState(prevState => ({ ...prevState, isMobileReordering: !prevState.isMobileReordering }));
};
constructor(props) {
super(props);
this.props = props;
this.isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
this.state = {
dashboards: [],
newSelectedBoxType: {},
askDeleteDashboard: false,
isMobileReordering: false
};
}
componentDidMount() {
this.init();
}
componentDidUpdate(prevProps) {
if (prevProps.currentUrl !== this.props.currentUrl) {
this.init();
}
}
render(
props,
{
dashboards,
currentDashboard,
loading,
dashboardValidationError,
dashboardAlreadyExistError,
unknownError,
askDeleteDashboard,
savingNewDashboardList,
isMobileReordering
}
) {
return (
<EditDashboardPage
isTouchDevice={this.isTouchDevice}
dashboards={dashboards}
currentDashboard={currentDashboard}
loading={loading}
dashboardValidationError={dashboardValidationError}
dashboardAlreadyExistError={dashboardAlreadyExistError}
unknownError={unknownError}
toggleDashboardDropdown={this.toggleDashboardDropdown}
redirectToDashboard={this.redirectToDashboard}
editDashboard={this.editDashboard}
cancelDashboardEdit={this.cancelDashboardEdit}
moveBoxDown={this.moveBoxDown}
moveBoxUp={this.moveBoxUp}
moveCard={this.moveCard}
addBox={this.addBox}
removeBox={this.removeBox}
updateNewSelectedBox={this.updateNewSelectedBox}
saveDashboard={this.saveDashboard}
updateBoxConfig={this.updateBoxConfig}
updateCurrentDashboardName={this.updateCurrentDashboardName}
askDeleteCurrentDashboard={this.askDeleteCurrentDashboard}
cancelDeleteCurrentDashboard={this.cancelDeleteCurrentDashboard}
deleteCurrentDashboard={this.deleteCurrentDashboard}
askDeleteDashboard={askDeleteDashboard}
updateDashboardList={this.updateDashboardList}
savingNewDashboardList={savingNewDashboardList}
toggleMobileReorder={this.toggleMobileReorder}
isMobileReordering={isMobileReordering}
/>
);
}
}
export default connect('user,fullScreen,currentUrl,httpClient,gatewayAccountExpired', {})(EditDashboard);