Merge pull request #1229 from dod-ccpo/to-blank-state

Styling tweaks to application and TO index pages
This commit is contained in:
leigh-mil 2019-12-04 15:16:57 -05:00 committed by GitHub
commit 95697db8f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 77 additions and 132 deletions

View File

@ -3,7 +3,7 @@
"files": "^.secrets.baseline$|^.*pgsslrootcert.yml$",
"lines": null
},
"generated_at": "2019-11-26T21:33:43Z",
"generated_at": "2019-12-03T19:44:47Z",
"plugins_used": [
{
"base64_limit": 4.5,
@ -170,7 +170,7 @@
"hashed_secret": "e4f14805dfd1e6af030359090c535e149e6b4207",
"is_secret": false,
"is_verified": false,
"line_number": 657,
"line_number": 656,
"type": "Hex High Entropy String"
}
]

View File

@ -11,16 +11,6 @@ from atst.domain.authz.decorator import user_can_access_decorator as user_can
from atst.utils.flash import formatted_flash as flash
@portfolios_bp.route("/portfolios")
def portfolios():
portfolios = Portfolios.for_user(g.current_user)
if portfolios:
return render_template("portfolios/index.html", page=5, portfolios=portfolios)
else:
return render_template("portfolios/blank_slate.html")
@portfolios_bp.route("/portfolios/new")
def new_portfolio():
form = PortfolioCreationForm()

View File

@ -1,28 +1,20 @@
.empty-state {
text-align: center;
padding: 5rem ($gap * 2) 2rem;
display: flex;
flex-direction: column;
align-items: center;
padding: $gap * 3;
max-width: 100%;
background-color: $color-gray-lightest;
margin-top: $gap * 5;
> .icon {
@include icon-size(50);
@include icon-color($color-gray-light);
hr {
margin-left: -$gap * 3;
margin-right: -$gap * 3;
}
&__message {
font-weight: $font-bold;
}
&__footer {
text-align: center;
&__sub-message {
@include h4;
color: $color-gray;
max-width: 100%;
@include media($large-screen) {
@include h3;
a.usa-button {
width: 60%;
display: inline-block;
}
}
}

View File

@ -383,6 +383,8 @@
}
.portfolio-applications {
margin-top: $gap * 5;
&__header {
&--title {
@include subheading;

View File

@ -1,7 +1,7 @@
.sticky-cta {
margin-left: -$gap * 4;
margin-left: -$gap * 5;
margin-right: -$gap * 5;
z-index: 10;
z-index: 1;
background-color: $color-gray-lightest;
border-top: 1px solid $color-gray-lighter;
border-bottom: 1px solid $color-gray-lighter;

View File

@ -1,5 +1,6 @@
{% from "components/icon.html" import Icon %}
{% from "components/empty_state.html" import EmptyState %}
{% from "components/sticky_cta.html" import StickyCTA %}
{% extends "portfolios/base.html" %}
@ -7,29 +8,26 @@
{% block portfolio_content %}
{% call StickyCTA(text="common.applications"|translate) %}
{% if can_create_applications and portfolio.applications %}
<a href="{{ url_for("applications.view_new_application_step_1", portfolio_id=portfolio.id) }}" class="usa-button usa-button-primary">
{{ "portfolios.applications.create_button"|translate }}
</a>
{% endif %}
{% endcall %}
<div class='portfolio-applications'>
{% include "fragments/flash.html" %}
<div class='portfolio-applications__header row'>
<div class='portfolio-applications__header--title col col--grow'>Applications</div>
<div class='portfolio-applications__header--actions col'>
{% if can_create_applications %}
<a class='icon-link' href='{{ url_for('applications.view_new_application_step_1', portfolio_id=portfolio.id) }}'>
{{ 'portfolios.applications.add_application_text' | translate }}
{{ Icon("plus", classes="sidenav__link-icon") }}
</a>
{% endif %}
</div>
</div>
{% if not portfolio.applications %}
{{ EmptyState(
'This portfolio doesnt have any applications',
action_label='Add a new application' if can_create_applications else None,
action_href=url_for('applications.create_new_application_step_1', portfolio_id=portfolio.id) if can_create_applications else None,
icon='cloud',
sub_message=None if can_create_applications else 'Please contact your JEDI Cloud portfolio administrator to set up a new application.',
add_perms=can_create_applications
header="portfolios.applications.empty_state.header"|translate,
message="portfolios.applications.empty_state.message"|translate,
button_text="portfolios.applications.empty_state.button_text"|translate,
button_link=url_for("applications.view_new_application_step_1", portfolio_id=portfolio.id),
view_only_text="portfolios.applications.empty_state.view_only_text"|translate,
user_can_create=can_create_applications,
) }}
{% else %}

View File

@ -1,20 +1,14 @@
{% from "components/icon.html" import Icon %}
{% macro EmptyState(message, action_label, action_href, icon=None, sub_message=None, add_perms=True) -%}
<div class='empty-state'>
<p class='empty-state__message'>{{ message }}</p>
{% if icon %}
{{ Icon(icon) }}
{% endif %}
{% if sub_message %}
<p class='empty-state__sub-message'>{{ sub_message }}</p>
{% endif %}
{% if add_perms and (action_href and action_label) %}
<a href='{{ action_href }}' class='usa-button usa-button-big'>{{ action_label }}</a>
{% endif %}
{% macro EmptyState(header, message, button_text, button_link, view_only_text, user_can_create=True) %}
<div class="empty-state">
<h3>{{ header }}</h3>
<p>{{ message }}</p>
<hr>
<div class="empty-state__footer">
{% if user_can_create %}
<a href="{{ button_link }}" class="usa-button usa-button-primary">{{ button_text }}</a>
{% else %}
<p>{{ view_only_text }}</p>
{% endif %}
</div>
</div>
{%- endmacro %}
{% endmacro %}

View File

@ -1,15 +0,0 @@
{% extends "base.html" %}
{% from "components/empty_state.html" import EmptyState %}
{% from "components/tooltip.html" import Tooltip %}
{% block content %}
{{
EmptyState(
action_href="#",
action_label=("portfolios.index.empty.start_button" | translate),
icon="cloud",
message=("portfolios.index.empty.title" | translate),
)
}}
{% endblock %}

View File

@ -15,13 +15,14 @@
%}
{{ EmptyState(
('portfolios.reports.empty_state.message' | translate),
action_label= ('portfolios.reports.empty_state.action_label' | translate) if can_create_applications else None,
action_href=url_for('applications.create_new_application_step_1', portfolio_id=portfolio.id) if can_create_applications else None,
icon='chart',
sub_message=message,
add_perms=can_create_applications
header='portfolios.reports.empty_state.message' | translate,
message=message,
button_text="portfolios.applications.empty_state.button_text"|translate,
button_link=url_for("applications.view_new_application_step_1", portfolio_id=portfolio.id),
view_only_text="portfolios.applications.empty_state.view_only_text"|translate,
user_can_create=can_create_applications,
) }}
{% else %}
<spend-table
v-bind:applications='{{ monthly_totals['applications'] | tojson }}'

View File

@ -93,9 +93,11 @@
{% endmacro %}
{% call StickyCTA(text="Funding") %}
{% if user_can(permissions.CREATE_TASK_ORDER) %}
<a href="{{ url_for("task_orders.form_step_one_add_pdf", portfolio_id=portfolio.id) }}" class="usa-button usa-button-primary" type="submit">Start a new task order</a>
{% call StickyCTA(text="common.task_orders"|translate) %}
{% if user_can(permissions.CREATE_TASK_ORDER) and task_orders %}
<a href="{{ url_for("task_orders.form_step_one_add_pdf", portfolio_id=portfolio.id) }}" class="usa-button usa-button-primary">
{{ "task_orders.add_new_button" | translate }}
</a>
{% endif %}
{% endcall %}
@ -107,11 +109,12 @@
{{ TaskOrderList(task_orders) }}
{% else %}
{{ EmptyState(
'This portfolio doesnt have any active or pending task orders.',
action_label='Add a New Task Order',
action_href=url_for('task_orders.form_step_one_add_pdf', portfolio_id=portfolio.id),
icon='cloud',
add_perms=user_can(permissions.CREATE_TASK_ORDER)
header="task_orders.empty_state.header"|translate,
message="task_orders.empty_state.message"|translate,
button_link=url_for('task_orders.form_step_one_add_pdf', portfolio_id=portfolio.id),
button_text="task_orders.empty_state.button_text"|translate,
view_only_text="task_orders.empty_state.view_only_text"|translate,
user_can_create=user_can(permissions.CREATE_TASK_ORDER),
) }}
{% endif %}
</div>

View File

@ -65,32 +65,6 @@ def test_create_portfolio_failure(client, user_session):
assert len(PortfoliosQuery.get_all()) == original_portfolio_count
def test_portfolio_index_with_existing_portfolios(client, user_session):
portfolio = PortfolioFactory.create()
user_session(portfolio.owner)
response = client.get(url_for("portfolios.portfolios"))
assert response.status_code == 200
assert portfolio.name.encode("utf8") in response.data
assert (
translate("portfolios.index.empty.start_button").encode("utf8")
not in response.data
)
def test_portfolio_index_without_existing_portfolios(client, user_session):
user = UserFactory.create()
user_session(user)
response = client.get(url_for("portfolios.portfolios"))
assert response.status_code == 200
assert (
translate("portfolios.index.empty.start_button").encode("utf8") in response.data
)
def test_portfolio_reports(client, user_session):
portfolio = PortfolioFactory.create(
applications=[

View File

@ -26,7 +26,6 @@ _NO_ACCESS_CHECK_REQUIRED = _NO_LOGIN_REQUIRED + [
"portfolios.accept_invitation", # available to all users; access control is built into invitation logic
"portfolios.create_portfolio", # create a portfolio
"portfolios.new_portfolio", # all users can create a portfolio
"portfolios.portfolios", # the portfolios list is scoped to the user separately
"task_orders.get_started", # all users can start a new TO
"users.update_user", # available to all users
"users.user", # available to all users

View File

@ -105,13 +105,6 @@ def test_protected_routes_redirect_to_login(client, app):
assert server_name in resp.headers["Location"]
def test_get_protected_route_encodes_redirect(client):
portfolio_index = url_for("portfolios.portfolios")
response = client.get(portfolio_index)
redirect = url_for("atst.root", next=portfolio_index)
assert redirect in response.headers["Location"]
def test_unprotected_routes_set_user_if_logged_in(client, app, user_session):
user = UserFactory.create()

View File

@ -44,6 +44,7 @@ ccpo:
alert_message: "Confirm removing CCPO superuser access from {user_name}"
remove_button: Remove Access
common:
applications: Applications
cancel: Cancel
close: Close
confirm: Confirm
@ -65,6 +66,7 @@ common:
response_label: Response required
save: Save
save_changes: Save Changes
task_orders: Task Orders
undo: Undo
view: View
resource_names:
@ -308,6 +310,12 @@ portfolios:
add_member: Add Team Member
add_another_environment: Add another environment
app_settings_text: App settings
create_button: Create Application
empty_state:
header: You don't have any Applications yet
message: You can manage multiple Applications within a single Portfolio as long as the funding sources are the same.
button_text: Create Your First Application
view_only_text: Contact your portfolio administrator to add an application.
new:
step_1_header: Name and Describe New Application
step_1_button_text: "Next: Add Environments"
@ -455,6 +463,7 @@ portfolios:
action_label: 'Add a new application'
task_orders:
add_new_button: Add New Task Order
review:
pdf_title: Approved Task Order
review_your_funding: Review your funding
@ -507,6 +516,11 @@ task_orders:
alert_message: All task orders require a Contracting Officer signature.
next_button: 'Confirm & Submit'
sticky_header_text: 'Add Task Order (step {step} of 5)'
empty_state:
header: Add approved task orders
message: Upload your approved Task Order here. You are required to confirm you have the appropriate signature. You will have the ability to add additional approved Task Orders with more funding to this Portfolio in the future.
button_text: Add Task Order
view_only_text: Contact your portfolio administrator to add a Task Order.
new:
form_help_text: Before you can begin work in the cloud, you'll need to complete the information below and upload your approved task order for reference by the CCPO.
app_info: