Merge pull request #617 from dod-ccpo/reskin-portfolio-reports
Reskin portfolio reports
This commit is contained in:
commit
a7733925ca
@ -62,9 +62,11 @@ def portfolio_reports(portfolio_id):
|
||||
prev_month = current_month - timedelta(days=28)
|
||||
two_months_ago = prev_month - timedelta(days=28)
|
||||
|
||||
expiration_date = (
|
||||
portfolio.legacy_task_order and portfolio.legacy_task_order.expiration_date
|
||||
task_order = next(
|
||||
(task_order for task_order in portfolio.task_orders if task_order.is_active),
|
||||
None,
|
||||
)
|
||||
expiration_date = task_order and task_order.end_date
|
||||
if expiration_date:
|
||||
remaining_difference = expiration_date - today
|
||||
remaining_days = remaining_difference.days
|
||||
@ -76,8 +78,7 @@ def portfolio_reports(portfolio_id):
|
||||
cumulative_budget=Reports.cumulative_budget(portfolio),
|
||||
portfolio_totals=Reports.portfolio_totals(portfolio),
|
||||
monthly_totals=Reports.monthly_totals(portfolio),
|
||||
jedi_request=portfolio.request,
|
||||
legacy_task_order=portfolio.legacy_task_order,
|
||||
task_order=task_order,
|
||||
current_month=current_month,
|
||||
prev_month=prev_month,
|
||||
two_months_ago=two_months_ago,
|
||||
|
1
static/icons/envelope.svg
Normal file
1
static/icons/envelope.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm0 48v40.805c-22.422 18.259-58.168 46.651-134.587 106.49-16.841 13.247-50.201 45.072-73.413 44.701-23.208.375-56.579-31.459-73.413-44.701C106.18 199.465 70.425 171.067 48 152.805V112h416zM48 400V214.398c22.914 18.251 55.409 43.862 104.938 82.646 21.857 17.205 60.134 55.186 103.062 54.955 42.717.231 80.509-37.199 103.053-54.947 49.528-38.783 82.032-64.401 104.947-82.653V400H48z"/></svg>
|
After Width: | Height: | Size: 574 B |
@ -95,17 +95,21 @@
|
||||
margin: 6 * $gap $gap 0 $gap;
|
||||
}
|
||||
|
||||
.portfolio-applications {
|
||||
.portfolio-applications__header {
|
||||
margin-bottom: 4 * $gap;
|
||||
|
||||
.portfolio-applications__header--title {
|
||||
@mixin subheading {
|
||||
color: $color-gray-dark;
|
||||
padding: $gap 0;
|
||||
text-transform: uppercase;
|
||||
opacity: 0.54;
|
||||
font-size: $small-font-size;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.portfolio-applications {
|
||||
.portfolio-applications__header {
|
||||
margin-bottom: 4 * $gap;
|
||||
|
||||
.portfolio-applications__header--title {
|
||||
@include subheading;
|
||||
}
|
||||
|
||||
.portfolio-applications__header--actions {
|
||||
@ -287,3 +291,19 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.portfolio-reports {
|
||||
.portfolio-reports__header {
|
||||
margin-bottom: 4 * $gap;
|
||||
|
||||
.portfolio-reports__header--title {
|
||||
@include subheading;
|
||||
}
|
||||
}
|
||||
|
||||
.panel {
|
||||
box-shadow: 0 6px 18px 0 rgba(144,164,183,0.3);
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,11 @@
|
||||
|
||||
.funding-summary-row__col {
|
||||
|
||||
hr {
|
||||
margin: 2 * $gap 0;
|
||||
border-bottom: 1px solid $color-gray-lightest;
|
||||
}
|
||||
|
||||
@include media($medium-screen) {
|
||||
@include grid-pad;
|
||||
flex-grow: 1;
|
||||
@ -36,6 +41,11 @@
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.subheading {
|
||||
@include h4;
|
||||
margin: 0 $gap 2 * $gap 0;
|
||||
-ms-flex-negative: 1;
|
||||
}
|
||||
|
||||
// Spending Summary
|
||||
// ===============================
|
||||
@ -53,40 +63,28 @@
|
||||
}
|
||||
}
|
||||
|
||||
.spend-summary__heading {
|
||||
@include h3;
|
||||
margin: 0 $gap 0 0;
|
||||
-ms-flex-negative: 1;
|
||||
}
|
||||
|
||||
.spend-summary__budget {
|
||||
margin: 0 0 0 $gap;
|
||||
|
||||
@include ie-only {
|
||||
margin: $gap 0 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
> div {
|
||||
text-align: right;
|
||||
dl {
|
||||
text-align: left;
|
||||
margin: 0 0 ($gap / 2) 0;
|
||||
|
||||
@include ie-only {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
dd, dt {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
dt {
|
||||
color: $color-gray;
|
||||
text-transform: uppercase;
|
||||
color: $color-gray-light;
|
||||
margin-right: $gap;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
dd {
|
||||
font-weight: bold;
|
||||
}
|
||||
font-size: $small-font-size;
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,32 +95,33 @@
|
||||
}
|
||||
|
||||
.spend-summary__spent {
|
||||
margin: $gap 0 0 0;
|
||||
margin: 2 * $gap 0;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
|
||||
dd, dt {
|
||||
@include h5;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: normal;
|
||||
margin-left: $gap;
|
||||
letter-spacing: 0.47px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Task Order Summary
|
||||
// ===============================
|
||||
&.to-summary {
|
||||
|
||||
.to-summary__row {
|
||||
.icon-link {
|
||||
font-weight: $font-normal
|
||||
}
|
||||
|
||||
.subheading {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.to-summary__heading {
|
||||
@include h3;
|
||||
margin: 0;
|
||||
@include h4;
|
||||
margin: 0 $gap 0 0;
|
||||
}
|
||||
|
||||
.to-summary__to-number {
|
||||
@ -134,7 +133,6 @@
|
||||
margin-right: $gap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include media($xlarge-screen) {
|
||||
display: flex;
|
||||
@ -163,23 +161,27 @@
|
||||
.to-summary__expiration {
|
||||
|
||||
dl {
|
||||
margin: ($gap * 2) 0 0 0;
|
||||
|
||||
> div {
|
||||
margin: 0 0 ($gap / 2) 0;
|
||||
text-align: right;
|
||||
|
||||
dd, dt {
|
||||
display: block;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
dt {
|
||||
color: $color-gray;
|
||||
margin-right: $gap;
|
||||
font-weight: normal;
|
||||
font-size: $small-font-size;
|
||||
text-transform: uppercase;
|
||||
font-weight: $font-bold;
|
||||
color: $color-gray-light;
|
||||
}
|
||||
|
||||
dd {
|
||||
font-weight: bold;
|
||||
dd.ending-soon {
|
||||
color: $color-red-dark;
|
||||
font-size: $h2-font-size;
|
||||
white-space: nowrap;
|
||||
|
||||
.icon {
|
||||
@include icon-size(28);
|
||||
@include icon-color($color-red-dark);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -203,9 +205,12 @@
|
||||
|
||||
|
||||
.spend-table {
|
||||
box-shadow: 0 6px 18px 0 rgba(144,164,183,0.3);
|
||||
|
||||
.spend-table__header {
|
||||
@include panel-base;
|
||||
@include panel-theme-default;
|
||||
border-top: none;
|
||||
border-bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@ -215,8 +220,8 @@
|
||||
padding: $gap * 2;
|
||||
|
||||
.spend-table__title {
|
||||
@include h3;
|
||||
margin: 0;
|
||||
@include h4;
|
||||
font-size: $lead-font-size;
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
@ -227,6 +232,12 @@
|
||||
}
|
||||
|
||||
table {
|
||||
thead th {
|
||||
text-transform: uppercase;
|
||||
border-bottom: 1px solid $color-gray-lightest;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
th, td {
|
||||
white-space: nowrap;
|
||||
|
||||
@ -234,10 +245,6 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.current-month {
|
||||
background-color: $color-aqua-lightest;
|
||||
}
|
||||
|
||||
&.previous-month {
|
||||
color: $color-gray;
|
||||
}
|
||||
@ -286,28 +293,53 @@
|
||||
.spend-table__portfolio {
|
||||
th, td {
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid $color-gray-lightest;
|
||||
}
|
||||
}
|
||||
|
||||
.spend-table__application {
|
||||
.spend-table__application__toggler {
|
||||
@include icon-link-color($color-black-light, $color-gray-lightest);
|
||||
@include icon-link-color($color-blue, $color-gray-lightest);
|
||||
margin-left: -$gap;
|
||||
color: $color-blue;
|
||||
|
||||
.icon {
|
||||
@include icon-size(12);
|
||||
margin-right: $gap;
|
||||
}
|
||||
|
||||
.open-indicator {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 5 * $gap;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 10px solid transparent;
|
||||
border-right: 10px solid transparent;
|
||||
|
||||
border-bottom: 10px solid $color-blue-light;
|
||||
}
|
||||
}
|
||||
|
||||
th, td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
th[scope=rowgroup] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.spend-table__application__env {
|
||||
margin-left: $gap;
|
||||
margin-left: 2 * $gap;
|
||||
|
||||
&:last-child {
|
||||
td, th {
|
||||
padding-bottom: $gap * 5;
|
||||
box-shadow: inset 0 (-$gap * 2.5) 0 $color-gray-lightest;
|
||||
th, td {
|
||||
.icon-link {
|
||||
font-weight: $font-normal;
|
||||
font-size: $base-font-size;
|
||||
}
|
||||
|
||||
border-bottom: 1px dashed $color-white;
|
||||
background-color: $color-blue-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
<div class='portfolio-header row'>
|
||||
<div class='col col--grow'>
|
||||
<div class='portfolio-header__name'>
|
||||
{{ portfolio.name }}
|
||||
{{ secondary_breadcrumb or portfolio.name }}
|
||||
</div>
|
||||
<div class='portfolio-header__budget row'>
|
||||
<span>Available budget</span>
|
||||
|
@ -1,6 +1,5 @@
|
||||
{% extends "portfolios/base.html" %}
|
||||
|
||||
{% from "components/alert.html" import Alert %}
|
||||
{% from "components/icon.html" import Icon %}
|
||||
{% from "components/empty_state.html" import EmptyState %}
|
||||
|
||||
@ -8,42 +7,38 @@
|
||||
|
||||
{% block portfolio_content %}
|
||||
|
||||
{{ Alert("Budget Report for Portfolio " + portfolio.name,
|
||||
message="<p>Track your monthly and cumulative expenditures for your portfolio, applications, and environments below.</p>\
|
||||
<p>Please note that the projected spend is based on the <em>average expense over the last three completed months</em> and therefore does not account for future changes that might be made in scale or configuration of your cloud services.</p>",
|
||||
actions=[
|
||||
{"label": "Learn More", "href": url_for('atst.helpdocs'), "icon": "info"}
|
||||
] ) }}
|
||||
|
||||
<div class='portfolio-reports'>
|
||||
<div v-cloak class='funding-summary-row'>
|
||||
|
||||
<div class='funding-summary-row__col'>
|
||||
<div class='panel spend-summary'>
|
||||
<h4 class='spend-summary__heading subheading'>Portfolio Total Spend</h4>
|
||||
<div class='row'>
|
||||
<h2 class='spend-summary__heading col'>Portfolio Total Spend</h2>
|
||||
<dl class='spend-summary__budget'>
|
||||
<dl class='spend-summary__budget col col--grow row'>
|
||||
{% set budget = portfolio_totals['budget'] %}
|
||||
{% set spent = portfolio_totals['spent'] %}
|
||||
{% set remaining = budget - spent %}
|
||||
<div>
|
||||
<dt>Budget </dt>
|
||||
<dl class='col col--grow'>
|
||||
<dt>Budget</dt>
|
||||
<dd>{{ budget | dollars }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
<div>
|
||||
<dl class='col col--grow'>
|
||||
<dt>Remaining</dt>
|
||||
<dd>{{ remaining | dollars }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<hr></hr>
|
||||
|
||||
<div>
|
||||
<meter value='{{ spent }}' min='0' max='{{ budget }}' title='{{ spent | dollars }} Total spend to date'>
|
||||
<div class='meter__fallback' style='width:{{ (spent / budget) * 100 if budget else 0 }}%;'></div>
|
||||
</meter>
|
||||
|
||||
<dl class='spend-summary__spent'>
|
||||
<dt>Total spend to date</dt>
|
||||
<dt>Total spending to date</dt>
|
||||
<dd>{{ spent | dollars }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
@ -55,18 +50,19 @@
|
||||
<div class='to-summary__row'>
|
||||
|
||||
<div class='to-summary__to'>
|
||||
<h2 class='to-summary__heading'>Task Order</h2>
|
||||
<h2 class='to-summary__heading subheading'>Current Task Order</h2>
|
||||
<dl class='to-summary__to-number'>
|
||||
<dt class='usa-sr-only'>Task Order Number</dt>
|
||||
<dd>{{ legacy_task_order.number }}</dd>
|
||||
<dd>{{ task_order.number }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class='to-summary__expiration'>
|
||||
<dl>
|
||||
<hr></hr>
|
||||
|
||||
<div class='to-summary__expiration row'>
|
||||
<div class='col col--grow'>
|
||||
<h4 class='subheading'>Expiration Date</h4>
|
||||
<div>
|
||||
<dt>Expires</dt>
|
||||
<dd>
|
||||
{% if expiration_date %}
|
||||
<local-datetime
|
||||
timestamp='{{ expiration_date }}'
|
||||
@ -75,32 +71,47 @@
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<dt>Remaining</dt>
|
||||
<dd>
|
||||
<a href='{{ url_for("portfolios.portfolio_funding", portfolio_id=portfolio.id) }}' class='icon-link'>
|
||||
{{ Icon('cog') }}
|
||||
Manage Task Order
|
||||
</a>
|
||||
</div>
|
||||
<div class='col col--grow'>
|
||||
<dl>
|
||||
<dt>Remaining Days</dt>
|
||||
<dd class='{{ 'ending-soon' if remaining_days is not none and remaining_days < 190 }}'>
|
||||
{% if remaining_days is not none %}
|
||||
{{ remaining_days }} days
|
||||
{{ Icon('arrow-down') }}
|
||||
<span>{{ remaining_days }}</span>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href='{{ url_for("portfolios.portfolio", portfolio_id=portfolio.id) }}' class='icon-link'>
|
||||
Manage Task Order
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<hr></hr>
|
||||
|
||||
<dl class='to-summary__co'>
|
||||
<dt>Contracting Officer</dt>
|
||||
<dd>
|
||||
{{ jedi_request.contracting_officer_full_name }}
|
||||
<a class='icon-link' href='mailto:{{ jedi_request.contracting_officer_email }}'>{{ jedi_request.contracting_officer_email }}</a>
|
||||
<dt class='subheading'>Contracting Officer</dt>
|
||||
<dd class='row'>
|
||||
<div class='col col--grow'>
|
||||
{% if task_order.ko_first_name and task_order.ko_last_name %}
|
||||
{{ task_order.ko_first_name }} {{ task_order.ko_last_name }}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class='col'>
|
||||
{% if task_order.ko_email %}
|
||||
<a class='icon-link' href='mailto:{{ task_order.ko_email }}'>
|
||||
{{ Icon('envelope') }}
|
||||
{{ task_order.ko_email }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
@ -142,7 +153,7 @@
|
||||
|
||||
<div class='budget-chart panel' ref='panel'>
|
||||
<header class='budget-chart__header panel__heading panel__heading--tight'>
|
||||
<h2 class='h3'>Cumulative Budget</h2>
|
||||
<h4>Cumulative Budget</h4>
|
||||
|
||||
<div class='budget-chart__legend'>
|
||||
<dl class='budget-chart__legend__spend'>
|
||||
@ -331,7 +342,7 @@
|
||||
|
||||
<div class='spend-table responsive-table-wrapper'>
|
||||
<div class='spend-table__header'>
|
||||
<h2 class='spend-table__title'>Total spend per month </h2>
|
||||
<h2 class='spend-table__title'>Total spent per month</h2>
|
||||
|
||||
<select name='month' id='month' onchange='location = this.value' class='spend-table__month-select'>
|
||||
{% for m in cumulative_budget["months"] %}
|
||||
@ -368,12 +379,12 @@
|
||||
<th scope='col' class='table-cell--align-right previous-month'>{{ two_months_ago.strftime('%B %Y') }}</th>
|
||||
<th scope='col' class='table-cell--align-right previous-month'>{{ prev_month.strftime('%B %Y') }}</th>
|
||||
<th scope='col' class='table-cell--align-right current-month'>{{ current_month.strftime('%B %Y') }}</th>
|
||||
<th class='current-month'>% of total spend this month</th>
|
||||
<th class='current-month'></th>
|
||||
</thead>
|
||||
|
||||
<tbody class='spend-table__portfolio'>
|
||||
<tr>
|
||||
<th scope='row'>Total</th>
|
||||
<th scope='row'>Portfolio Total</th>
|
||||
<td class='table-cell--align-right previous-month'>{{ portfolio_totals.get(two_months_ago_index, 0) | dollars }}</td>
|
||||
<td class='table-cell--align-right previous-month'>{{ portfolio_totals.get(prev_month_index, 0) | dollars }}</td>
|
||||
<td class='table-cell--align-right current-month'>{{ portfolio_totals.get(current_month_index, 0) | dollars }}</td>
|
||||
@ -389,7 +400,7 @@
|
||||
<tr>
|
||||
<th scope='rowgroup'>
|
||||
<button v-on:click='toggle($event, name)' class='icon-link icon-link--large spend-table__application__toggler'>
|
||||
<template v-if='application.isVisible'>{{ Icon('caret_down') }}</template>
|
||||
<template v-if='application.isVisible'>{{ Icon('caret_down') }}<div class='open-indicator'></div></template>
|
||||
<template v-else>{{ Icon('caret_right') }}</template>
|
||||
<span v-html='name'></span>
|
||||
</button>
|
||||
@ -418,10 +429,9 @@
|
||||
|
||||
<tr v-for='(environment, envName) in environments[name]' v-show='application.isVisible' class='spend-table__application__env'>
|
||||
<th scope='rowgroup'>
|
||||
<a href='#' class='icon-link spend-table__application__env'>
|
||||
{{ Icon('link') }}
|
||||
<div class='icon-link spend-table__application__env'>
|
||||
<span v-html='envName'></span>
|
||||
</a>
|
||||
</div>
|
||||
</th>
|
||||
|
||||
<td class='table-cell--align-right previous-month'>
|
||||
@ -442,7 +452,7 @@
|
||||
</table>
|
||||
</spend-table>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
Loading…
x
Reference in New Issue
Block a user