diff --git a/.secrets.baseline b/.secrets.baseline
index 7a81d3cb..49a70104 100644
--- a/.secrets.baseline
+++ b/.secrets.baseline
@@ -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"
}
]
diff --git a/atst/routes/portfolios/index.py b/atst/routes/portfolios/index.py
index 06a4e783..a34d9057 100644
--- a/atst/routes/portfolios/index.py
+++ b/atst/routes/portfolios/index.py
@@ -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()
diff --git a/styles/components/_empty_state.scss b/styles/components/_empty_state.scss
index 0e102a4d..12405e05 100644
--- a/styles/components/_empty_state.scss
+++ b/styles/components/_empty_state.scss
@@ -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;
}
}
}
diff --git a/styles/components/_portfolio_layout.scss b/styles/components/_portfolio_layout.scss
index 0e018809..0da98f17 100644
--- a/styles/components/_portfolio_layout.scss
+++ b/styles/components/_portfolio_layout.scss
@@ -383,6 +383,8 @@
}
.portfolio-applications {
+ margin-top: $gap * 5;
+
&__header {
&--title {
@include subheading;
diff --git a/styles/components/_sticky_cta.scss b/styles/components/_sticky_cta.scss
index c5946951..ce05144f 100644
--- a/styles/components/_sticky_cta.scss
+++ b/styles/components/_sticky_cta.scss
@@ -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;
diff --git a/templates/applications/index.html b/templates/applications/index.html
index bc7ae431..4ab303e4 100644
--- a/templates/applications/index.html
+++ b/templates/applications/index.html
@@ -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 %}
+
+ {{ "portfolios.applications.create_button"|translate }}
+
+ {% endif %}
+{% endcall %}
+
{% include "fragments/flash.html" %}
-
{% if not portfolio.applications %}
{{ EmptyState(
- 'This portfolio doesn’t 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 %}
diff --git a/templates/components/empty_state.html b/templates/components/empty_state.html
index a9d9ff5e..9989e4f8 100644
--- a/templates/components/empty_state.html
+++ b/templates/components/empty_state.html
@@ -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) -%}
-
-
{{ message }}
-
- {% if icon %}
- {{ Icon(icon) }}
- {% endif %}
-
- {% if sub_message %}
-
{{ sub_message }}
- {% endif %}
-
- {% if add_perms and (action_href and action_label) %}
-
{{ action_label }}
- {% endif %}
-
+{% macro EmptyState(header, message, button_text, button_link, view_only_text, user_can_create=True) %}
+
+
{{ header }}
+
{{ message }}
+
+
-{%- endmacro %}
+{% endmacro %}
diff --git a/templates/portfolios/blank_slate.html b/templates/portfolios/blank_slate.html
deleted file mode 100644
index 62c59aa5..00000000
--- a/templates/portfolios/blank_slate.html
+++ /dev/null
@@ -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 %}
diff --git a/templates/portfolios/reports/application_and_env_spending.html b/templates/portfolios/reports/application_and_env_spending.html
index e43ebf0c..0af72112 100644
--- a/templates/portfolios/reports/application_and_env_spending.html
+++ b/templates/portfolios/reports/application_and_env_spending.html
@@ -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 %}
Start a new task order
+{% call StickyCTA(text="common.task_orders"|translate) %}
+ {% if user_can(permissions.CREATE_TASK_ORDER) and task_orders %}
+
+ {{ "task_orders.add_new_button" | translate }}
+
{% endif %}
{% endcall %}
@@ -107,11 +109,12 @@
{{ TaskOrderList(task_orders) }}
{% else %}
{{ EmptyState(
- 'This portfolio doesn’t 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 %}
diff --git a/tests/routes/portfolios/test_index.py b/tests/routes/portfolios/test_index.py
index ef368c3d..dbee544a 100644
--- a/tests/routes/portfolios/test_index.py
+++ b/tests/routes/portfolios/test_index.py
@@ -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=[
diff --git a/tests/test_access.py b/tests/test_access.py
index 24948ec2..668d66d9 100644
--- a/tests/test_access.py
+++ b/tests/test_access.py
@@ -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
diff --git a/tests/test_auth.py b/tests/test_auth.py
index d6160491..23dc46d2 100644
--- a/tests/test_auth.py
+++ b/tests/test_auth.py
@@ -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()
diff --git a/translations.yaml b/translations.yaml
index 387aecec..d63b1a5e 100644
--- a/translations.yaml
+++ b/translations.yaml
@@ -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: