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

ICU-13883 add remove org modal #2435

Merged
merged 11 commits into from
Aug 15, 2024
10 changes: 10 additions & 0 deletions addons/core/translations/resources/en-us.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,10 @@ role:
help: Customize which scopes are associated with this role.
view-projects: '{selected}/{total} total projects'
apply: Apply changes to org
remove-org-and-projects: Remove org and projects
remove-org-only: Remove just the org
remove-orgs-and-projects: Remove orgs and projects
remove-orgs-only: Remove just the orgs
form:
this:
label: Add this scope
Expand All @@ -627,6 +631,12 @@ role:
descendants:
label: Add all descendants
help: Add all of the orgs and the projects in the orgs that are underneath this scope.
remove-org:
title: Remove this org?
description: You're about to remove {orgDisplayName}. Do you want to remove the projects under {orgDisplayName} as well? Or do you just want to remove {orgDisplayName} from this project?
remove-all-orgs:
title: Remove all selected orgs?
description: You're about to remove all the selected orgs. Do you want to remove the projects under these orgs as well? Or do you just want to remove just the orgs?
target:
title: Target
title_plural: Targets
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
@iconPosition='trailing'
@text={{t
'resources.role.scope.actions.view-projects'
selected=projectTotals.selected
selected=projectTotals.selected.length
total=projectTotals.total
}}
@route='scopes.scope.roles.role.manage-scopes.manage-org-projects'
Expand Down Expand Up @@ -127,4 +127,63 @@
/>
</A.Footer>
</Hds::ApplicationState>
{{/if}}

{{#if this.selectedOrg}}
<Hds::Modal
@onClose={{this.toggleRemoveOrgModal}}
data-test-manage-scopes-remove-org-modal
as |M|
>
<M.Header>{{t 'resources.role.scope.remove-org.title'}}</M.Header>
<M.Body>
<Hds::Text::Body @tag='p' @color='primary'>
{{t
'resources.role.scope.remove-org.description'
orgDisplayName=this.orgDisplayName
}}
</Hds::Text::Body>
</M.Body>
<M.Footer as |F|>
<Hds::ButtonSet>
<Hds::Button
@text={{t 'resources.role.scope.actions.remove-org-and-projects'}}
{{on 'click' (fn this.removeProjects false)}}
/>
<Hds::Button
@color='secondary'
@text={{t 'resources.role.scope.actions.remove-org-only'}}
{{on 'click' F.close}}
/>
</Hds::ButtonSet>
</M.Footer>
</Hds::Modal>
{{/if}}

{{#if this.selectedOrgs}}
<Hds::Modal
@onClose={{this.toggleRemoveAllModal}}
data-test-manage-scopes-remove-all-orgs-modal
as |M|
>
<M.Header>{{t 'resources.role.scope.remove-all-orgs.title'}}</M.Header>
<M.Body>
<Hds::Text::Body @tag='p' @color='primary'>
{{t 'resources.role.scope.remove-all-orgs.description'}}
</Hds::Text::Body>
</M.Body>
<M.Footer as |F|>
<Hds::ButtonSet>
<Hds::Button
@text={{t 'resources.role.scope.actions.remove-orgs-and-projects'}}
{{on 'click' (fn this.removeProjects true)}}
/>
<Hds::Button
@color='secondary'
@text={{t 'resources.role.scope.actions.remove-orgs-only'}}
{{on 'click' F.close}}
/>
</Hds::ButtonSet>
</M.Footer>
</Hds::Modal>
{{/if}}
113 changes: 104 additions & 9 deletions ui/admin/app/components/form/role/manage-custom-scopes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,122 @@

import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class FormRoleManageCustomScopesIndexComponent extends Component {
// =attributes

@tracked selectedOrgs = [];
@tracked selectedOrg = '';

/**
* Returns the display name of the selectedOrg.
* @type {string}
*/
get orgDisplayName() {
const org = this.args.model.orgScopes.find(
({ id }) => id === this.selectedOrg,
);
return org.displayName;
}

// =actions

/**
* Handles the selection changes for the paginated table.
* @param {object} selectableRowsStates
* @param {object} selectionKey
*/
@action
selectionChange({ selectableRowsStates }) {
const { role } = this.args.model;
selectableRowsStates.forEach((row) => {
const { isSelected, selectionKey: key } = row;
const includesId = role.grant_scope_ids.includes(key);
if (isSelected && !includesId) {
role.grant_scope_ids = [...role.grant_scope_ids, key];
} else if (!isSelected && includesId) {
selectionChange({ selectableRowsStates, selectionKey }) {
const { role, projectTotals } = this.args.model;

const addOrRemoveValues = (add, remove, orgId) => {
lisbet-alvarez marked this conversation as resolved.
Show resolved Hide resolved
let selectedOrg;
const includesId = role.grant_scope_ids.includes(orgId);
const selected = projectTotals[orgId]?.selected;
const remaining = projectTotals[orgId]?.remaining;
const total = projectTotals[orgId]?.total;
if (add && !includesId) {
if (remaining) {
role.grant_scope_ids = [...role.grant_scope_ids, ...remaining];
projectTotals[orgId] = {
selected: [...selected, ...remaining],
total,
remaining: [],
};
}
role.grant_scope_ids = [...role.grant_scope_ids, orgId];
} else if (remove && includesId) {
if (selected?.length) {
selectedOrg = orgId;
}
role.grant_scope_ids = role.grant_scope_ids.filter(
(item) => item !== key,
(item) => item !== orgId,
);
}
return selectedOrg;
};

if (selectionKey === 'all') {
const selectedOrgs = [];
selectableRowsStates.forEach(({ selectionKey, isSelected }) => {
const selectedOrg = addOrRemoveValues(
isSelected,
!isSelected,
selectionKey,
);
if (selectedOrg) {
selectedOrgs.push(selectedOrg);
}
});
this.selectedOrgs = selectedOrgs;
} else {
this.selectedOrg = addOrRemoveValues(true, true, selectionKey);
}
}

/**
* Removes projects from an org that was deselected and toggles the correct modal off.
* @param {boolean} toggleRemoveAllModal
*/
@action
removeProjects(toggleRemoveAllModal) {
const selectedOrgs = toggleRemoveAllModal
? this.selectedOrgs
: [this.selectedOrg];
const { role, projectTotals } = this.args.model;
selectedOrgs.forEach((orgId) => {
const { selected, total, remaining } = projectTotals[orgId];
role.grant_scope_ids = role.grant_scope_ids.filter(
(item) => !selected.includes(item),
);
projectTotals[orgId] = {
selected: [],
total,
remaining: [...selected, ...remaining],
};
});
if (toggleRemoveAllModal) {
this.toggleRemoveAllModal();
} else {
this.toggleRemoveOrgModal();
}
}

/**
* Toggles the modal to remove an org and projects off.
*/
@action
toggleRemoveOrgModal() {
this.selectedOrg = [];
lisbet-alvarez marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Toggles the modal to remove orgs and projects off.
*/
@action
toggleRemoveAllModal() {
this.selectedOrgs = [];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import Route from '@ember/routing/route';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
import { TrackedObject } from 'tracked-built-ins';
lisbet-alvarez marked this conversation as resolved.
Show resolved Hide resolved
import { TYPE_SCOPE_PROJECT } from 'api/models/scope';

export default class ScopesScopeRolesRoleManageScopesManageCustomScopesRoute extends Route {
Expand Down Expand Up @@ -88,13 +89,17 @@ export default class ScopesScopeRolesRoleManageScopesManageCustomScopesRoute ext
options,
);

const projectTotals = {};
// We want this object to be tracked so that changes to this object
// cause the "Projects selected" column to re-render with updates.
const projectTotals = new TrackedObject({});
projects.forEach(({ id, scope }) => {
if (!projectTotals[scope.id]) {
projectTotals[scope.id] = { selected: 0, total: 0 };
projectTotals[scope.id] = { selected: [], total: 0, remaining: [] };
}
if (projectIDs.includes(id)) {
projectTotals[scope.id].selected++;
projectTotals[scope.id].selected.push(id);
} else {
projectTotals[scope.id].remaining.push(id);
}
projectTotals[scope.id].total++;
});
Expand Down
Loading