Merge pull request #611 from dod-ccpo/reskin-portfolio-nav
Reskin portfolio navigation
This commit is contained in:
commit
f19191c6d2
1
static/icons/chart-pie.svg
Normal file
1
static/icons/chart-pie.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 544 512"><path d="M527.79 288H290.5l158.03 158.03c6.04 6.04 15.98 6.53 22.19.68 38.7-36.46 65.32-85.61 73.13-140.86 1.34-9.46-6.51-17.85-16.06-17.85zm-15.83-64.8C503.72 103.74 408.26 8.28 288.8.04 279.68-.59 272 7.1 272 16.24V240h223.77c9.14 0 16.82-7.68 16.19-16.8zM224 288V50.71c0-9.55-8.39-17.4-17.84-16.06C86.99 51.49-4.1 155.6.14 280.37 4.5 408.51 114.83 513.59 243.03 511.98c50.4-.63 96.97-16.87 135.26-44.03 7.9-5.6 8.42-17.23 1.57-24.08L224 288z"/></svg>
|
After Width: | Height: | Size: 515 B |
1
static/icons/cog.svg
Normal file
1
static/icons/cog.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z"/></svg>
|
After Width: | Height: | Size: 890 B |
1
static/icons/home.svg
Normal file
1
static/icons/home.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M280.37 148.26L96 300.11V464a16 16 0 0 0 16 16l112.06-.29a16 16 0 0 0 15.92-16V368a16 16 0 0 1 16-16h64a16 16 0 0 1 16 16v95.64a16 16 0 0 0 16 16.05L464 480a16 16 0 0 0 16-16V300L295.67 148.26a12.19 12.19 0 0 0-15.3 0zM571.6 251.47L488 182.56V44.05a12 12 0 0 0-12-12h-56a12 12 0 0 0-12 12v72.61L318.47 43a48 48 0 0 0-61 0L4.34 251.47a12 12 0 0 0-1.6 16.9l25.5 31A12 12 0 0 0 45.15 301l235.22-193.74a12.19 12.19 0 0 1 15.3 0L530.9 301a12 12 0 0 0 16.9-1.6l25.5-31a12 12 0 0 0-1.7-16.93z"/></svg>
|
After Width: | Height: | Size: 565 B |
@ -1,5 +1,5 @@
|
||||
#app-root {
|
||||
background-color: $color-gray-lightest;
|
||||
background-color: $color-white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
|
@ -2,34 +2,93 @@
|
||||
@include media($large-screen) {
|
||||
@include grid-row;
|
||||
}
|
||||
}
|
||||
|
||||
.portfolio-navigation {
|
||||
@include panel-margin;
|
||||
margin-bottom: $gap * 4;
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
li {
|
||||
flex-grow: 1;
|
||||
.line {
|
||||
box-sizing: border-box;
|
||||
height: 2px;
|
||||
width: 100%;
|
||||
border: 1px solid $color-gray-lightest;
|
||||
}
|
||||
}
|
||||
|
||||
@include media($medium-screen) {
|
||||
margin-bottom: $gap * 5;
|
||||
.portfolio-breadcrumbs {
|
||||
margin-bottom: $gap * 2;
|
||||
color: $color-gray-medium;
|
||||
font-size: $h5-font-size;
|
||||
|
||||
.icon-link {
|
||||
color: $color-gray-medium;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
@include media($large-screen) {
|
||||
width: 20rem;
|
||||
margin-right: $gap * 2;
|
||||
.icon--tiny {
|
||||
padding: $gap 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
display: block;
|
||||
.icon {
|
||||
@include icon-color($color-gray-medium);
|
||||
}
|
||||
|
||||
.portfolio-breadcrumbs__crumb {
|
||||
.icon {
|
||||
@include icon-color($color-blue);
|
||||
}
|
||||
|
||||
.icon-link {
|
||||
color: $color-blue;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.portfolio-header {
|
||||
margin: 2 * $gap 0;
|
||||
|
||||
.portfolio-header__name {
|
||||
@include h1;
|
||||
}
|
||||
|
||||
.portfolio-header__budget {
|
||||
font-size: $small-font-size;
|
||||
align-items: center;
|
||||
|
||||
button {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.portfolio-header__budget--dollars {
|
||||
font-size: $h2-font-size;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.links {
|
||||
font-size: $small-font-size;
|
||||
|
||||
.icon-link {
|
||||
&.active {
|
||||
color: $color-gray;
|
||||
.icon {
|
||||
@include icon-color($color-gray);
|
||||
}
|
||||
}
|
||||
|
||||
.icon-link--icon {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.icon {
|
||||
@include icon-size(20);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.portfolio-content {
|
||||
margin-top: 6 * $gap;
|
||||
}
|
||||
|
||||
.portfolio-funding {
|
||||
.portfolio-funding__header {
|
||||
padding: 0;
|
||||
|
@ -62,7 +62,7 @@ $color-black-light: #212121;
|
||||
|
||||
$color-gray-dark: #323a45;
|
||||
$color-gray: #5b616b;
|
||||
$color-gray-medium: #757575;
|
||||
$color-gray-medium: #9b9b9b;
|
||||
$color-gray-light: #aeb0b5;
|
||||
$color-gray-lighter: #d6d7d9;
|
||||
$color-gray-lightest: #f1f1f1;
|
||||
|
@ -1,68 +0,0 @@
|
||||
{% from "components/sidenav_item.html" import SidenavItem %}
|
||||
|
||||
<nav class='sidenav portfolio-navigation'>
|
||||
<ul>
|
||||
{{ SidenavItem(
|
||||
("navigation.portfolio_navigation.applications" | translate),
|
||||
href=url_for("portfolios.portfolio_applications", portfolio_id=portfolio.id),
|
||||
active=request.url_rule.rule.startswith('/portfolios/<portfolio_id>/applications'),
|
||||
subnav=None if not user_can(permissions.ADD_APPLICATION_IN_PORTFOLIO) else [
|
||||
{
|
||||
"label": ("navigation.portfolio_navigation.add_new_application_label" | translate),
|
||||
"href": url_for('portfolios.new_application', portfolio_id=portfolio.id),
|
||||
"active": g.matchesPath('\/portfolios\/[A-Za-z0-9-]*\/applications'),
|
||||
"icon": "plus"
|
||||
}
|
||||
]
|
||||
) }}
|
||||
|
||||
{% if user_can(permissions.VIEW_PORTFOLIO_MEMBERS) %}
|
||||
{{ SidenavItem(
|
||||
("navigation.portfolio_navigation.members" | translate),
|
||||
href=url_for("portfolios.portfolio_members", portfolio_id=portfolio.id),
|
||||
active=request.url_rule.rule.startswith('/portfolios/<portfolio_id>/members'),
|
||||
subnav=None if not user_can(permissions.ASSIGN_AND_UNASSIGN_ATAT_ROLE) else [
|
||||
{
|
||||
"label": ("navigation.portfolio_navigation.add_new_member_label" | translate),
|
||||
"href": url_for("portfolios.new_member", portfolio_id=portfolio.id),
|
||||
"active": request.url_rule.rule.startswith('/portfolios/<portfolio_id>/members/new'),
|
||||
"icon": "plus"
|
||||
}
|
||||
]
|
||||
) }}
|
||||
{% endif %}
|
||||
|
||||
{% if user_can(permissions.VIEW_USAGE_DOLLARS) %}
|
||||
{{ SidenavItem(
|
||||
("navigation.portfolio_navigation.budget_report" | translate),
|
||||
href=url_for("portfolios.portfolio_reports", portfolio_id=portfolio.id),
|
||||
active=request.url_rule.rule.startswith('/portfolios/<portfolio_id>/reports')
|
||||
) }}
|
||||
{% endif %}
|
||||
|
||||
{{ SidenavItem(
|
||||
("navigation.portfolio_navigation.portfolio_funding" | translate),
|
||||
href=url_for("portfolios.portfolio_funding", portfolio_id=portfolio.id),
|
||||
active=request.url_rule.rule.startswith('/portfolios/<portfolio_id>/task_order'),
|
||||
subnav=None
|
||||
) }}
|
||||
|
||||
{% if user_can(permissions.EDIT_PORTFOLIO_INFORMATION) %}
|
||||
{{ SidenavItem(
|
||||
("navigation.portfolio_navigation.portfolio_settings" | translate),
|
||||
href=url_for("portfolios.portfolio", portfolio_id=portfolio.id),
|
||||
active=request.url_rule.rule.startswith('/portfolios/<portfolio_id>/edit'),
|
||||
subnav=None
|
||||
) }}
|
||||
{% endif %}
|
||||
|
||||
{% if user_can(permissions.VIEW_PORTFOLIO_AUDIT_LOG) %}
|
||||
{{ SidenavItem(
|
||||
("navigation.portfolio_navigation.activity_log" | translate),
|
||||
href=url_for("portfolios.portfolio_activity", portfolio_id=portfolio.id),
|
||||
active=request.url_rule.rule.startswith('/portfolios/<portfolio_id>/activity')
|
||||
) }}
|
||||
{% endif %}
|
||||
|
||||
</ul>
|
||||
</nav>
|
@ -1,6 +1,8 @@
|
||||
{% extends "portfolios/base.html" %}
|
||||
{% from "components/pagination.html" import Pagination %}
|
||||
|
||||
{% set secondary_breadcrumb = "navigation.portfolio_navigation.breadcrumbs.admin" | translate %}
|
||||
|
||||
{% block portfolio_content %}
|
||||
<div v-cloak>
|
||||
{% include "fragments/audit_events_log.html" %}
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
{% extends "portfolios/base.html" %}
|
||||
|
||||
|
||||
{% block portfolio_content %}
|
||||
|
||||
{% if not portfolio.applications %}
|
||||
|
@ -3,13 +3,19 @@
|
||||
{% block content %}
|
||||
|
||||
<div class='portfolio-panel-container'>
|
||||
<div class='col'>
|
||||
{% include 'navigation/portfolio_navigation.html' %}
|
||||
</div>
|
||||
|
||||
<div class='col col--grow'>
|
||||
{% block portfolio_breadcrumbs %}
|
||||
{% include "portfolios/breadcrumbs.html" %}
|
||||
{% endblock %}
|
||||
<div class='line'></div>
|
||||
{% block portfolio_header %}
|
||||
{% include "portfolios/header.html" %}
|
||||
{% endblock %}
|
||||
<div class='line'></div>
|
||||
<div class='portfolio-content'>
|
||||
{% block portfolio_content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
19
templates/portfolios/breadcrumbs.html
Normal file
19
templates/portfolios/breadcrumbs.html
Normal file
@ -0,0 +1,19 @@
|
||||
{% from "components/icon.html" import Icon %}
|
||||
|
||||
<div class="row portfolio-breadcrumbs">
|
||||
<a class="icon-link portfolio-breadcrumbs__home" href="{{ url_for("portfolios.portfolio_applications", portfolio_id=portfolio.id) }}">
|
||||
{{ Icon("home") }}
|
||||
<span>
|
||||
{{ portfolio.name }} Portfolio
|
||||
</span>
|
||||
</a>
|
||||
<div class="portfolio-breadcrumbs__crumb">
|
||||
{% if secondary_breadcrumb %}
|
||||
{{ Icon("caret_right", classes="icon--tiny") }}
|
||||
<div class="icon-link">
|
||||
{{ secondary_breadcrumb }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div
|
||||
>
|
@ -3,6 +3,8 @@
|
||||
{% from "components/icon.html" import Icon %}
|
||||
{% from "components/text_input.html" import TextInput %}
|
||||
|
||||
{% set secondary_breadcrumb = "navigation.portfolio_navigation.breadcrumbs.admin" | translate %}
|
||||
|
||||
{% block portfolio_content %}
|
||||
|
||||
{% include "fragments/flash.html" %}
|
||||
|
51
templates/portfolios/header.html
Normal file
51
templates/portfolios/header.html
Normal file
@ -0,0 +1,51 @@
|
||||
{% from "components/icon.html" import Icon %}
|
||||
|
||||
{% macro Link(icon, text, url, active=False) %}
|
||||
<a class='icon-link {{ "active icon-link--disabled" if active }}' href='{{ url }}'>
|
||||
<div class='col'>
|
||||
<div class='icon-link--icon'>{{ Icon(icon) }}</div>
|
||||
<div class='icon-link--name'>{{ text }}</div>
|
||||
</div>
|
||||
</a>
|
||||
{% endmacro %}
|
||||
|
||||
<div class='portfolio-header row'>
|
||||
<div class='col col--grow'>
|
||||
<div class='portfolio-header__name'>
|
||||
{{ portfolio.name }}
|
||||
</div>
|
||||
<div class='portfolio-header__budget row'>
|
||||
<span>Available budget</span>
|
||||
<button type="button" tabindex="0" class="icon-tooltip" v-tooltip.right="{content: 'The available budget shown includes the available budget of all active task orders', container: false}">
|
||||
{{ Icon('info') }}
|
||||
</button>
|
||||
<span class='portfolio-header__budget--dollars'>
|
||||
{{ portfolio.task_orders | sum(attribute='budget') | dollars }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class='row links'>
|
||||
{% if user_can(permissions.VIEW_USAGE_DOLLARS) %}
|
||||
{{ Link(
|
||||
icon='chart-pie',
|
||||
text='navigation.portfolio_navigation.breadcrumbs.reports' | translate,
|
||||
url=url_for("portfolios.portfolio_reports", portfolio_id=portfolio.id),
|
||||
active=request.url_rule.endpoint == "portfolios.portfolio_reports",
|
||||
) }}
|
||||
{% endif %}
|
||||
{{ Link(
|
||||
icon='dollar-sign',
|
||||
text='navigation.portfolio_navigation.breadcrumbs.funding' | translate,
|
||||
url=url_for("portfolios.portfolio_funding", portfolio_id=portfolio.id),
|
||||
active=request.url_rule.endpoint == "portfolios.portfolio_funding",
|
||||
) }}
|
||||
{% if user_can(permissions.EDIT_PORTFOLIO_INFORMATION) %}
|
||||
{{ Link(
|
||||
icon='cog',
|
||||
text='navigation.portfolio_navigation.breadcrumbs.admin' | translate,
|
||||
url=url_for("portfolios.portfolio", portfolio_id=portfolio.id),
|
||||
active=request.url_rule.endpoint == "portfolios.portfolio",
|
||||
) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
@ -4,6 +4,8 @@
|
||||
{% from "components/icon.html" import Icon %}
|
||||
{% from "components/empty_state.html" import EmptyState %}
|
||||
|
||||
{% set secondary_breadcrumb = "navigation.portfolio_navigation.breadcrumbs.reports" | translate %}
|
||||
|
||||
{% block portfolio_content %}
|
||||
|
||||
{{ Alert("Budget Report for Portfolio " + portfolio.name,
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
{% extends "portfolios/base.html" %}
|
||||
|
||||
{% set secondary_breadcrumb = "navigation.portfolio_navigation.breadcrumbs.funding" | translate %}
|
||||
|
||||
{% block portfolio_content %}
|
||||
|
||||
{% macro ViewLink(task_order) %}
|
||||
|
@ -1,5 +1,7 @@
|
||||
{% extends "portfolios/base.html" %}
|
||||
|
||||
{% set secondary_breadcrumb = "navigation.portfolio_navigation.breadcrumbs.funding" | translate %}
|
||||
|
||||
{% from "components/checkbox_input.html" import CheckboxInput %}
|
||||
{% from "components/icon.html" import Icon %}
|
||||
{% from "components/text_input.html" import TextInput %}
|
||||
|
@ -1,5 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% set secondary_breadcrumb = "navigation.portfolio_navigation.breadcrumbs.funding" | translate %}
|
||||
|
||||
{% from "components/edit_link.html" import EditLink %}
|
||||
{% from "components/required_label.html" import RequiredLabel %}
|
||||
{% from "components/icon.html" import Icon %}
|
||||
|
@ -1,5 +1,7 @@
|
||||
{% extends "portfolios/base.html" %}
|
||||
|
||||
{% set secondary_breadcrumb = "navigation.portfolio_navigation.breadcrumbs.funding" | translate %}
|
||||
|
||||
{% from "components/icon.html" import Icon %}
|
||||
|
||||
{% block portfolio_content %}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import pytest
|
||||
from flask import url_for
|
||||
|
||||
from tests.factories import (
|
||||
@ -20,7 +21,7 @@ def test_user_with_permission_has_budget_report_link(client, user_session):
|
||||
user_session(portfolio.owner)
|
||||
response = client.get("/portfolios/{}/applications".format(portfolio.id))
|
||||
assert (
|
||||
'href="/portfolios/{}/reports"'.format(portfolio.id).encode() in response.data
|
||||
"href='/portfolios/{}/reports'".format(portfolio.id).encode() in response.data
|
||||
)
|
||||
|
||||
|
||||
@ -38,6 +39,7 @@ def test_user_without_permission_has_no_budget_report_link(client, user_session)
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Temporarily no add activity log link")
|
||||
def test_user_with_permission_has_activity_log_link(client, user_session):
|
||||
portfolio = PortfolioFactory.create()
|
||||
ccpo = UserFactory.from_atat_role("ccpo")
|
||||
@ -69,6 +71,7 @@ def test_user_with_permission_has_activity_log_link(client, user_session):
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Temporarily no add activity log link")
|
||||
def test_user_without_permission_has_no_activity_log_link(client, user_session):
|
||||
portfolio = PortfolioFactory.create()
|
||||
developer = UserFactory.create()
|
||||
@ -87,6 +90,7 @@ def test_user_without_permission_has_no_activity_log_link(client, user_session):
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Temporarily no add application link")
|
||||
def test_user_with_permission_has_add_application_link(client, user_session):
|
||||
portfolio = PortfolioFactory.create()
|
||||
user_session(portfolio.owner)
|
||||
@ -97,6 +101,7 @@ def test_user_with_permission_has_add_application_link(client, user_session):
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Temporarily no add application link")
|
||||
def test_user_without_permission_has_no_add_application_link(client, user_session):
|
||||
user = UserFactory.create()
|
||||
portfolio = PortfolioFactory.create()
|
||||
|
@ -1,3 +1,4 @@
|
||||
import pytest
|
||||
from flask import url_for
|
||||
|
||||
from tests.factories import (
|
||||
@ -36,6 +37,7 @@ def create_portfolio_and_invite_user(
|
||||
return portfolio
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Temporarily no add member link")
|
||||
def test_user_with_permission_has_add_member_link(client, user_session):
|
||||
portfolio = PortfolioFactory.create()
|
||||
user_session(portfolio.owner)
|
||||
@ -46,6 +48,7 @@ def test_user_with_permission_has_add_member_link(client, user_session):
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="Temporarily no add member link")
|
||||
def test_user_without_permission_has_no_add_member_link(client, user_session):
|
||||
user = UserFactory.create()
|
||||
portfolio = PortfolioFactory.create()
|
||||
|
@ -305,6 +305,10 @@ navigation:
|
||||
applications: Applications
|
||||
portfolio_funding: Funding
|
||||
portfolio_settings: Portfolio Settings
|
||||
breadcrumbs:
|
||||
admin: Admin
|
||||
funding: Funding
|
||||
reports: Reports
|
||||
requests:
|
||||
_new:
|
||||
new_request: New Request
|
||||
|
Loading…
x
Reference in New Issue
Block a user