Skip to content

Commit

Permalink
EPMRPP-98425 || Duplicate dashboard on current project
Browse files Browse the repository at this point in the history
  • Loading branch information
iso9000t committed Jan 30, 2025
1 parent e8a3e2e commit d2cdd10
Show file tree
Hide file tree
Showing 11 changed files with 331 additions and 27 deletions.
7 changes: 6 additions & 1 deletion app/src/common/urls.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ export const URLS = {
dashboard: (activeProject, id) => `${urlBase}${activeProject}/dashboard/${id}`,
dashboards: (activeProject, params) =>
`${urlBase}${activeProject}/dashboard${getQueryParams({ ...params })}`,
dashboardConfig: (activeProject, id) => `${urlBase}${activeProject}/dashboard/${id}/config`,
dashboardPreconfigured: (activeProject) => `${urlBase}${activeProject}/dashboard/preconfigured`,

widget: (activeProject, widgetId = '') => {
const widgetIdPart = widgetId ? `/${widgetId}` : '';
Expand Down Expand Up @@ -315,7 +317,10 @@ export const URLS = {
btsIntegrationPostTicket: (projectId, integrationId) =>
`${urlBase}bts/${projectId}/${integrationId}/ticket`,
btsTicket: (activeProject, issueId, btsProject, btsUrl) =>
`${urlBase}bts/${activeProject}/ticket/${issueId}${getQueryParams({ btsProject, btsUrl })}`,
`${urlBase}bts/${activeProject}/ticket/${issueId}${getQueryParams({
btsProject,
btsUrl,
})}`,
runUniqueErrorAnalysis: (activeProject) => `${urlBase}${activeProject}/launch/cluster`,
clusterByLaunchId: (activeProject, launchId, query) =>
`${urlBase}${activeProject}/launch/cluster/${launchId}${getQueryParams(query)}`,
Expand Down
12 changes: 12 additions & 0 deletions app/src/controllers/dashboard/actionCreators.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import {
UPDATE_DASHBOARD,
UPDATE_DASHBOARD_SUCCESS,
UPDATE_DASHBOARD_WIDGETS,
DUPLICATE_DASHBOARD,
DUPLICATE_DASHBOARD_SUCCESS,
} from './constants';

export const fetchDashboardsAction = (params) => ({
Expand All @@ -51,6 +53,16 @@ export const addDashboardSuccessAction = (item) => ({
payload: item,
});

export const duplicateDashboardAction = (item) => ({
type: DUPLICATE_DASHBOARD,
payload: item,
});

export const duplicateDashboardSuccessAction = (item) => ({
type: DUPLICATE_DASHBOARD_SUCCESS,
payload: item,
});

export const updateDashboardAction = (item) => ({
type: UPDATE_DASHBOARD,
payload: item,
Expand Down
2 changes: 2 additions & 0 deletions app/src/controllers/dashboard/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export const ADD_DASHBOARD_SUCCESS = 'addDashboardSuccess';
export const UPDATE_DASHBOARD = 'updateDashboard';
export const UPDATE_DASHBOARD_WIDGETS = 'updateDashboardWidgets';
export const UPDATE_DASHBOARD_SUCCESS = 'updateDashboardSuccess';
export const DUPLICATE_DASHBOARD = 'duplicateDashboard';
export const DUPLICATE_DASHBOARD_SUCCESS = 'duplicateDashboardSuccess';
export const REMOVE_DASHBOARD = 'removeDashboard';
export const REMOVE_DASHBOARD_SUCCESS = 'removeDashboardSuccess';
export const CHANGE_VISIBILITY_TYPE = 'changeVisibilityType';
Expand Down
2 changes: 2 additions & 0 deletions app/src/controllers/dashboard/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export {
updateDashboardWidgetsAction,
toggleFullScreenModeAction,
changeFullScreenModeAction,
duplicateDashboardAction,
duplicateDashboardSuccessAction,
} from './actionCreators';
export { dashboardReducer } from './reducer';
export {
Expand Down
3 changes: 3 additions & 0 deletions app/src/controllers/dashboard/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
REMOVE_DASHBOARD_SUCCESS,
TOGGLE_FULL_SCREEN_MODE,
UPDATE_DASHBOARD_SUCCESS,
DUPLICATE_DASHBOARD_SUCCESS,
} from './constants';

const dashboardsReducer = (state = INITIAL_STATE.dashboards, { type = '', payload = {} }) => {
Expand All @@ -46,6 +47,8 @@ const dashboardsReducer = (state = INITIAL_STATE.dashboards, { type = '', payloa
return state.map((item) => (item.id === payload.id ? payload : item));
case REMOVE_DASHBOARD_SUCCESS:
return state.filter((item) => item.id !== payload);
case DUPLICATE_DASHBOARD_SUCCESS:
return [...state, payload];
default:
return state;
}
Expand Down
38 changes: 38 additions & 0 deletions app/src/controllers/dashboard/sagas.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
REMOVE_DASHBOARD_SUCCESS,
INCREASE_TOTAL_DASHBOARDS_LOCALLY,
DECREASE_TOTAL_DASHBOARDS_LOCALLY,
DUPLICATE_DASHBOARD,
} from './constants';
import { dashboardItemsSelector, querySelector } from './selectors';
import {
Expand Down Expand Up @@ -131,6 +132,42 @@ function* addDashboard({ payload: dashboard }) {
});
}

function* duplicateDashboard({ payload: dashboard }) {
const activeProject = yield select(activeProjectSelector);
try {
const config = yield call(fetch, URLS.dashboardConfig(activeProject, dashboard.id));

const result = yield call(fetch, URLS.dashboardPreconfigured(activeProject), {
method: 'post',
data: {
name: dashboard.name,
description: dashboard.description,
config,
},
});

yield put(
addDashboardSuccessAction({
id: result.id,
name: dashboard.name,
description: dashboard.description,
owner: dashboard.owner,
widgets: [], // or config.widgets if available
}),
);

yield put(
showNotification({
message: 'Dashboard successfully duplicated',
type: NOTIFICATION_TYPES.SUCCESS,
}),
);
yield put(hideModalAction());
} catch (error) {
yield put(showDefaultErrorNotification(error));
}
}

function* updateDashboard({ payload: dashboard }) {
const activeProject = yield select(activeProjectSelector);
const { name, description, id } = dashboard;
Expand Down Expand Up @@ -205,5 +242,6 @@ export function* dashboardSagas() {
yield takeEvery(REMOVE_DASHBOARD, removeDashboard),
yield takeEvery(CHANGE_VISIBILITY_TYPE, changeVisibilityType),
yield takeEvery(REMOVE_DASHBOARD_SUCCESS, redirectAfterDelete),
yield takeEvery(DUPLICATE_DASHBOARD, duplicateDashboard),
]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
OwnerColumn,
EditColumn,
DeleteColumn,
DuplicateColumn,
} from './dashboardTableColumns';
import styles from './dashboardTable.scss';

Expand All @@ -45,6 +46,10 @@ const messages = defineMessages({
id: 'DashboardTable.owner',
defaultMessage: 'Owner',
},
duplicate: {
id: 'DashboardTable.duplicate',
defaultMessage: 'Duplicate',
},
edit: {
id: 'DashboardTable.edit',
defaultMessage: 'Edit',
Expand All @@ -64,6 +69,7 @@ export class DashboardTable extends Component {
intl: PropTypes.object.isRequired,
onDeleteItem: PropTypes.func,
onEditItem: PropTypes.func,
onDuplicate: PropTypes.func,
onAddItem: PropTypes.func,
projectId: PropTypes.string,
dashboardItems: PropTypes.array,
Expand All @@ -74,6 +80,7 @@ export class DashboardTable extends Component {
static defaultProps = {
onDeleteItem: () => {},
onEditItem: () => {},
onDuplicate: () => {},
onAddItem: () => {},
projectId: '',
dashboardItems: [],
Expand All @@ -82,7 +89,7 @@ export class DashboardTable extends Component {
};

getTableColumns() {
const { onDeleteItem, onEditItem, intl, projectId } = this.props;
const { onDeleteItem, onEditItem, onDuplicate, intl, projectId } = this.props;

return [
{
Expand Down Expand Up @@ -111,6 +118,17 @@ export class DashboardTable extends Component {
formatter: (value) => value.owner,
component: OwnerColumn,
},
{
title: {
full: intl.formatMessage(messages.duplicate),
short: intl.formatMessage(messages.duplicate),
},
component: DuplicateColumn,
customProps: {
onDuplicate,
},
align: ALIGN_CENTER,
},
{
title: {
full: intl.formatMessage(messages.edit),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,109 @@
}

.icon-holder {
border-left: 1px solid $COLOR--gray-91;
min-height: 20px;
display: flex;
align-items: center;
justify-content: center;

&.no-border {
border-left: none;
}
}

.edit-cell,
.delete-cell {
.icon-holder {
border-left: 1px solid $COLOR--gray-91;
}
}

.dashboard-table {
background-color: $COLOR--white-two;
padding-bottom: 15px;
}

.with-button {
i {
cursor: pointer;
}
}

.duplicate-dropdown {
position: relative;
display: flex;
align-items: center;
padding-right: 15px;
cursor: pointer;
color: $COLOR--gray-60;

&:hover {
color: $COLOR--gray-47;
}
}

.duplicate-icon {
display: flex;
justify-content: center;
align-items: center;
height: 20px;
width: 20px;

svg {
height: 30px;
width: 30px;
stroke-width: 1.5;
}
}

.arrow {
position: absolute;
right: 2px;
top: 50%;
margin-top: -1px;
width: 0;
height: 0;
border-top: 4px solid currentColor;
border-right: 4px solid transparent;
border-left: 4px solid transparent;
box-sizing: border-box;
transition: transform 200ms linear;

&.opened {
transform: rotate(180deg);
}
}

.hamburger-menu {
position: absolute;
top: 100%;
right: -25px;
z-index: $Z-INDEX-POPUP;
margin-top: 4px;
background: $COLOR--white-two;
border: 1px solid $COLOR--gray-91;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
border-radius: 4px;
min-width: max-content;
white-space: nowrap;
display: none;

&.shown {
display: block;
}
}

.dropdown-item {
padding: 8px 15px;
font-size: 13px;
color: $COLOR--charcoal-grey;
text-align: left;

&:hover {
background: $COLOR--tealish-hover;
}
}

@media (max-width: $SCREEN_SM_MAX) {
.name,
.description,
Expand All @@ -82,9 +176,3 @@
}
}
}

.with-button {
i {
cursor: pointer;
}
}
Loading

0 comments on commit d2cdd10

Please sign in to comment.