Skip to content
Draft
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
11 changes: 10 additions & 1 deletion src/api/reloadConfiguration.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { makeRequest, METHODS } from '@entando/apimanager';
import { SUCCESS } from 'test/mocks/reloadConfiguration';
// eslint-disable-next-line import/no-unresolved, import/extensions
import { SUCCESS, STATUS_RESPONSE } from 'test/mocks/reloadConfiguration';

export const reloadConf = () => (
makeRequest({
Expand All @@ -11,5 +12,13 @@ export const reloadConf = () => (
})
);

export const getReloadStatus = () => (
makeRequest({
uri: '/api/reloadConfiguration/status',
method: METHODS.GET,
mockResponse: STATUS_RESPONSE,
useAuthentication: true,
})
);

export default reloadConf;
10 changes: 9 additions & 1 deletion src/locales/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -732,8 +732,16 @@ export default {
'reloadConfiguration.help': 'The RELOAD CONFIGURATION section allows users to reload the system configuration. This operation is necessary after modifying some parameters.',
'reloadConfiguration.reload.title': 'Reload the configuration',
'reloadConfiguration.reload.confirm': 'Are you sure you want to reload the configuration?',
'reloadConfiguration.confirm.success': 'The configuration has been reloaded.',
'reloadConfiguration.confirm.success': 'The configuration has been reloaded successfully.',
'reloadConfiguration.confirm.error': 'Something went wrong while reloading the configuration. Try again in a minute.',
'reloadConfiguration.confirm.progress': 'Reloading configuration in progress...',
'reloadConfiguration.confirm.pleaseWait': 'Please wait while the system configuration is being reloaded. This page will update automatically.',
'reloadConfiguration.confirm.waiting': 'Configuration reload completed with warnings. Some components could not be reloaded properly.',
'reloadConfiguration.confirm.fail': 'Configuration reload failed. Please check the system logs for more details.',
'reloadConfiguration.table.beanId': 'Component',
'reloadConfiguration.table.status': 'Status',
'reloadConfiguration.bean.status.ok': 'OK',
'reloadConfiguration.bean.status.ko': 'Error',
'activityStream.newPage': 'created a new page',
'activityStream.editPage': 'edited a new page',
'activityStream.deletePage': 'delete a page',
Expand Down
10 changes: 9 additions & 1 deletion src/locales/it.js
Original file line number Diff line number Diff line change
Expand Up @@ -732,8 +732,16 @@ export default {
'reloadConfiguration.help': 'Dalla sezione RICARICA CONFIGURAZIONE è possibile ricaricare la configurazione del sistema. Questa operazione si rende necessaria dopo la modifica di alcuni parametri.',
'reloadConfiguration.reload.title': 'Ricarica la Configurazione',
'reloadConfiguration.reload.confirm': 'Sei sicuro di voler ricaricare la configurazione?',
'reloadConfiguration.confirm.success': 'La configurazione di sistema è stata ricaricata.',
'reloadConfiguration.confirm.success': 'La configurazione di sistema è stata ricaricata con successo.',
'reloadConfiguration.confirm.error': 'Non è stato possibile ricaricare la configurazione di sistema',
'reloadConfiguration.confirm.progress': 'Ricaricamento della configurazione in corso...',
'reloadConfiguration.confirm.pleaseWait': 'Attendere mentre la configurazione del sistema viene ricaricata. Questa pagina si aggiornerà automaticamente.',
'reloadConfiguration.confirm.waiting': 'Ricaricamento della configurazione completato con avvisi. Alcuni componenti non sono stati ricaricati correttamente.',
'reloadConfiguration.confirm.fail': 'Ricaricamento della configurazione non riuscito. Controllare i log di sistema per ulteriori dettagli.',
'reloadConfiguration.table.beanId': 'Componente',
'reloadConfiguration.table.status': 'Stato',
'reloadConfiguration.bean.status.ok': 'OK',
'reloadConfiguration.bean.status.ko': 'Errore',
'activityStream.newPage': 'ha creato una nuova Pagina',
'activityStream.editPage': 'ha modificato una Pagina',
'activityStream.deletePage': 'ha eliminato una Pagina',
Expand Down
76 changes: 71 additions & 5 deletions src/state/reload-configuration/actions.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,95 @@
import { addToast, addErrors, TOAST_ERROR } from '@entando/messages';

import { reloadConf } from 'api/reloadConfiguration';
import { SET_STATUS } from 'state/reload-configuration/types';
import { reloadConf, getReloadStatus } from 'api/reloadConfiguration';
import { SET_STATUS, SET_RELOAD_INFO, SET_LOADING } from 'state/reload-configuration/types';
import { history, ROUTE_RELOAD_CONFIRM } from 'app-init/router';

const POLLING_INTERVAL = 500; // Poll every 2 seconds

export const setStatus = status => ({
type: SET_STATUS,
payload: {
status,
},
});

export const setReloadInfo = (percentage, info) => ({
type: SET_RELOAD_INFO,
payload: {
percentage,
info,
},
});

export const setLoading = loading => ({
type: SET_LOADING,
payload: {
loading,
},
});

// Poll the reload status
const pollReloadStatus = (dispatch) => {
const poll = () => {
getReloadStatus().then((response) => {
response.json().then((data) => {
if (response.ok) {
const { status, percentage, info } = data.payload;
dispatch(setStatus(status));
dispatch(setReloadInfo(percentage, info));

// Continue polling if still in progress
if (status === 'progress') {
setTimeout(poll, POLLING_INTERVAL);
} else {
// Reload complete - stop loading
dispatch(setLoading(false));
}
} else {
dispatch(addErrors(data.errors.map(err => err.message)));
dispatch(setLoading(false));
}
});
}).catch(() => {
dispatch(setLoading(false));
});
};

// Start polling
poll();
};

// thunk
export const sendReloadConf = () => dispatch =>
new Promise((resolve) => {
// Set initial loading state and status
dispatch(setLoading(true));
dispatch(setStatus('progress'));
dispatch(setReloadInfo(0, null));

reloadConf().then((response) => {
response.json().then((data) => {
if (response.ok) {
dispatch(setStatus(data.payload));
const { status, percentage, info } = data.payload;
dispatch(setStatus(status));
dispatch(setReloadInfo(percentage, info));

// Navigate to confirm page
history.push(ROUTE_RELOAD_CONFIRM);

// Always start polling to get the latest status
// This ensures we show progress even if reload is very fast
pollReloadStatus(dispatch);

resolve();
} else {
dispatch(addErrors(data.errors.map(err => err.message)));
data.errors.forEach(err => dispatch(addToast(err.message, TOAST_ERROR)));
dispatch(setLoading(false));
resolve();
}
resolve();
});
}).catch(() => {});
}).catch(() => {
dispatch(setLoading(false));
});
});
46 changes: 43 additions & 3 deletions src/state/reload-configuration/reducer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { SET_STATUS } from 'state/reload-configuration/types';
import { SET_STATUS, SET_RELOAD_INFO, SET_LOADING } from 'state/reload-configuration/types';
import { combineReducers } from 'redux';

export const status = (state = {}, action = {}) => {
const initialState = {
status: null,
percentage: null,
info: null,
loading: false,
};

export const status = (state = initialState.status, action = {}) => {
switch (action.type) {
case SET_STATUS: {
return action.payload.status;
Expand All @@ -9,4 +17,36 @@ export const status = (state = {}, action = {}) => {
}
};

export default status;
export const percentage = (state = initialState.percentage, action = {}) => {
switch (action.type) {
case SET_RELOAD_INFO: {
return action.payload.percentage;
}
default: return state;
}
};

export const info = (state = initialState.info, action = {}) => {
switch (action.type) {
case SET_RELOAD_INFO: {
return action.payload.info;
}
default: return state;
}
};

export const loading = (state = initialState.loading, action = {}) => {
switch (action.type) {
case SET_LOADING: {
return action.payload.loading;
}
default: return state;
}
};

export default combineReducers({
status,
percentage,
info,
loading,
});
3 changes: 3 additions & 0 deletions src/state/reload-configuration/selectors.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// eslint-disable-next-line
export const getConfiguration = state => state.configuration;
export const getStatus = state => state.configuration.status;
export const getPercentage = state => state.configuration.percentage;
export const getInfo = state => state.configuration.info;
export const getLoading = state => state.configuration.loading;
2 changes: 2 additions & 0 deletions src/state/reload-configuration/types.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
// eslint-disable-next-line
export const SET_STATUS = 'reload-configuration/set-status';
export const SET_RELOAD_INFO = 'reload-configuration/set-reload-info';
export const SET_LOADING = 'reload-configuration/set-loading';
91 changes: 87 additions & 4 deletions src/ui/reload-configuration/ReloadConfirm.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,109 @@
import React from 'react';
import { Alert } from 'patternfly-react';
import { Table } from 'react-bootstrap';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';

const ReloadConfirm = ({ status }) => {
const alertType = status === 'success' ? 'success' : 'danger';
const ReloadConfirm = ({
status, percentage, info, loading,
}) => {
// Determine alert type based on status
let alertType = 'info';
let messageId = 'reloadConfiguration.confirm.progress';

if (status === 'success') {
alertType = 'success';
messageId = 'reloadConfiguration.confirm.success';
} else if (status === 'waiting') {
alertType = 'warning';
messageId = 'reloadConfiguration.confirm.waiting';
} else if (status === 'fail') {
alertType = 'danger';
messageId = 'reloadConfiguration.confirm.fail';
}

// Render info table if available
const renderInfoTable = () => {
if (!info || Object.keys(info).length === 0) {
return null;
}

const entries = Object.entries(info);

return (
<div className="ReloadConfirm__table">
<Table striped bordered condensed hover>
<thead>
<tr>
<th>#</th>
<th>
<FormattedMessage id="reloadConfiguration.table.beanId" />
</th>
<th>
<FormattedMessage id="reloadConfiguration.table.status" />
</th>
</tr>
</thead>
<tbody>
{entries.map(([beanId, errorMessage], index) => {
const hasError = errorMessage && errorMessage.trim().length > 0;
return (
<tr key={beanId}>
<td>{index + 1}</td>
<td>
{hasError ? <strong>{beanId}</strong> : beanId}
</td>
<td>
{hasError ? (
<span>
<FormattedMessage id="reloadConfiguration.bean.status.ko" />
: {errorMessage}
</span>
) : (
<FormattedMessage id="reloadConfiguration.bean.status.ok" />
)}
</td>
</tr>
);
})}
</tbody>
</Table>
</div>
);
};

const isInProgress = status === 'progress' || loading;

return (
<div className="ReloadConfirm">
<Alert type={alertType}>
<FormattedMessage id={`reloadConfiguration.confirm.${status}`} />
<strong><FormattedMessage id={messageId} /></strong>
{isInProgress && percentage !== null && percentage >= 0 && (
<span> - {percentage}%</span>
)}
</Alert>
{isInProgress && (
<p className="text-muted">
<i className="fa fa-spinner fa-spin" /> <FormattedMessage id="reloadConfiguration.confirm.pleaseWait" />
</p>
)}
{renderInfoTable()}
</div>
);
};

ReloadConfirm.propTypes = {
status: PropTypes.string,
percentage: PropTypes.number,
info: PropTypes.objectOf(PropTypes.string),
loading: PropTypes.bool,
};

ReloadConfirm.defaultProps = {
status: 'error',
status: null,
percentage: null,
info: null,
loading: false,
};

export default ReloadConfirm;
5 changes: 4 additions & 1 deletion src/ui/reload-configuration/ReloadConfirmContainer.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@

import { connect } from 'react-redux';
import { getStatus } from 'state/reload-configuration/selectors';
import { getStatus, getPercentage, getInfo, getLoading } from 'state/reload-configuration/selectors';
import ReloadConfirm from 'ui/reload-configuration/ReloadConfirm';

export const mapStateToProps = state => ({
status: getStatus(state),
percentage: getPercentage(state),
info: getInfo(state),
loading: getLoading(state),
});

const ReloadConfirmContainer = connect(mapStateToProps, null)(ReloadConfirm);
Expand Down
Loading
Loading