Merge pull request #1294 from dod-ccpo/portfolio-admin-styling__part-2
Portfolio admin styling - Managers table
This commit is contained in:
commit
7de2f440c6
@ -75,10 +75,10 @@ class Portfolios(object):
|
|||||||
permission_sets = PortfolioRoles._permission_sets_for_names(
|
permission_sets = PortfolioRoles._permission_sets_for_names(
|
||||||
member_data.get("permission_sets", [])
|
member_data.get("permission_sets", [])
|
||||||
)
|
)
|
||||||
role = PortfolioRole(portfolio_id=portfolio.id, permission_sets=permission_sets)
|
role = PortfolioRole(portfolio=portfolio, permission_sets=permission_sets)
|
||||||
|
|
||||||
invitation = PortfolioInvitations.create(
|
invitation = PortfolioInvitations.create(
|
||||||
inviter=inviter, role=role, member_data=member_data
|
inviter=inviter, role=role, member_data=member_data["user_data"]
|
||||||
)
|
)
|
||||||
|
|
||||||
PortfoliosQuery.add_and_commit(role)
|
PortfoliosQuery.add_and_commit(role)
|
||||||
|
@ -1,76 +1,59 @@
|
|||||||
from wtforms.validators import Required
|
from wtforms.validators import Required
|
||||||
from wtforms.fields import StringField, FormField, FieldList, HiddenField
|
from wtforms.fields import BooleanField, FormField
|
||||||
|
|
||||||
from atst.domain.permission_sets import PermissionSets
|
|
||||||
from .forms import BaseForm
|
from .forms import BaseForm
|
||||||
from .member import NewForm as BaseNewMemberForm
|
from .member import NewForm as BaseNewMemberForm
|
||||||
|
from atst.domain.permission_sets import PermissionSets
|
||||||
from atst.forms.fields import SelectField
|
from atst.forms.fields import SelectField
|
||||||
from atst.utils.localization import translate
|
from atst.utils.localization import translate
|
||||||
|
|
||||||
|
|
||||||
class PermissionsForm(BaseForm):
|
class PermissionsForm(BaseForm):
|
||||||
member_name = StringField()
|
perms_app_mgmt = BooleanField(
|
||||||
member_id = HiddenField()
|
translate("forms.new_member.app_mgmt.label"),
|
||||||
perms_app_mgmt = SelectField(
|
default=False,
|
||||||
translate("forms.new_member.app_mgmt"),
|
description=translate("forms.new_member.app_mgmt.description"),
|
||||||
choices=[
|
|
||||||
(
|
|
||||||
PermissionSets.VIEW_PORTFOLIO_APPLICATION_MANAGEMENT,
|
|
||||||
translate("common.view"),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
PermissionSets.EDIT_PORTFOLIO_APPLICATION_MANAGEMENT,
|
|
||||||
translate("common.edit"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
perms_funding = SelectField(
|
perms_funding = BooleanField(
|
||||||
translate("forms.new_member.funding"),
|
translate("forms.new_member.funding.label"),
|
||||||
choices=[
|
default=False,
|
||||||
(PermissionSets.VIEW_PORTFOLIO_FUNDING, translate("common.view")),
|
description=translate("forms.new_member.funding.description"),
|
||||||
(PermissionSets.EDIT_PORTFOLIO_FUNDING, translate("common.edit")),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
perms_reporting = SelectField(
|
perms_reporting = BooleanField(
|
||||||
translate("forms.new_member.reporting"),
|
translate("forms.new_member.reporting.label"),
|
||||||
choices=[
|
default=False,
|
||||||
(PermissionSets.VIEW_PORTFOLIO_REPORTS, translate("common.view")),
|
description=translate("forms.new_member.reporting.description"),
|
||||||
(PermissionSets.EDIT_PORTFOLIO_REPORTS, translate("common.edit")),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
perms_portfolio_mgmt = SelectField(
|
perms_portfolio_mgmt = BooleanField(
|
||||||
translate("forms.new_member.portfolio_mgmt"),
|
translate("forms.new_member.portfolio_mgmt.label"),
|
||||||
choices=[
|
default=False,
|
||||||
(PermissionSets.VIEW_PORTFOLIO_ADMIN, translate("common.view")),
|
description=translate("forms.new_member.portfolio_mgmt.description"),
|
||||||
(PermissionSets.EDIT_PORTFOLIO_ADMIN, translate("common.edit")),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def data(self):
|
def data(self):
|
||||||
_data = super().data
|
_data = super().data
|
||||||
_data["permission_sets"] = []
|
_data.pop("csrf_token", None)
|
||||||
for field in _data:
|
perm_sets = []
|
||||||
if "perms" in field:
|
|
||||||
_data["permission_sets"].append(_data[field])
|
|
||||||
|
|
||||||
|
if _data["perms_app_mgmt"]:
|
||||||
|
perm_sets.append(PermissionSets.EDIT_PORTFOLIO_APPLICATION_MANAGEMENT)
|
||||||
|
|
||||||
|
if _data["perms_funding"]:
|
||||||
|
perm_sets.append(PermissionSets.EDIT_PORTFOLIO_FUNDING)
|
||||||
|
|
||||||
|
if _data["perms_reporting"]:
|
||||||
|
perm_sets.append(PermissionSets.EDIT_PORTFOLIO_REPORTS)
|
||||||
|
|
||||||
|
if _data["perms_portfolio_mgmt"]:
|
||||||
|
perm_sets.append(PermissionSets.EDIT_PORTFOLIO_ADMIN)
|
||||||
|
|
||||||
|
_data["permission_sets"] = perm_sets
|
||||||
return _data
|
return _data
|
||||||
|
|
||||||
|
|
||||||
class MembersPermissionsForm(BaseForm):
|
class NewForm(PermissionsForm):
|
||||||
members_permissions = FieldList(FormField(PermissionsForm))
|
|
||||||
|
|
||||||
|
|
||||||
class NewForm(BaseForm):
|
|
||||||
user_data = FormField(BaseNewMemberForm)
|
user_data = FormField(BaseNewMemberForm)
|
||||||
permission_sets = FormField(PermissionsForm)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def update_data(self):
|
|
||||||
return {
|
|
||||||
"permission_sets": self.data.get("permission_sets").get("permission_sets"),
|
|
||||||
**self.data.get("user_data"),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class AssignPPOCForm(PermissionsForm):
|
class AssignPPOCForm(PermissionsForm):
|
||||||
|
@ -12,17 +12,6 @@ from atst.utils import first_or_none
|
|||||||
from atst.models.mixins.auditable import record_permission_sets_updates
|
from atst.models.mixins.auditable import record_permission_sets_updates
|
||||||
|
|
||||||
|
|
||||||
MEMBER_STATUSES = {
|
|
||||||
"active": "Active",
|
|
||||||
"revoked": "Invite revoked",
|
|
||||||
"expired": "Invite expired",
|
|
||||||
"error": "Error on invite",
|
|
||||||
"pending": "Pending",
|
|
||||||
"unknown": "Unknown errors",
|
|
||||||
"disabled": "Disabled",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Status(Enum):
|
class Status(Enum):
|
||||||
ACTIVE = "active"
|
ACTIVE = "active"
|
||||||
DISABLED = "disabled"
|
DISABLED = "disabled"
|
||||||
@ -90,23 +79,23 @@ class PortfolioRole(
|
|||||||
@property
|
@property
|
||||||
def display_status(self):
|
def display_status(self):
|
||||||
if self.status == Status.ACTIVE:
|
if self.status == Status.ACTIVE:
|
||||||
return MEMBER_STATUSES["active"]
|
return "active"
|
||||||
elif self.status == Status.DISABLED:
|
elif self.status == Status.DISABLED:
|
||||||
return MEMBER_STATUSES["disabled"]
|
return "disabled"
|
||||||
elif self.latest_invitation:
|
elif self.latest_invitation:
|
||||||
if self.latest_invitation.is_revoked:
|
if self.latest_invitation.is_revoked:
|
||||||
return MEMBER_STATUSES["revoked"]
|
return "invite_revoked"
|
||||||
elif self.latest_invitation.is_rejected_wrong_user:
|
elif self.latest_invitation.is_rejected_wrong_user:
|
||||||
return MEMBER_STATUSES["error"]
|
return "invite_error"
|
||||||
elif (
|
elif (
|
||||||
self.latest_invitation.is_rejected_expired
|
self.latest_invitation.is_rejected_expired
|
||||||
or self.latest_invitation.is_expired
|
or self.latest_invitation.is_expired
|
||||||
):
|
):
|
||||||
return MEMBER_STATUSES["expired"]
|
return "invite_expired"
|
||||||
else:
|
else:
|
||||||
return MEMBER_STATUSES["pending"]
|
return "invite_pending"
|
||||||
else:
|
else:
|
||||||
return MEMBER_STATUSES["unknown"]
|
return "unknown"
|
||||||
|
|
||||||
def has_permission_set(self, perm_set_name):
|
def has_permission_set(self, perm_set_name):
|
||||||
return first_or_none(
|
return first_or_none(
|
||||||
|
@ -17,63 +17,51 @@ from atst.utils.flash import formatted_flash as flash
|
|||||||
from atst.domain.exceptions import UnauthorizedError
|
from atst.domain.exceptions import UnauthorizedError
|
||||||
|
|
||||||
|
|
||||||
def permission_str(member, edit_perm_set, view_perm_set):
|
def filter_perm_sets_data(member):
|
||||||
if member.has_permission_set(edit_perm_set):
|
perm_sets_data = {
|
||||||
return edit_perm_set
|
"perms_portfolio_mgmt": bool(
|
||||||
else:
|
member.has_permission_set(PermissionSets.EDIT_PORTFOLIO_ADMIN)
|
||||||
return view_perm_set
|
|
||||||
|
|
||||||
|
|
||||||
def serialize_member_form_data(member):
|
|
||||||
return {
|
|
||||||
"member_name": member.full_name,
|
|
||||||
"member_id": member.id,
|
|
||||||
"perms_app_mgmt": permission_str(
|
|
||||||
member,
|
|
||||||
PermissionSets.EDIT_PORTFOLIO_APPLICATION_MANAGEMENT,
|
|
||||||
PermissionSets.VIEW_PORTFOLIO_APPLICATION_MANAGEMENT,
|
|
||||||
),
|
),
|
||||||
"perms_funding": permission_str(
|
"perms_app_mgmt": bool(
|
||||||
member,
|
member.has_permission_set(
|
||||||
PermissionSets.EDIT_PORTFOLIO_FUNDING,
|
PermissionSets.EDIT_PORTFOLIO_APPLICATION_MANAGEMENT
|
||||||
PermissionSets.VIEW_PORTFOLIO_FUNDING,
|
)
|
||||||
),
|
),
|
||||||
"perms_reporting": permission_str(
|
"perms_funding": bool(
|
||||||
member,
|
member.has_permission_set(PermissionSets.EDIT_PORTFOLIO_FUNDING)
|
||||||
PermissionSets.EDIT_PORTFOLIO_REPORTS,
|
|
||||||
PermissionSets.VIEW_PORTFOLIO_REPORTS,
|
|
||||||
),
|
),
|
||||||
"perms_portfolio_mgmt": permission_str(
|
"perms_reporting": bool(
|
||||||
member,
|
member.has_permission_set(PermissionSets.EDIT_PORTFOLIO_REPORTS)
|
||||||
PermissionSets.EDIT_PORTFOLIO_ADMIN,
|
|
||||||
PermissionSets.VIEW_PORTFOLIO_ADMIN,
|
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return perm_sets_data
|
||||||
|
|
||||||
def get_members_data(portfolio):
|
|
||||||
members = sorted(
|
def filter_members_data(members_list, portfolio):
|
||||||
[serialize_member_form_data(member) for member in portfolio.members],
|
members_data = []
|
||||||
key=lambda member: member["member_name"],
|
for member in members_list:
|
||||||
)
|
members_data.append(
|
||||||
for member in members:
|
{
|
||||||
if member["member_id"] == portfolio.owner_role.id:
|
"role_id": member.id,
|
||||||
ppoc = member
|
"user_name": member.user_name,
|
||||||
members.remove(member)
|
"permission_sets": filter_perm_sets_data(member),
|
||||||
members.insert(0, ppoc)
|
"status": member.display_status,
|
||||||
return members
|
"ppoc": PermissionSets.PORTFOLIO_POC in member.permission_sets,
|
||||||
|
# add in stuff here for forms
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return sorted(members_data, key=lambda member: member["user_name"])
|
||||||
|
|
||||||
|
|
||||||
def render_admin_page(portfolio, form=None):
|
def render_admin_page(portfolio, form=None):
|
||||||
pagination_opts = Paginator.get_pagination_opts(http_request)
|
pagination_opts = Paginator.get_pagination_opts(http_request)
|
||||||
audit_events = AuditLog.get_portfolio_events(portfolio, pagination_opts)
|
audit_events = AuditLog.get_portfolio_events(portfolio, pagination_opts)
|
||||||
members_data = get_members_data(portfolio)
|
|
||||||
portfolio_form = PortfolioForm(obj=portfolio)
|
portfolio_form = PortfolioForm(obj=portfolio)
|
||||||
member_perms_form = member_forms.MembersPermissionsForm(
|
member_list = portfolio.members
|
||||||
data={"members_permissions": members_data}
|
|
||||||
)
|
|
||||||
|
|
||||||
assign_ppoc_form = member_forms.AssignPPOCForm()
|
assign_ppoc_form = member_forms.AssignPPOCForm()
|
||||||
|
|
||||||
for pf_role in portfolio.roles:
|
for pf_role in portfolio.roles:
|
||||||
if pf_role.user != portfolio.owner and pf_role.is_active:
|
if pf_role.user != portfolio.owner and pf_role.is_active:
|
||||||
assign_ppoc_form.role_id.choices += [(pf_role.id, pf_role.full_name)]
|
assign_ppoc_form.role_id.choices += [(pf_role.id, pf_role.full_name)]
|
||||||
@ -87,13 +75,12 @@ def render_admin_page(portfolio, form=None):
|
|||||||
"portfolios/admin.html",
|
"portfolios/admin.html",
|
||||||
form=form,
|
form=form,
|
||||||
portfolio_form=portfolio_form,
|
portfolio_form=portfolio_form,
|
||||||
member_perms_form=member_perms_form,
|
members=filter_members_data(member_list, portfolio),
|
||||||
member_form=member_forms.NewForm(),
|
new_manager_form=member_forms.NewForm(),
|
||||||
assign_ppoc_form=assign_ppoc_form,
|
assign_ppoc_form=assign_ppoc_form,
|
||||||
portfolio=portfolio,
|
portfolio=portfolio,
|
||||||
audit_events=audit_events,
|
audit_events=audit_events,
|
||||||
user=g.current_user,
|
user=g.current_user,
|
||||||
ppoc_id=members_data[0].get("member_id"),
|
|
||||||
current_member_id=current_member_id,
|
current_member_id=current_member_id,
|
||||||
applications_count=len(portfolio.applications),
|
applications_count=len(portfolio.applications),
|
||||||
)
|
)
|
||||||
@ -106,34 +93,6 @@ def admin(portfolio_id):
|
|||||||
return render_admin_page(portfolio)
|
return render_admin_page(portfolio)
|
||||||
|
|
||||||
|
|
||||||
@portfolios_bp.route("/portfolios/<portfolio_id>/admin", methods=["POST"])
|
|
||||||
@user_can(Permissions.EDIT_PORTFOLIO_USERS, message="view portfolio admin page")
|
|
||||||
def edit_members(portfolio_id):
|
|
||||||
portfolio = Portfolios.get_for_update(portfolio_id)
|
|
||||||
member_perms_form = member_forms.MembersPermissionsForm(http_request.form)
|
|
||||||
|
|
||||||
if member_perms_form.validate():
|
|
||||||
for subform in member_perms_form.members_permissions:
|
|
||||||
member_id = subform.member_id.data
|
|
||||||
member = PortfolioRoles.get_by_id(member_id)
|
|
||||||
if member is not portfolio.owner_role:
|
|
||||||
new_perm_set = subform.data["permission_sets"]
|
|
||||||
PortfolioRoles.update(member, new_perm_set)
|
|
||||||
|
|
||||||
flash("update_portfolio_members", portfolio=portfolio)
|
|
||||||
|
|
||||||
return redirect(
|
|
||||||
url_for(
|
|
||||||
"portfolios.admin",
|
|
||||||
portfolio_id=portfolio_id,
|
|
||||||
fragment="portfolio-members",
|
|
||||||
_anchor="portfolio-members",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return render_admin_page(portfolio)
|
|
||||||
|
|
||||||
|
|
||||||
@portfolios_bp.route("/portfolios/<portfolio_id>/update_ppoc", methods=["POST"])
|
@portfolios_bp.route("/portfolios/<portfolio_id>/update_ppoc", methods=["POST"])
|
||||||
@user_can(Permissions.EDIT_PORTFOLIO_POC, message="update portfolio ppoc")
|
@user_can(Permissions.EDIT_PORTFOLIO_POC, message="update portfolio ppoc")
|
||||||
def update_ppoc(portfolio_id):
|
def update_ppoc(portfolio_id):
|
||||||
|
@ -79,7 +79,7 @@ def invite_member(portfolio_id):
|
|||||||
|
|
||||||
if form.validate():
|
if form.validate():
|
||||||
try:
|
try:
|
||||||
invite = Portfolios.invite(portfolio, g.current_user, form.update_data)
|
invite = Portfolios.invite(portfolio, g.current_user, form.data)
|
||||||
send_portfolio_invitation(
|
send_portfolio_invitation(
|
||||||
invitee_email=invite.email,
|
invitee_email=invite.email,
|
||||||
inviter_name=g.current_user.full_name,
|
inviter_name=g.current_user.full_name,
|
||||||
|
@ -159,7 +159,7 @@ def get_users():
|
|||||||
|
|
||||||
def add_members_to_portfolio(portfolio):
|
def add_members_to_portfolio(portfolio):
|
||||||
for user_data in PORTFOLIO_USERS:
|
for user_data in PORTFOLIO_USERS:
|
||||||
invite = Portfolios.invite(portfolio, portfolio.owner, user_data)
|
invite = Portfolios.invite(portfolio, portfolio.owner, {"user_data": user_data})
|
||||||
profile = {
|
profile = {
|
||||||
k: user_data[k] for k in user_data if k not in ["dod_id", "permission_sets"]
|
k: user_data[k] for k in user_data if k not in ["dod_id", "permission_sets"]
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
@import "components/dod_login_notice.scss";
|
@import "components/dod_login_notice.scss";
|
||||||
@import "components/sticky_cta.scss";
|
@import "components/sticky_cta.scss";
|
||||||
@import "components/error_page.scss";
|
@import "components/error_page.scss";
|
||||||
|
@import "components/member_form.scss";
|
||||||
|
|
||||||
@import "sections/login";
|
@import "sections/login";
|
||||||
@import "sections/home";
|
@import "sections/home";
|
||||||
|
61
styles/components/_member_form.scss
Normal file
61
styles/components/_member_form.scss
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
.member-form {
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
input[type="checkbox"] + label::before {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input__inline-fields {
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
.usa-input__choices label {
|
||||||
|
font-weight: $font-bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.input__inline-fields {
|
||||||
|
padding: $gap * 2;
|
||||||
|
border: 1px solid $color-gray-lighter;
|
||||||
|
|
||||||
|
&.checked {
|
||||||
|
border: 1px solid $color-blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-weight: $font-bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.usa-input__help {
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding-left: 3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
.usa-input {
|
||||||
|
width: 45rem;
|
||||||
|
|
||||||
|
input,
|
||||||
|
label,
|
||||||
|
.usa-input__message {
|
||||||
|
max-width: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
label .icon-validation {
|
||||||
|
left: unset;
|
||||||
|
right: -$gap * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--validation--phoneExt {
|
||||||
|
width: 18rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#modal--add-app-mem,
|
||||||
|
#modal--add-portfolio-manager {
|
||||||
|
.modal__body {
|
||||||
|
min-width: 75rem;
|
||||||
|
}
|
||||||
|
}
|
@ -5,13 +5,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
margin-left: 2 * $gap;
|
margin-left: 2 * $gap;
|
||||||
|
|
||||||
.line {
|
|
||||||
box-sizing: border-box;
|
|
||||||
height: 2px;
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid $color-gray-lightest;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.portfolio-header {
|
.portfolio-header {
|
||||||
@ -40,36 +33,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__budget {
|
|
||||||
font-size: $small-font-size;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.icon-tooltip {
|
|
||||||
margin-left: -$gap / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--dollars {
|
|
||||||
font-size: $h2-font-size;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--amount {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--cents {
|
|
||||||
font-size: 2rem;
|
|
||||||
margin-top: 0.75rem;
|
|
||||||
margin-left: -0.7rem;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.links {
|
.links {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: $small-font-size;
|
font-size: $small-font-size;
|
||||||
@ -109,22 +72,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.column-left {
|
|
||||||
width: 12.5rem;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.column-right {
|
|
||||||
margin-left: -0.4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.unfunded {
|
|
||||||
color: $color-red;
|
|
||||||
.icon {
|
|
||||||
@include icon-color($color-red);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin subheading {
|
@mixin subheading {
|
||||||
@ -138,6 +85,10 @@
|
|||||||
.portfolio-content {
|
.portfolio-content {
|
||||||
margin: (4 * $gap) $gap 0 $gap;
|
margin: (4 * $gap) $gap 0 $gap;
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
padding-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
a.add-new-button {
|
a.add-new-button {
|
||||||
display: inherit;
|
display: inherit;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
@ -157,44 +108,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input.usa-button.usa-button-primary {
|
|
||||||
width: 9rem;
|
|
||||||
height: 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
padding-left: 1.2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.members-table-ppoc {
|
|
||||||
select::-ms-expand {
|
|
||||||
display: none;
|
|
||||||
color: $color-gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
-moz-appearance: none;
|
|
||||||
appearance: none;
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
float: right;
|
|
||||||
margin: 5px 0px;
|
|
||||||
padding: 0px 24px;
|
|
||||||
background-image: none;
|
|
||||||
-ms-word-break: normal;
|
|
||||||
word-break: normal;
|
|
||||||
padding-right: 3rem;
|
|
||||||
padding-left: 1.2rem;
|
|
||||||
color: $color-gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
select:hover {
|
|
||||||
box-shadow: none;
|
|
||||||
color: $color-gray;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a.modal-link.icon-link {
|
a.modal-link.icon-link {
|
||||||
float: right;
|
float: right;
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
.usa-button,
|
.usa-button,
|
||||||
a {
|
a {
|
||||||
margin: 0 0 0 $gap;
|
margin: 0 0 0 $gap;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
@include media($medium-screen) {
|
@include media($medium-screen) {
|
||||||
margin: 0 0 0 ($gap * 2);
|
margin: 0 0 0 ($gap * 2);
|
||||||
|
@ -21,24 +21,8 @@ table.atat-table {
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
&--align-center {
|
&--third {
|
||||||
text-align: center;
|
width: 33%;
|
||||||
}
|
|
||||||
|
|
||||||
&--shrink {
|
|
||||||
width: 1%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--expand {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--hide-small {
|
|
||||||
display: none;
|
|
||||||
|
|
||||||
@include media($medium-screen) {
|
|
||||||
display: table-cell;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,28 +67,6 @@ table.atat-table {
|
|||||||
|
|
||||||
@include panel-margin;
|
@include panel-margin;
|
||||||
|
|
||||||
&__header {
|
|
||||||
@include panel-base;
|
|
||||||
@include panel-theme-default;
|
|
||||||
|
|
||||||
border-top: none;
|
|
||||||
border-bottom: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: $gap * 2;
|
|
||||||
|
|
||||||
&__title {
|
|
||||||
@include h4;
|
|
||||||
|
|
||||||
font-size: $lead-font-size;
|
|
||||||
justify-content: space-between;
|
|
||||||
flex: 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
table {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
@ -23,66 +23,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#modal--add-app-mem,
|
|
||||||
.form-content--app-mem {
|
|
||||||
text-align: left;
|
|
||||||
|
|
||||||
.modal__body {
|
|
||||||
min-width: 75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="checkbox"] + label::before {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input__inline-fields {
|
|
||||||
text-align: left;
|
|
||||||
|
|
||||||
.usa-input__choices label {
|
|
||||||
font-weight: $font-bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.input__inline-fields {
|
|
||||||
padding: $gap * 2;
|
|
||||||
border: 1px solid $color-gray-lighter;
|
|
||||||
|
|
||||||
&.checked {
|
|
||||||
border: 1px solid $color-blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
font-weight: $font-bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.usa-input__help {
|
|
||||||
margin-bottom: 0;
|
|
||||||
padding-left: 3rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.application-member__user-info {
|
|
||||||
.usa-input {
|
|
||||||
width: 45rem;
|
|
||||||
|
|
||||||
input,
|
|
||||||
label,
|
|
||||||
.usa-input__message {
|
|
||||||
max-width: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
label .icon-validation {
|
|
||||||
left: unset;
|
|
||||||
right: -$gap * 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--validation--phoneExt {
|
|
||||||
width: 18rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-roles {
|
.environment-roles {
|
||||||
padding: 0 ($gap * 3) ($gap * 3);
|
padding: 0 ($gap * 3) ($gap * 3);
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@
|
|||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro InfoFields(member_form) %}
|
{% macro InfoFields(member_form) %}
|
||||||
<div class="application-member__user-info">
|
<div class="user-info">
|
||||||
{{ TextInput(member_form.first_name, validation='requiredField', optional=False) }}
|
{{ TextInput(member_form.first_name, validation='requiredField', optional=False) }}
|
||||||
{{ TextInput(member_form.last_name, validation='requiredField', optional=False) }}
|
{{ TextInput(member_form.last_name, validation='requiredField', optional=False) }}
|
||||||
{{ TextInput(member_form.email, validation='email', optional=False) }}
|
{{ TextInput(member_form.email, validation='email', optional=False) }}
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
{% from "components/alert.html" import Alert %}
|
|
||||||
{% from "components/icon.html" import Icon %}
|
{% from "components/icon.html" import Icon %}
|
||||||
{% from "components/label.html" import Label %}
|
{% from "components/label.html" import Label %}
|
||||||
{% import "applications/fragments/new_member_modal_content.html" as member_steps %}
|
{% import "components/member_form.html" as member_form %}
|
||||||
{% import "applications/fragments/member_form_fields.html" as member_fields %}
|
{% import "applications/fragments/member_form_fields.html" as member_fields %}
|
||||||
{% from "components/modal.html" import Modal %}
|
{% from "components/modal.html" import Modal %}
|
||||||
{% from "components/multi_step_modal_form.html" import MultiStepModalForm %}
|
{% from "components/multi_step_modal_form.html" import MultiStepModalForm %}
|
||||||
{% from "components/save_button.html" import SaveButton %}
|
{% from "components/save_button.html" import SaveButton %}
|
||||||
{% from "components/toggle_list.html" import ToggleButton, ToggleSection %}
|
|
||||||
|
|
||||||
{% macro MemberManagementTemplate(
|
{% macro MemberManagementTemplate(
|
||||||
application,
|
application,
|
||||||
@ -179,8 +177,19 @@
|
|||||||
form=new_member_form,
|
form=new_member_form,
|
||||||
form_action=url_for(action_new, application_id=application.id),
|
form_action=url_for(action_new, application_id=application.id),
|
||||||
steps=[
|
steps=[
|
||||||
member_steps.MemberStepOne(new_member_form),
|
member_form.BasicStep(
|
||||||
member_steps.MemberStepTwo(new_member_form, application)
|
title="portfolios.applications.members.form.add_member"|translate,
|
||||||
|
form=member_fields.InfoFields(new_member_form.user_data),
|
||||||
|
next_button_text="portfolios.applications.members.form.next_button"|translate,
|
||||||
|
previous=False,
|
||||||
|
modal=new_member_modal_name,
|
||||||
|
),
|
||||||
|
member_form.SubmitStep(
|
||||||
|
name=new_member_modal_name,
|
||||||
|
form=member_fields.PermsFields(form=new_member_form, new=True),
|
||||||
|
submit_text="portfolios.applications.members.form.add_member"|translate,
|
||||||
|
modal=new_member_modal_name,
|
||||||
|
)
|
||||||
],
|
],
|
||||||
) }}
|
) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
{% from "components/icon.html" import Icon %}
|
|
||||||
{% import "applications/fragments/member_form_fields.html" as member_fields %}
|
|
||||||
|
|
||||||
{% macro MemberFormTemplate(title=None, next_button=None, previous=True) %}
|
|
||||||
<hr class="full-width">
|
|
||||||
{% if title %} <h1>{{ title }}</h1> {% endif %}
|
|
||||||
|
|
||||||
{{ caller() }}
|
|
||||||
|
|
||||||
<div class='action-group'>
|
|
||||||
{{ next_button }}
|
|
||||||
{% if previous %}
|
|
||||||
<input
|
|
||||||
type='button'
|
|
||||||
v-on:click="previous()"
|
|
||||||
class='action-group__action usa-button usa-button-secondary'
|
|
||||||
value='{{ "common.previous" | translate }}'>
|
|
||||||
{% endif %}
|
|
||||||
<a class='action-group__action' v-on:click="closeModal('{{ new_port_mem }}')">{{ "common.cancel" | translate }}</a>
|
|
||||||
</div>
|
|
||||||
{% endmacro %}
|
|
||||||
|
|
||||||
{% macro MemberStepOne(member_form) %}
|
|
||||||
{% set next_button %}
|
|
||||||
<input
|
|
||||||
type='button'
|
|
||||||
v-on:click="next()"
|
|
||||||
v-bind:disabled="!canSave"
|
|
||||||
class='action-group__action usa-button'
|
|
||||||
value='{{ "portfolios.applications.members.form.next_button" | translate }}'>
|
|
||||||
{% endset %}
|
|
||||||
|
|
||||||
{% call MemberFormTemplate(title="portfolios.applications.members.form.add_member"|translate, next_button=next_button, previous=False) %}
|
|
||||||
{{ member_fields.InfoFields(member_form.user_data) }}
|
|
||||||
{% endcall %}
|
|
||||||
{% endmacro %}
|
|
||||||
{% macro MemberStepTwo(member_form, application) %}
|
|
||||||
{% set next_button %}
|
|
||||||
<input
|
|
||||||
type="submit"
|
|
||||||
class='action-group__action usa-button'
|
|
||||||
form="add-app-mem"
|
|
||||||
v-bind:disabled="!canSave"
|
|
||||||
value='{{ "portfolios.applications.members.form.add_member" | translate}}'>
|
|
||||||
{% endset %}
|
|
||||||
|
|
||||||
{% call MemberFormTemplate(next_button=next_button) %}
|
|
||||||
{{ member_fields.PermsFields(form=member_form, new=True) }}
|
|
||||||
{% endcall %}
|
|
||||||
{% endmacro %}
|
|
@ -9,11 +9,15 @@
|
|||||||
"text": "changes pending",
|
"text": "changes pending",
|
||||||
"color": "default",
|
"color": "default",
|
||||||
},
|
},
|
||||||
|
"ppoc": {"text": "primary point of contact"}
|
||||||
} %}
|
} %}
|
||||||
|
|
||||||
{% if type -%}
|
{% if type in label_info.keys() -%}
|
||||||
<span class='label label--{{ label_info[type]["color"] }} {{ classes }}'>
|
<span class='label label--{{ label_info[type]["color"] }} {{ classes }}'>
|
||||||
{{ Icon(label_info[type]["icon"]) }} {{ label_info[type]["text"] }}
|
{% if label_info[type]["icon"] %}
|
||||||
|
{{ Icon(label_info[type]["icon"]) }}
|
||||||
|
{% endif %}
|
||||||
|
{{ label_info[type]["text"] }}
|
||||||
</span>
|
</span>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
65
templates/components/member_form.html
Normal file
65
templates/components/member_form.html
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<!-- Layout macro -->
|
||||||
|
{% macro MemberForm(title=None, next_button=None, previous=True, modal=modal) %}
|
||||||
|
<div class="member-form">
|
||||||
|
<hr class="full-width">
|
||||||
|
{% if title %} <h2>{{ title }}</h2> {% endif %}
|
||||||
|
|
||||||
|
{{ caller() }}
|
||||||
|
</div>
|
||||||
|
<div class='action-group'>
|
||||||
|
{{ next_button }}
|
||||||
|
{% if previous %}
|
||||||
|
<input
|
||||||
|
type='button'
|
||||||
|
v-on:click="previous()"
|
||||||
|
class='action-group__action usa-button usa-button-secondary'
|
||||||
|
value='{{ "common.previous" | translate }}'>
|
||||||
|
{% endif %}
|
||||||
|
<a class='action-group__action' v-on:click="closeModal('{{ modal }}')">{{ "common.cancel" | translate }}</a>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Step macros to use with MultiStepModalForm -->
|
||||||
|
{% macro BasicStep(
|
||||||
|
title=None,
|
||||||
|
form=form,
|
||||||
|
next_button_text=next_button_text,
|
||||||
|
previous=True,
|
||||||
|
modal=modal
|
||||||
|
) %}
|
||||||
|
{% set next_button %}
|
||||||
|
<input
|
||||||
|
type='button'
|
||||||
|
v-on:click="next()"
|
||||||
|
v-bind:disabled="!canSave"
|
||||||
|
class='action-group__action usa-button'
|
||||||
|
value='{{ next_button_text }}'>
|
||||||
|
{% endset %}
|
||||||
|
|
||||||
|
{% call MemberForm(title=title, next_button=next_button, previous=previous, modal=modal) %}
|
||||||
|
{{ form }}
|
||||||
|
{% endcall %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro SubmitStep(
|
||||||
|
name=name,
|
||||||
|
title=None,
|
||||||
|
form=form,
|
||||||
|
submit_text=submit_text,
|
||||||
|
previous=True,
|
||||||
|
modal=modal
|
||||||
|
) %}
|
||||||
|
{% set next_button %}
|
||||||
|
<input
|
||||||
|
type="submit"
|
||||||
|
class='action-group__action usa-button'
|
||||||
|
form="{{ name }}"
|
||||||
|
v-bind:disabled="!canSave"
|
||||||
|
value='{{ submit_text }}'>
|
||||||
|
{% endset %}
|
||||||
|
|
||||||
|
{% call MemberForm(title=title, next_button=next_button, previous=previous, modal=modal) %}
|
||||||
|
{{ form }}
|
||||||
|
{% endcall %}
|
||||||
|
{% endmacro %}
|
@ -1,5 +1,6 @@
|
|||||||
{% extends "portfolios/base.html" %}
|
{% extends "portfolios/base.html" %}
|
||||||
|
|
||||||
|
{% from "components/label.html" import Label %}
|
||||||
{% from "components/pagination.html" import Pagination %}
|
{% from "components/pagination.html" import Pagination %}
|
||||||
{% from 'components/save_button.html' import SaveButton %}
|
{% from 'components/save_button.html' import SaveButton %}
|
||||||
{% from 'components/sticky_cta.html' import StickyCTA %}
|
{% from 'components/sticky_cta.html' import StickyCTA %}
|
||||||
@ -9,8 +10,8 @@
|
|||||||
|
|
||||||
{{ StickyCTA(text="Settings") }}
|
{{ StickyCTA(text="Settings") }}
|
||||||
|
|
||||||
<div v-cloak class="portfolio-admin portfolio-content">
|
<div v-cloak class="portfolio-admin">
|
||||||
|
{% include "fragments/flash.html" %}
|
||||||
<!-- max width of this section is 460px -->
|
<!-- max width of this section is 460px -->
|
||||||
<section class="form-container__half">
|
<section class="form-container__half">
|
||||||
<h3>Portfolio name and component</h3>
|
<h3>Portfolio name and component</h3>
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
{% from "components/icon.html" import Icon %}
|
|
||||||
{% from "components/text_input.html" import TextInput %}
|
|
||||||
{% from "components/multi_step_modal_form.html" import MultiStepModalForm %}
|
|
||||||
|
|
||||||
{% macro SimpleOptionsInput(field) %}
|
|
||||||
<div class="usa-input">
|
|
||||||
<fieldset data-ally-disabled="true" class="usa-input__choices">
|
|
||||||
<legend>
|
|
||||||
<div class="usa-input__title-inline">
|
|
||||||
{{ field.label | striptags}}
|
|
||||||
</div>
|
|
||||||
</legend>
|
|
||||||
{{ field() }}
|
|
||||||
</fieldset>
|
|
||||||
</div>
|
|
||||||
{% endmacro %}
|
|
||||||
|
|
||||||
{% set step_one %}
|
|
||||||
<hr class="full-width">
|
|
||||||
<h1>Invite new portfolio member</h1>
|
|
||||||
<div class='form-row'>
|
|
||||||
<div class='form-col form-col--half'>
|
|
||||||
{{ TextInput(member_form.user_data.first_name, validation='requiredField', optional=False) }}
|
|
||||||
</div>
|
|
||||||
<div class='form-col form-col--half'>
|
|
||||||
{{ TextInput(member_form.user_data.last_name, validation='requiredField', optional=False) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class='form-row'>
|
|
||||||
<div class='form-col form-col--half'>
|
|
||||||
{{ TextInput(member_form.user_data.email, validation='email', optional=False) }}
|
|
||||||
</div>
|
|
||||||
<div class='form-col form-col--half'>
|
|
||||||
{{ TextInput(member_form.user_data.phone_number, validation='usPhone') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class='form-row'>
|
|
||||||
<div class='form-col form-col--half'>
|
|
||||||
{{ TextInput(member_form.user_data.dod_id, validation='dodId', optional=False) }}
|
|
||||||
</div>
|
|
||||||
<div class='form-col form-col--half'>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class='action-group'>
|
|
||||||
<input
|
|
||||||
type='button'
|
|
||||||
v-on:click="next()"
|
|
||||||
v-bind:disabled="!canSave"
|
|
||||||
class='action-group__action usa-button usa-button-primary'
|
|
||||||
value='Next'>
|
|
||||||
<a class='action-group__action' v-on:click="closeModal('{{ new_port_mem }}')">Cancel</a>
|
|
||||||
</div>
|
|
||||||
{% endset %}
|
|
||||||
{% set step_two %}
|
|
||||||
<hr class="full-width">
|
|
||||||
<h1>Assign member permissions</h1>
|
|
||||||
<a class='icon-link'>
|
|
||||||
{{ Icon('info') }}
|
|
||||||
{{ "portfolios.admin.permissions_info" | translate }}
|
|
||||||
</a>
|
|
||||||
{{ SimpleOptionsInput(member_form.permission_sets.perms_app_mgmt) }}
|
|
||||||
{{ SimpleOptionsInput(member_form.permission_sets.perms_funding) }}
|
|
||||||
{{ SimpleOptionsInput(member_form.permission_sets.perms_reporting) }}
|
|
||||||
{{ SimpleOptionsInput(member_form.permission_sets.perms_portfolio_mgmt) }}
|
|
||||||
<div class='action-group'>
|
|
||||||
<input
|
|
||||||
type="submit"
|
|
||||||
class='action-group__action usa-button usa-button-primary'
|
|
||||||
form="add-port-mem"
|
|
||||||
value='Invite member'>
|
|
||||||
<a class='action-group__action' v-on:click="closeModal('{{ new_port_mem }}')">Cancel</a>
|
|
||||||
</div>
|
|
||||||
{% endset %}
|
|
||||||
{{ MultiStepModalForm(
|
|
||||||
'add-port-mem',
|
|
||||||
member_form,
|
|
||||||
url_for("portfolios.invite_member", portfolio_id=portfolio.id),
|
|
||||||
[step_one, step_two],
|
|
||||||
) }}
|
|
37
templates/portfolios/fragments/member_form_fields.html
Normal file
37
templates/portfolios/fragments/member_form_fields.html
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{% from "components/checkbox_input.html" import CheckboxInput %}
|
||||||
|
{% from "components/icon.html" import Icon %}
|
||||||
|
{% from "components/phone_input.html" import PhoneInput %}
|
||||||
|
{% from "components/text_input.html" import TextInput %}
|
||||||
|
|
||||||
|
{% macro PermsFields(form, member_role_id=None) %}
|
||||||
|
<h2>Set Portfolio Permissions</h2>
|
||||||
|
<div class="portfolio-perms">
|
||||||
|
{% if new %}
|
||||||
|
{% set app_mgmt = form.perms_app_mgmt.name %}
|
||||||
|
{% set funding = form.perms_funding.name %}
|
||||||
|
{% set reporting = form.perms_reporting.name %}
|
||||||
|
{% set portfolio_mgmt = form.perms_portfolio_mgmt.name %}
|
||||||
|
{% else %}
|
||||||
|
{% set app_mgmt = "perms_app_mgmt-{}".format(member_role_id) %}
|
||||||
|
{% set funding = "perms_funding-{}".format(member_role_id) %}
|
||||||
|
{% set reporting = "perms_reporting-{}".format(member_role_id) %}
|
||||||
|
{% set portfolio_mgmt = "perms_portfolio_mgmt-{}".format(member_role_id) %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{{ CheckboxInput(form.perms_app_mgmt, classes="input__inline-fields", key=app_mgmt, id=app_mgmt, optional=True) }}
|
||||||
|
{{ CheckboxInput(form.perms_funding, classes="input__inline-fields", key=funding, id=funding, optional=True) }}
|
||||||
|
{{ CheckboxInput(form.perms_reporting, classes="input__inline-fields", key=reporting, id=reporting, optional=True) }}
|
||||||
|
{{ CheckboxInput(form.perms_portfolio_mgmt, classes="input__inline-fields", key=portfolio_mgmt, id=portfolio_mgmt, optional=True) }}
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro InfoFields(member_form) %}
|
||||||
|
<div class="user-info">
|
||||||
|
{{ TextInput(member_form.first_name, validation='requiredField', optional=False) }}
|
||||||
|
{{ TextInput(member_form.last_name, validation='requiredField', optional=False) }}
|
||||||
|
{{ TextInput(member_form.email, validation='email', optional=False) }}
|
||||||
|
{{ PhoneInput(member_form.phone_number, member_form.phone_ext)}}
|
||||||
|
{{ TextInput(member_form.dod_id, validation='dodId', optional=False) }}
|
||||||
|
<a href="#">How do I find the DoD ID?</a>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
@ -1,38 +0,0 @@
|
|||||||
{% from "components/alert.html" import Alert %}
|
|
||||||
{% from "components/modal.html" import Modal %}
|
|
||||||
{% from "components/options_input.html" import OptionsInput %}
|
|
||||||
|
|
||||||
{% for subform in member_perms_form.members_permissions %}
|
|
||||||
{% set modal_id = "portfolio_id_{}_user_id_{}".format(portfolio.id, subform.member_id.data) %}
|
|
||||||
{% set ppoc = subform.member_id.data == ppoc_id %}
|
|
||||||
{% set archive_button_class = 'button-danger-outline' %}
|
|
||||||
|
|
||||||
<tr {% if ppoc %}class="members-table-ppoc"{% endif %}>
|
|
||||||
<td class='name'>{{ subform.member_name.data }}
|
|
||||||
<div>
|
|
||||||
{% if ppoc %}
|
|
||||||
{% set archive_button_class = 'usa-button-disabled' %}
|
|
||||||
<span class='you'>PPoC</span>
|
|
||||||
{% endif %}
|
|
||||||
{% if subform.member_id.data == current_member_id %}
|
|
||||||
{% set archive_button_class = 'usa-button-disabled' %}
|
|
||||||
<span class='you'>(<span class='green'>you</span>)</span>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>{{ OptionsInput(subform.perms_app_mgmt, label=False, disabled=ppoc) }}</td>
|
|
||||||
<td>{{ OptionsInput(subform.perms_funding, label=False, disabled=ppoc) }}</td>
|
|
||||||
<td>{{ OptionsInput(subform.perms_reporting, label=False, disabled=ppoc) }}</td>
|
|
||||||
<td>{{ OptionsInput(subform.perms_portfolio_mgmt, label=False, disabled=ppoc) }}</td>
|
|
||||||
|
|
||||||
<td>
|
|
||||||
<a v-on:click="openModal('{{ modal_id }}')" class='usa-button {{ archive_button_class }}'>
|
|
||||||
{{ "portfolios.members.archive_button" | translate }}
|
|
||||||
</a>
|
|
||||||
{% if not ppoc %}
|
|
||||||
{{ subform.member_id() }}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
@ -1,26 +0,0 @@
|
|||||||
{% for subform in member_perms_form.members_permissions %}
|
|
||||||
{% set ppoc = subform.member_id.data == ppoc_id %}
|
|
||||||
{% set heading_perms = [subform.perms_app_mgmt, subform.perms_funding, subform.perms_reporting, subform.perms_portfolio_mgmt] %}
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td class='name'>{{ subform.member_name.data }}
|
|
||||||
<div>
|
|
||||||
{% if ppoc %}
|
|
||||||
<span class='you'>PPoC</span>
|
|
||||||
{% endif %}
|
|
||||||
{% if subform.member_id.data == current_member_id %}
|
|
||||||
<span class='you'>(<span class='green'>you</span>)</span>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{% for access in heading_perms %}
|
|
||||||
{% if dict(access.choices).get(access.data) == ('portfolios.members.permissions.edit_access' | translate) %}
|
|
||||||
<td class='green'>{{ 'portfolios.members.permissions.edit_access' | translate }}</td>
|
|
||||||
{% else %}
|
|
||||||
<td>{{ 'common.view' | translate }}</td>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
@ -1,105 +1,74 @@
|
|||||||
{% from "components/icon.html" import Icon %}
|
|
||||||
{% from 'components/save_button.html' import SaveButton %}
|
|
||||||
{% from "components/modal.html" import Modal %}
|
|
||||||
{% from "components/alert.html" import Alert %}
|
{% from "components/alert.html" import Alert %}
|
||||||
|
{% from "components/icon.html" import Icon %}
|
||||||
|
{% import "components/member_form.html" as member_form %}
|
||||||
|
{% from "components/modal.html" import Modal %}
|
||||||
|
{% from "components/multi_step_modal_form.html" import MultiStepModalForm %}
|
||||||
|
{% from 'components/save_button.html' import SaveButton %}
|
||||||
|
{% import "portfolios/fragments/member_form_fields.html" as member_form_fields %}
|
||||||
|
|
||||||
<section class="member-list" id="portfolio-members">
|
<h3>Portfolio Managers</h3>
|
||||||
<div class='responsive-table-wrapper panel accordion-table'>
|
<div class="panel">
|
||||||
{% if g.matchesPath("portfolio-members") %}
|
<section class="member-list">
|
||||||
{% include "fragments/flash.html" %}
|
<div class="responsive-table-wrapper">
|
||||||
{% endif %}
|
<table class="atat-table">
|
||||||
<base-form inline-template>
|
<thead>
|
||||||
<form method='POST' id="member-perms" action='{{ url_for("portfolios.edit_members", portfolio_id=portfolio.id) }}' autocomplete="off" enctype="multipart/form-data">
|
<tr>
|
||||||
{{ member_perms_form.csrf_token }}
|
<th class="table-cell--third">Name</th>
|
||||||
|
<th>Portfolio Permissions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for member in members -%}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<strong>{{ member.user_name }}{% if member.role_id == current_member_id %} (You){% endif %}</strong>
|
||||||
|
<br>
|
||||||
|
{% if member.ppoc %}
|
||||||
|
{{ Label(type="ppoc", classes='label--below label--purple')}}
|
||||||
|
{% endif %}
|
||||||
|
{{ Label(type=member.status, classes='label--below')}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% for perm, value in member.permission_sets.items() -%}
|
||||||
|
<div>
|
||||||
|
{% if value -%}
|
||||||
|
{{ ("portfolios.admin.members.{}.{}".format(perm, value)) | translate }}
|
||||||
|
{%- endif %}
|
||||||
|
</div>
|
||||||
|
{%-endfor %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{%- endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<div class='application-list-item'>
|
|
||||||
<header>
|
|
||||||
<div class='responsive-table-wrapper__header'>
|
|
||||||
<div class='responsive-table-wrapper__title'>
|
|
||||||
<div class='h3'>{{ "portfolios.admin.portfolio_members_title" | translate }}</div>
|
|
||||||
<div class='subheading'>
|
|
||||||
{{ "portfolios.admin.portfolio_members_subheading" | translate }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a class='icon-link'>
|
|
||||||
{{ Icon('info') }}
|
|
||||||
{{ "portfolios.admin.settings_info" | translate }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
{% if not portfolio.members %}
|
|
||||||
<p>{{ "portfolios.admin.no_members" | translate }}</p>
|
|
||||||
{% else %}
|
|
||||||
<table class="atat-table">
|
|
||||||
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>{{ "portfolios.members.permissions.name" | translate }}</td>
|
|
||||||
<td>{{ "portfolios.members.permissions.app_mgmt" | translate }}</td>
|
|
||||||
<td>{{ "portfolios.members.permissions.funding" | translate }}</td>
|
|
||||||
<td>{{ "portfolios.members.permissions.reporting" | translate }}</td>
|
|
||||||
<td>{{ "portfolios.members.permissions.portfolio_mgmt" | translate }}</td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
{% if user_can(permissions.EDIT_PORTFOLIO_USERS) %}
|
|
||||||
{% include "portfolios/fragments/members_edit.html" %}
|
|
||||||
{% elif user_can(permissions.VIEW_PORTFOLIO_USERS) %}
|
|
||||||
{% include "portfolios/fragments/members_view.html" %}
|
|
||||||
{% endif %}
|
|
||||||
</tbody>
|
|
||||||
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div class="panel__footer">
|
|
||||||
<div class="action-group save">
|
|
||||||
{% if user_can(permissions.EDIT_PORTFOLIO_USERS) %}
|
|
||||||
{{ SaveButton(text=('common.save' | translate), element="input", form="member-perms") }}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if user_can(permissions.CREATE_PORTFOLIO_USERS) %}
|
|
||||||
<a class="icon-link modal-link" v-on:click="openModal('add-port-mem')">
|
|
||||||
{{ "portfolios.admin.add_new_member" | translate }}
|
|
||||||
{{ Icon("plus") }}
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</base-form>
|
|
||||||
{% if user_can(permissions.EDIT_PORTFOLIO_USERS) %}
|
|
||||||
{% for subform in member_perms_form.members_permissions %}
|
|
||||||
{% set modal_id = "portfolio_id_{}_user_id_{}".format(portfolio.id, subform.member_id.data) %}
|
|
||||||
{% call Modal(name=modal_id, dismissable=False) %}
|
|
||||||
<h1>{{ "portfolios.admin.alert_header" | translate }}</h1>
|
|
||||||
<hr>
|
|
||||||
{{
|
|
||||||
Alert(
|
|
||||||
title="portfolios.admin.alert_title" | translate,
|
|
||||||
message="portfolios.admin.alert_message" | translate,
|
|
||||||
level="warning"
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
<div class="action-group">
|
|
||||||
<form method="POST" action="{{ url_for('portfolios.remove_member', portfolio_id=portfolio.id, portfolio_role_id=subform.member_id.data)}}">
|
|
||||||
{{ member_perms_form.csrf_token }}
|
|
||||||
<button class="usa-button usa-button-danger">
|
|
||||||
{{ "portfolios.members.archive_button" | translate }}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
<a v-on:click="closeModal('{{ modal_id }}')" class="action-group__action icon-link icon-link--default">{{ "common.cancel" | translate }}</a>
|
|
||||||
</div>
|
|
||||||
{% endcall %}
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% if user_can(permissions.CREATE_PORTFOLIO_USERS) %}
|
{% if user_can(permissions.CREATE_PORTFOLIO_USERS) %}
|
||||||
{% include "portfolios/fragments/add_new_portfolio_member.html" %}
|
{% set new_manager_modal = "add-portfolio-manager" %}
|
||||||
|
<a class="usa-button usa-button-secondary add-new-button" v-on:click="openModal('{{ new_manager_modal }}')">
|
||||||
|
Add Portfolio Manager
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{{ MultiStepModalForm(
|
||||||
|
name=new_manager_modal,
|
||||||
|
form=new_manager_form,
|
||||||
|
form_action=url_for("portfolios.invite_member", portfolio_id=portfolio.id),
|
||||||
|
steps=[
|
||||||
|
member_form.BasicStep(
|
||||||
|
title="Add Manager",
|
||||||
|
form=member_form_fields.InfoFields(new_manager_form.user_data),
|
||||||
|
next_button_text="Next: Permissions",
|
||||||
|
previous=False,
|
||||||
|
modal=new_manager_modal_name,
|
||||||
|
),
|
||||||
|
member_form.SubmitStep(
|
||||||
|
name=new_manager_modal,
|
||||||
|
form=member_form_fields.PermsFields(new_manager_form),
|
||||||
|
submit_text="Add Mananger",
|
||||||
|
modal=new_manager_modal_name,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</section>
|
</div>
|
||||||
|
@ -205,7 +205,7 @@ def test_invite():
|
|||||||
inviter = UserFactory.create()
|
inviter = UserFactory.create()
|
||||||
member_data = UserFactory.dictionary()
|
member_data = UserFactory.dictionary()
|
||||||
|
|
||||||
invitation = Portfolios.invite(portfolio, inviter, member_data)
|
invitation = Portfolios.invite(portfolio, inviter, {"user_data": member_data})
|
||||||
|
|
||||||
assert invitation.role
|
assert invitation.role
|
||||||
assert invitation.role.portfolio == portfolio
|
assert invitation.role.portfolio == portfolio
|
||||||
|
@ -151,12 +151,12 @@ def test_event_details():
|
|||||||
|
|
||||||
def test_status_when_member_is_active():
|
def test_status_when_member_is_active():
|
||||||
portfolio_role = PortfolioRoleFactory.create(status=PortfolioRoleStatus.ACTIVE)
|
portfolio_role = PortfolioRoleFactory.create(status=PortfolioRoleStatus.ACTIVE)
|
||||||
assert portfolio_role.display_status == "Active"
|
assert portfolio_role.display_status == "active"
|
||||||
|
|
||||||
|
|
||||||
def test_status_when_member_is_disabled():
|
def test_status_when_member_is_disabled():
|
||||||
portfolio_role = PortfolioRoleFactory.create(status=PortfolioRoleStatus.DISABLED)
|
portfolio_role = PortfolioRoleFactory.create(status=PortfolioRoleStatus.DISABLED)
|
||||||
assert portfolio_role.display_status == "Disabled"
|
assert portfolio_role.display_status == "disabled"
|
||||||
|
|
||||||
|
|
||||||
def test_status_when_invitation_has_been_rejected_for_expirations():
|
def test_status_when_invitation_has_been_rejected_for_expirations():
|
||||||
@ -168,7 +168,7 @@ def test_status_when_invitation_has_been_rejected_for_expirations():
|
|||||||
PortfolioInvitationFactory.create(
|
PortfolioInvitationFactory.create(
|
||||||
role=portfolio_role, status=InvitationStatus.REJECTED_EXPIRED
|
role=portfolio_role, status=InvitationStatus.REJECTED_EXPIRED
|
||||||
)
|
)
|
||||||
assert portfolio_role.display_status == "Invite expired"
|
assert portfolio_role.display_status == "invite_expired"
|
||||||
|
|
||||||
|
|
||||||
def test_status_when_invitation_has_been_rejected_for_wrong_user():
|
def test_status_when_invitation_has_been_rejected_for_wrong_user():
|
||||||
@ -180,7 +180,7 @@ def test_status_when_invitation_has_been_rejected_for_wrong_user():
|
|||||||
PortfolioInvitationFactory.create(
|
PortfolioInvitationFactory.create(
|
||||||
role=portfolio_role, status=InvitationStatus.REJECTED_WRONG_USER
|
role=portfolio_role, status=InvitationStatus.REJECTED_WRONG_USER
|
||||||
)
|
)
|
||||||
assert portfolio_role.display_status == "Error on invite"
|
assert portfolio_role.display_status == "invite_error"
|
||||||
|
|
||||||
|
|
||||||
def test_status_when_invitation_has_been_revoked():
|
def test_status_when_invitation_has_been_revoked():
|
||||||
@ -192,7 +192,7 @@ def test_status_when_invitation_has_been_revoked():
|
|||||||
PortfolioInvitationFactory.create(
|
PortfolioInvitationFactory.create(
|
||||||
role=portfolio_role, status=InvitationStatus.REVOKED
|
role=portfolio_role, status=InvitationStatus.REVOKED
|
||||||
)
|
)
|
||||||
assert portfolio_role.display_status == "Invite revoked"
|
assert portfolio_role.display_status == "invite_revoked"
|
||||||
|
|
||||||
|
|
||||||
def test_status_when_invitation_is_expired():
|
def test_status_when_invitation_is_expired():
|
||||||
@ -206,7 +206,7 @@ def test_status_when_invitation_is_expired():
|
|||||||
status=InvitationStatus.PENDING,
|
status=InvitationStatus.PENDING,
|
||||||
expiration_time=datetime.datetime.now() - datetime.timedelta(seconds=1),
|
expiration_time=datetime.datetime.now() - datetime.timedelta(seconds=1),
|
||||||
)
|
)
|
||||||
assert portfolio_role.display_status == "Invite expired"
|
assert portfolio_role.display_status == "invite_expired"
|
||||||
|
|
||||||
|
|
||||||
def test_can_not_resend_invitation_if_active():
|
def test_can_not_resend_invitation_if_active():
|
||||||
|
@ -34,138 +34,6 @@ def test_member_table_access(client, user_session):
|
|||||||
assert "<select" not in view_resp.data.decode()
|
assert "<select" not in view_resp.data.decode()
|
||||||
|
|
||||||
|
|
||||||
def test_update_member_permissions(client, user_session):
|
|
||||||
portfolio = PortfolioFactory.create()
|
|
||||||
rando = UserFactory.create()
|
|
||||||
rando_pf_role = PortfolioRoleFactory.create(
|
|
||||||
user=rando,
|
|
||||||
portfolio=portfolio,
|
|
||||||
permission_sets=[PermissionSets.get(PermissionSets.VIEW_PORTFOLIO_ADMIN)],
|
|
||||||
)
|
|
||||||
|
|
||||||
user = UserFactory.create()
|
|
||||||
PortfolioRoleFactory.create(
|
|
||||||
user=user,
|
|
||||||
portfolio=portfolio,
|
|
||||||
permission_sets=PermissionSets.get_many(
|
|
||||||
[PermissionSets.EDIT_PORTFOLIO_ADMIN, PermissionSets.VIEW_PORTFOLIO_ADMIN]
|
|
||||||
),
|
|
||||||
)
|
|
||||||
user_session(user)
|
|
||||||
|
|
||||||
form_data = {
|
|
||||||
"members_permissions-0-member_id": rando_pf_role.id,
|
|
||||||
"members_permissions-0-perms_app_mgmt": "edit_portfolio_application_management",
|
|
||||||
"members_permissions-0-perms_funding": "view_portfolio_funding",
|
|
||||||
"members_permissions-0-perms_reporting": "view_portfolio_reports",
|
|
||||||
"members_permissions-0-perms_portfolio_mgmt": "view_portfolio_admin",
|
|
||||||
}
|
|
||||||
|
|
||||||
response = client.post(
|
|
||||||
url_for("portfolios.edit_members", portfolio_id=portfolio.id),
|
|
||||||
data=form_data,
|
|
||||||
follow_redirects=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert rando_pf_role.has_permission_set(
|
|
||||||
PermissionSets.EDIT_PORTFOLIO_APPLICATION_MANAGEMENT
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_no_update_member_permissions_without_edit_access(client, user_session):
|
|
||||||
portfolio = PortfolioFactory.create()
|
|
||||||
rando = UserFactory.create()
|
|
||||||
rando_pf_role = PortfolioRoleFactory.create(
|
|
||||||
user=rando,
|
|
||||||
portfolio=portfolio,
|
|
||||||
permission_sets=[PermissionSets.get(PermissionSets.VIEW_PORTFOLIO_ADMIN)],
|
|
||||||
)
|
|
||||||
|
|
||||||
user = UserFactory.create()
|
|
||||||
PortfolioRoleFactory.create(
|
|
||||||
user=user,
|
|
||||||
portfolio=portfolio,
|
|
||||||
permission_sets=[PermissionSets.get(PermissionSets.VIEW_PORTFOLIO_ADMIN)],
|
|
||||||
)
|
|
||||||
user_session(user)
|
|
||||||
|
|
||||||
form_data = {
|
|
||||||
"members_permissions-0-member_id": rando_pf_role.id,
|
|
||||||
"members_permissions-0-perms_app_mgmt": "edit_portfolio_application_management",
|
|
||||||
"members_permissions-0-perms_funding": "view_portfolio_funding",
|
|
||||||
"members_permissions-0-perms_reporting": "view_portfolio_reports",
|
|
||||||
"members_permissions-0-perms_portfolio_mgmt": "view_portfolio_admin",
|
|
||||||
}
|
|
||||||
|
|
||||||
response = client.post(
|
|
||||||
url_for("portfolios.edit_members", portfolio_id=portfolio.id),
|
|
||||||
data=form_data,
|
|
||||||
follow_redirects=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 404
|
|
||||||
assert not rando_pf_role.has_permission_set(
|
|
||||||
PermissionSets.EDIT_PORTFOLIO_APPLICATION_MANAGEMENT
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_rerender_admin_page_if_member_perms_form_does_not_validate(
|
|
||||||
client, user_session, monkeypatch
|
|
||||||
):
|
|
||||||
portfolio = PortfolioFactory.create()
|
|
||||||
user = UserFactory.create()
|
|
||||||
role = PortfolioRoleFactory.create(
|
|
||||||
user=user,
|
|
||||||
portfolio=portfolio,
|
|
||||||
permission_sets=[PermissionSets.get(PermissionSets.EDIT_PORTFOLIO_ADMIN)],
|
|
||||||
)
|
|
||||||
user_session(user)
|
|
||||||
form_data = {
|
|
||||||
"members_permissions-0-member_id": role.id,
|
|
||||||
"members_permissions-0-perms_app_mgmt": "bad input",
|
|
||||||
"members_permissions-0-perms_funding": "view_portfolio_funding",
|
|
||||||
"members_permissions-0-perms_reporting": "view_portfolio_reports",
|
|
||||||
"members_permissions-0-perms_portfolio_mgmt": "view_portfolio_admin",
|
|
||||||
}
|
|
||||||
|
|
||||||
mock_route = MagicMock(return_value=("", 200, {}))
|
|
||||||
monkeypatch.setattr("atst.routes.portfolios.admin.render_admin_page", mock_route)
|
|
||||||
client.post(
|
|
||||||
url_for("portfolios.edit_members", portfolio_id=portfolio.id), data=form_data
|
|
||||||
)
|
|
||||||
mock_route.assert_called()
|
|
||||||
|
|
||||||
|
|
||||||
def test_cannot_update_portfolio_ppoc_perms(client, user_session):
|
|
||||||
portfolio = PortfolioFactory.create()
|
|
||||||
ppoc = portfolio.owner
|
|
||||||
ppoc_pf_role = PortfolioRoles.get(portfolio_id=portfolio.id, user_id=ppoc.id)
|
|
||||||
user = UserFactory.create()
|
|
||||||
PortfolioRoleFactory.create(portfolio=portfolio, user=user)
|
|
||||||
|
|
||||||
user_session(user)
|
|
||||||
|
|
||||||
assert ppoc_pf_role.has_permission_set(PermissionSets.PORTFOLIO_POC)
|
|
||||||
|
|
||||||
member_perms_data = {
|
|
||||||
"members_permissions-0-member_id": ppoc_pf_role.id,
|
|
||||||
"members_permissions-0-perms_app_mgmt": "view_portfolio_application_management",
|
|
||||||
"members_permissions-0-perms_funding": "view_portfolio_funding",
|
|
||||||
"members_permissions-0-perms_reporting": "view_portfolio_reports",
|
|
||||||
"members_permissions-0-perms_portfolio_mgmt": "view_portfolio_admin",
|
|
||||||
}
|
|
||||||
|
|
||||||
response = client.post(
|
|
||||||
url_for("portfolios.edit_members", portfolio_id=portfolio.id),
|
|
||||||
data=member_perms_data,
|
|
||||||
follow_redirects=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert response.status_code == 404
|
|
||||||
assert ppoc_pf_role.has_permission_set(PermissionSets.PORTFOLIO_POC)
|
|
||||||
|
|
||||||
|
|
||||||
def test_update_portfolio_name_and_description(client, user_session):
|
def test_update_portfolio_name_and_description(client, user_session):
|
||||||
portfolio = PortfolioFactory.create()
|
portfolio = PortfolioFactory.create()
|
||||||
user_session(portfolio.owner)
|
user_session(portfolio.owner)
|
||||||
|
@ -269,10 +269,10 @@ def test_existing_member_invite_resent_to_email_submitted_in_form(
|
|||||||
|
|
||||||
|
|
||||||
_DEFAULT_PERMS_FORM_DATA = {
|
_DEFAULT_PERMS_FORM_DATA = {
|
||||||
"permission_sets-perms_app_mgmt": PermissionSets.VIEW_PORTFOLIO_APPLICATION_MANAGEMENT,
|
"permission_sets-perms_app_mgmt": False,
|
||||||
"permission_sets-perms_funding": PermissionSets.VIEW_PORTFOLIO_FUNDING,
|
"permission_sets-perms_funding": False,
|
||||||
"permission_sets-perms_reporting": PermissionSets.VIEW_PORTFOLIO_REPORTS,
|
"permission_sets-perms_reporting": False,
|
||||||
"permission_sets-perms_portfolio_mgmt": PermissionSets.VIEW_PORTFOLIO_ADMIN,
|
"permission_sets-perms_portfolio_mgmt": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -161,15 +161,23 @@ forms:
|
|||||||
phone_number_label: Phone number
|
phone_number_label: Phone number
|
||||||
service_branch_label: Service branch or agency
|
service_branch_label: Service branch or agency
|
||||||
new_member:
|
new_member:
|
||||||
app_mgmt: App management
|
app_mgmt:
|
||||||
|
label: Edit Applications
|
||||||
|
description: Add, remove and edit applications in this Portfolio.
|
||||||
dod_id_label: DoD ID
|
dod_id_label: DoD ID
|
||||||
email_label: Email address
|
email_label: Email address
|
||||||
first_name_label: First name
|
first_name_label: First name
|
||||||
funding: Funding
|
funding:
|
||||||
|
label: Edit Funding
|
||||||
|
description: Add and Modify Task Orders to fund this Portfolio.
|
||||||
last_name_label: Last name
|
last_name_label: Last name
|
||||||
phone_number_label: Phone number
|
phone_number_label: Phone number
|
||||||
portfolio_mgmt: Portfolio management
|
portfolio_mgmt:
|
||||||
reporting: Reporting
|
label: Edit Portfolio
|
||||||
|
description: "Edit this Portfolio's settings."
|
||||||
|
reporting:
|
||||||
|
label: Edit Reporting
|
||||||
|
description: "View and export reports about this Portfolio's funding."
|
||||||
portfolio:
|
portfolio:
|
||||||
name:
|
name:
|
||||||
label: Portfolio Name
|
label: Portfolio Name
|
||||||
@ -317,6 +325,19 @@ portfolios:
|
|||||||
portfolio_members_title: Portfolio members
|
portfolio_members_title: Portfolio members
|
||||||
settings_info: Learn more about these settings
|
settings_info: Learn more about these settings
|
||||||
portfolio_name: Portfolio name
|
portfolio_name: Portfolio name
|
||||||
|
members:
|
||||||
|
perms_portfolio_mgmt:
|
||||||
|
'False': View Portfolio
|
||||||
|
'True': Edit Portfolio
|
||||||
|
perms_app_mgmt:
|
||||||
|
'False': View Applications
|
||||||
|
'True': Edit Applications
|
||||||
|
perms_funding:
|
||||||
|
'False': View Funding
|
||||||
|
'True': Edit Funding
|
||||||
|
perms_reporting:
|
||||||
|
'False': View Reporting
|
||||||
|
'True': Edit Reporting
|
||||||
applications:
|
applications:
|
||||||
add_application_text: Add a new application
|
add_application_text: Add a new application
|
||||||
add_environment: Create an Environment
|
add_environment: Create an Environment
|
||||||
|
Loading…
x
Reference in New Issue
Block a user