Merge pull request #1229 from dod-ccpo/to-blank-state
Styling tweaks to application and TO index pages
This commit is contained in:
commit
95697db8f5
@ -3,7 +3,7 @@
|
|||||||
"files": "^.secrets.baseline$|^.*pgsslrootcert.yml$",
|
"files": "^.secrets.baseline$|^.*pgsslrootcert.yml$",
|
||||||
"lines": null
|
"lines": null
|
||||||
},
|
},
|
||||||
"generated_at": "2019-11-26T21:33:43Z",
|
"generated_at": "2019-12-03T19:44:47Z",
|
||||||
"plugins_used": [
|
"plugins_used": [
|
||||||
{
|
{
|
||||||
"base64_limit": 4.5,
|
"base64_limit": 4.5,
|
||||||
@ -170,7 +170,7 @@
|
|||||||
"hashed_secret": "e4f14805dfd1e6af030359090c535e149e6b4207",
|
"hashed_secret": "e4f14805dfd1e6af030359090c535e149e6b4207",
|
||||||
"is_secret": false,
|
"is_secret": false,
|
||||||
"is_verified": false,
|
"is_verified": false,
|
||||||
"line_number": 657,
|
"line_number": 656,
|
||||||
"type": "Hex High Entropy String"
|
"type": "Hex High Entropy String"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -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
|
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")
|
@portfolios_bp.route("/portfolios/new")
|
||||||
def new_portfolio():
|
def new_portfolio():
|
||||||
form = PortfolioCreationForm()
|
form = PortfolioCreationForm()
|
||||||
|
@ -1,28 +1,20 @@
|
|||||||
.empty-state {
|
.empty-state {
|
||||||
|
padding: $gap * 3;
|
||||||
|
max-width: 100%;
|
||||||
|
background-color: $color-gray-lightest;
|
||||||
|
margin-top: $gap * 5;
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin-left: -$gap * 3;
|
||||||
|
margin-right: -$gap * 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 5rem ($gap * 2) 2rem;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
max-width: 100%;
|
|
||||||
|
|
||||||
> .icon {
|
a.usa-button {
|
||||||
@include icon-size(50);
|
width: 60%;
|
||||||
@include icon-color($color-gray-light);
|
display: inline-block;
|
||||||
}
|
|
||||||
|
|
||||||
&__message {
|
|
||||||
font-weight: $font-bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__sub-message {
|
|
||||||
@include h4;
|
|
||||||
|
|
||||||
color: $color-gray;
|
|
||||||
max-width: 100%;
|
|
||||||
|
|
||||||
@include media($large-screen) {
|
|
||||||
@include h3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -383,6 +383,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.portfolio-applications {
|
.portfolio-applications {
|
||||||
|
margin-top: $gap * 5;
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
&--title {
|
&--title {
|
||||||
@include subheading;
|
@include subheading;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
.sticky-cta {
|
.sticky-cta {
|
||||||
margin-left: -$gap * 4;
|
margin-left: -$gap * 5;
|
||||||
margin-right: -$gap * 5;
|
margin-right: -$gap * 5;
|
||||||
z-index: 10;
|
z-index: 1;
|
||||||
background-color: $color-gray-lightest;
|
background-color: $color-gray-lightest;
|
||||||
border-top: 1px solid $color-gray-lighter;
|
border-top: 1px solid $color-gray-lighter;
|
||||||
border-bottom: 1px solid $color-gray-lighter;
|
border-bottom: 1px solid $color-gray-lighter;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{% from "components/icon.html" import Icon %}
|
{% from "components/icon.html" import Icon %}
|
||||||
{% from "components/empty_state.html" import EmptyState %}
|
{% from "components/empty_state.html" import EmptyState %}
|
||||||
|
{% from "components/sticky_cta.html" import StickyCTA %}
|
||||||
|
|
||||||
{% extends "portfolios/base.html" %}
|
{% extends "portfolios/base.html" %}
|
||||||
|
|
||||||
@ -7,29 +8,26 @@
|
|||||||
|
|
||||||
{% block portfolio_content %}
|
{% block portfolio_content %}
|
||||||
|
|
||||||
<div class='portfolio-applications'>
|
{% call StickyCTA(text="common.applications"|translate) %}
|
||||||
{% include "fragments/flash.html" %}
|
{% if can_create_applications and portfolio.applications %}
|
||||||
<div class='portfolio-applications__header row'>
|
<a href="{{ url_for("applications.view_new_application_step_1", portfolio_id=portfolio.id) }}" class="usa-button usa-button-primary">
|
||||||
<div class='portfolio-applications__header--title col col--grow'>Applications</div>
|
{{ "portfolios.applications.create_button"|translate }}
|
||||||
<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>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
{% endcall %}
|
||||||
</div>
|
|
||||||
|
<div class='portfolio-applications'>
|
||||||
|
{% include "fragments/flash.html" %}
|
||||||
|
|
||||||
{% if not portfolio.applications %}
|
{% if not portfolio.applications %}
|
||||||
|
|
||||||
{{ EmptyState(
|
{{ EmptyState(
|
||||||
'This portfolio doesn’t have any applications',
|
header="portfolios.applications.empty_state.header"|translate,
|
||||||
action_label='Add a new application' if can_create_applications else None,
|
message="portfolios.applications.empty_state.message"|translate,
|
||||||
action_href=url_for('applications.create_new_application_step_1', portfolio_id=portfolio.id) if can_create_applications else None,
|
button_text="portfolios.applications.empty_state.button_text"|translate,
|
||||||
icon='cloud',
|
button_link=url_for("applications.view_new_application_step_1", portfolio_id=portfolio.id),
|
||||||
sub_message=None if can_create_applications else 'Please contact your JEDI Cloud portfolio administrator to set up a new application.',
|
view_only_text="portfolios.applications.empty_state.view_only_text"|translate,
|
||||||
add_perms=can_create_applications
|
user_can_create=can_create_applications,
|
||||||
) }}
|
) }}
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -1,20 +1,14 @@
|
|||||||
{% from "components/icon.html" import Icon %}
|
{% macro EmptyState(header, message, button_text, button_link, view_only_text, user_can_create=True) %}
|
||||||
|
<div class="empty-state">
|
||||||
{% macro EmptyState(message, action_label, action_href, icon=None, sub_message=None, add_perms=True) -%}
|
<h3>{{ header }}</h3>
|
||||||
<div class='empty-state'>
|
<p>{{ message }}</p>
|
||||||
<p class='empty-state__message'>{{ message }}</p>
|
<hr>
|
||||||
|
<div class="empty-state__footer">
|
||||||
{% if icon %}
|
{% if user_can_create %}
|
||||||
{{ Icon(icon) }}
|
<a href="{{ button_link }}" class="usa-button usa-button-primary">{{ button_text }}</a>
|
||||||
|
{% else %}
|
||||||
|
<p>{{ view_only_text }}</p>
|
||||||
{% endif %}
|
{% 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 %}
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{%- endmacro %}
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
@ -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 %}
|
|
@ -15,13 +15,14 @@
|
|||||||
%}
|
%}
|
||||||
|
|
||||||
{{ EmptyState(
|
{{ EmptyState(
|
||||||
('portfolios.reports.empty_state.message' | translate),
|
header='portfolios.reports.empty_state.message' | translate,
|
||||||
action_label= ('portfolios.reports.empty_state.action_label' | translate) if can_create_applications else None,
|
message=message,
|
||||||
action_href=url_for('applications.create_new_application_step_1', portfolio_id=portfolio.id) if can_create_applications else None,
|
button_text="portfolios.applications.empty_state.button_text"|translate,
|
||||||
icon='chart',
|
button_link=url_for("applications.view_new_application_step_1", portfolio_id=portfolio.id),
|
||||||
sub_message=message,
|
view_only_text="portfolios.applications.empty_state.view_only_text"|translate,
|
||||||
add_perms=can_create_applications
|
user_can_create=can_create_applications,
|
||||||
) }}
|
) }}
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<spend-table
|
<spend-table
|
||||||
v-bind:applications='{{ monthly_totals['applications'] | tojson }}'
|
v-bind:applications='{{ monthly_totals['applications'] | tojson }}'
|
||||||
|
@ -93,9 +93,11 @@
|
|||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
{% call StickyCTA(text="Funding") %}
|
{% call StickyCTA(text="common.task_orders"|translate) %}
|
||||||
{% if user_can(permissions.CREATE_TASK_ORDER) %}
|
{% 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" type="submit">Start a new task order</a>
|
<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 %}
|
{% endif %}
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
|
|
||||||
@ -107,11 +109,12 @@
|
|||||||
{{ TaskOrderList(task_orders) }}
|
{{ TaskOrderList(task_orders) }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ EmptyState(
|
{{ EmptyState(
|
||||||
'This portfolio doesn’t have any active or pending task orders.',
|
header="task_orders.empty_state.header"|translate,
|
||||||
action_label='Add a New Task Order',
|
message="task_orders.empty_state.message"|translate,
|
||||||
action_href=url_for('task_orders.form_step_one_add_pdf', portfolio_id=portfolio.id),
|
button_link=url_for('task_orders.form_step_one_add_pdf', portfolio_id=portfolio.id),
|
||||||
icon='cloud',
|
button_text="task_orders.empty_state.button_text"|translate,
|
||||||
add_perms=user_can(permissions.CREATE_TASK_ORDER)
|
view_only_text="task_orders.empty_state.view_only_text"|translate,
|
||||||
|
user_can_create=user_can(permissions.CREATE_TASK_ORDER),
|
||||||
) }}
|
) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -65,32 +65,6 @@ def test_create_portfolio_failure(client, user_session):
|
|||||||
assert len(PortfoliosQuery.get_all()) == original_portfolio_count
|
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):
|
def test_portfolio_reports(client, user_session):
|
||||||
portfolio = PortfolioFactory.create(
|
portfolio = PortfolioFactory.create(
|
||||||
applications=[
|
applications=[
|
||||||
|
@ -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.accept_invitation", # available to all users; access control is built into invitation logic
|
||||||
"portfolios.create_portfolio", # create a portfolio
|
"portfolios.create_portfolio", # create a portfolio
|
||||||
"portfolios.new_portfolio", # all users can 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
|
"task_orders.get_started", # all users can start a new TO
|
||||||
"users.update_user", # available to all users
|
"users.update_user", # available to all users
|
||||||
"users.user", # available to all users
|
"users.user", # available to all users
|
||||||
|
@ -105,13 +105,6 @@ def test_protected_routes_redirect_to_login(client, app):
|
|||||||
assert server_name in resp.headers["Location"]
|
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):
|
def test_unprotected_routes_set_user_if_logged_in(client, app, user_session):
|
||||||
user = UserFactory.create()
|
user = UserFactory.create()
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ ccpo:
|
|||||||
alert_message: "Confirm removing CCPO superuser access from {user_name}"
|
alert_message: "Confirm removing CCPO superuser access from {user_name}"
|
||||||
remove_button: Remove Access
|
remove_button: Remove Access
|
||||||
common:
|
common:
|
||||||
|
applications: Applications
|
||||||
cancel: Cancel
|
cancel: Cancel
|
||||||
close: Close
|
close: Close
|
||||||
confirm: Confirm
|
confirm: Confirm
|
||||||
@ -65,6 +66,7 @@ common:
|
|||||||
response_label: Response required
|
response_label: Response required
|
||||||
save: Save
|
save: Save
|
||||||
save_changes: Save Changes
|
save_changes: Save Changes
|
||||||
|
task_orders: Task Orders
|
||||||
undo: Undo
|
undo: Undo
|
||||||
view: View
|
view: View
|
||||||
resource_names:
|
resource_names:
|
||||||
@ -308,6 +310,12 @@ portfolios:
|
|||||||
add_member: Add Team Member
|
add_member: Add Team Member
|
||||||
add_another_environment: Add another environment
|
add_another_environment: Add another environment
|
||||||
app_settings_text: App settings
|
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:
|
new:
|
||||||
step_1_header: Name and Describe New Application
|
step_1_header: Name and Describe New Application
|
||||||
step_1_button_text: "Next: Add Environments"
|
step_1_button_text: "Next: Add Environments"
|
||||||
@ -455,6 +463,7 @@ portfolios:
|
|||||||
action_label: 'Add a new application'
|
action_label: 'Add a new application'
|
||||||
|
|
||||||
task_orders:
|
task_orders:
|
||||||
|
add_new_button: Add New Task Order
|
||||||
review:
|
review:
|
||||||
pdf_title: Approved Task Order
|
pdf_title: Approved Task Order
|
||||||
review_your_funding: Review your funding
|
review_your_funding: Review your funding
|
||||||
@ -507,6 +516,11 @@ task_orders:
|
|||||||
alert_message: All task orders require a Contracting Officer signature.
|
alert_message: All task orders require a Contracting Officer signature.
|
||||||
next_button: 'Confirm & Submit'
|
next_button: 'Confirm & Submit'
|
||||||
sticky_header_text: 'Add Task Order (step {step} of 5)'
|
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:
|
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.
|
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:
|
app_info:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user