Refactor New Portfolio page according to designs.
New designs call for a streamlined New Portfolio page, with far fewer input options. This commit refactors that page according to those designs. Some of the route functions in this commit refer to a "step 1" of creating a new Portfolio. Though there is no "step 2" right now, the designs call for a multistep flow for Portfolio creation process, so this commit sets the stage for that.
This commit is contained in:
parent
f9a3d2628e
commit
a097a0ce61
@ -0,0 +1,40 @@
|
||||
"""Remove unneeded portfolio columns
|
||||
|
||||
Revision ID: 802071bcd013
|
||||
Revises: 67a2151d6269
|
||||
Create Date: 2019-12-11 13:26:34.770480
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '802071bcd013' # pragma: allowlist secret
|
||||
down_revision = '67a2151d6269' # pragma: allowlist secret
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('portfolios', 'dev_team')
|
||||
op.drop_column('portfolios', 'complexity')
|
||||
op.drop_column('portfolios', 'team_experience')
|
||||
op.drop_column('portfolios', 'dev_team_other')
|
||||
op.drop_column('portfolios', 'app_migration')
|
||||
op.drop_column('portfolios', 'native_apps')
|
||||
op.drop_column('portfolios', 'complexity_other')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('portfolios', sa.Column('complexity_other', sa.VARCHAR(), autoincrement=False, nullable=True))
|
||||
op.add_column('portfolios', sa.Column('native_apps', sa.VARCHAR(), autoincrement=False, nullable=True))
|
||||
op.add_column('portfolios', sa.Column('app_migration', sa.VARCHAR(), autoincrement=False, nullable=True))
|
||||
op.add_column('portfolios', sa.Column('dev_team_other', sa.VARCHAR(), autoincrement=False, nullable=True))
|
||||
op.add_column('portfolios', sa.Column('team_experience', sa.VARCHAR(), autoincrement=False, nullable=True))
|
||||
op.add_column('portfolios', sa.Column('complexity', postgresql.ARRAY(sa.VARCHAR()), autoincrement=False, nullable=True))
|
||||
op.add_column('portfolios', sa.Column('dev_team', postgresql.ARRAY(sa.VARCHAR()), autoincrement=False, nullable=True))
|
||||
# ### end Alembic commands ###
|
@ -3,112 +3,14 @@ from atst.utils.localization import translate
|
||||
|
||||
|
||||
SERVICE_BRANCHES = [
|
||||
("", "- Select -"),
|
||||
("Air Force, Department of the", "Air Force, Department of the"),
|
||||
("Army and Air Force Exchange Service", "Army and Air Force Exchange Service"),
|
||||
("Army, Department of the", "Army, Department of the"),
|
||||
("air_force", translate("forms.portfolio.defense_component.choices.air_force")),
|
||||
("army", translate("forms.portfolio.defense_component.choices.army")),
|
||||
(
|
||||
"Defense Advanced Research Applications Agency",
|
||||
"Defense Advanced Research Applications Agency",
|
||||
"marine_corps",
|
||||
translate("forms.portfolio.defense_component.choices.marine_corps"),
|
||||
),
|
||||
("Defense Commissary Agency", "Defense Commissary Agency"),
|
||||
("Defense Contract Audit Agency", "Defense Contract Audit Agency"),
|
||||
("Defense Contract Management Agency", "Defense Contract Management Agency"),
|
||||
("Defense Finance & Accounting Service", "Defense Finance & Accounting Service"),
|
||||
("Defense Health Agency", "Defense Health Agency"),
|
||||
("Defense Information System Agency", "Defense Information System Agency"),
|
||||
("Defense Intelligence Agency", "Defense Intelligence Agency"),
|
||||
("Defense Legal Services Agency", "Defense Legal Services Agency"),
|
||||
("Defense Logistics Agency", "Defense Logistics Agency"),
|
||||
("Defense Media Activity", "Defense Media Activity"),
|
||||
("Defense Micro Electronics Activity", "Defense Micro Electronics Activity"),
|
||||
("Defense POW-MIA Accounting Agency", "Defense POW-MIA Accounting Agency"),
|
||||
("Defense Security Cooperation Agency", "Defense Security Cooperation Agency"),
|
||||
("Defense Security Service", "Defense Security Service"),
|
||||
("Defense Technical Information Center", "Defense Technical Information Center"),
|
||||
(
|
||||
"Defense Technology Security Administration",
|
||||
"Defense Technology Security Administration",
|
||||
),
|
||||
("Defense Threat Reduction Agency", "Defense Threat Reduction Agency"),
|
||||
("DoD Education Activity", "DoD Education Activity"),
|
||||
("DoD Human Recourses Activity", "DoD Human Recourses Activity"),
|
||||
("DoD Inspector General", "DoD Inspector General"),
|
||||
("DoD Test Resource Management Center", "DoD Test Resource Management Center"),
|
||||
(
|
||||
"Headquarters Defense Human Resource Activity ",
|
||||
"Headquarters Defense Human Resource Activity ",
|
||||
),
|
||||
("Joint Staff", "Joint Staff"),
|
||||
("Missile Defense Agency", "Missile Defense Agency"),
|
||||
("National Defense University", "National Defense University"),
|
||||
(
|
||||
"National Geospatial Intelligence Agency (NGA)",
|
||||
"National Geospatial Intelligence Agency (NGA)",
|
||||
),
|
||||
(
|
||||
"National Oceanic and Atmospheric Administration (NOAA)",
|
||||
"National Oceanic and Atmospheric Administration (NOAA)",
|
||||
),
|
||||
("National Reconnaissance Office", "National Reconnaissance Office"),
|
||||
("National Reconnaissance Office (NRO)", "National Reconnaissance Office (NRO)"),
|
||||
("National Security Agency (NSA)", "National Security Agency (NSA)"),
|
||||
(
|
||||
"National Security Agency-Central Security Service",
|
||||
"National Security Agency-Central Security Service",
|
||||
),
|
||||
("Navy, Department of the", "Navy, Department of the"),
|
||||
("Office of Economic Adjustment", "Office of Economic Adjustment"),
|
||||
("Office of the Secretary of Defense", "Office of the Secretary of Defense"),
|
||||
("Pentagon Force Protection Agency", "Pentagon Force Protection Agency"),
|
||||
(
|
||||
"Uniform Services University of the Health Sciences",
|
||||
"Uniform Services University of the Health Sciences",
|
||||
),
|
||||
("US Cyber Command (USCYBERCOM)", "US Cyber Command (USCYBERCOM)"),
|
||||
(
|
||||
"US Special Operations Command (USSOCOM)",
|
||||
"US Special Operations Command (USSOCOM)",
|
||||
),
|
||||
("US Strategic Command (USSTRATCOM)", "US Strategic Command (USSTRATCOM)"),
|
||||
(
|
||||
"US Transportation Command (USTRANSCOM)",
|
||||
"US Transportation Command (USTRANSCOM)",
|
||||
),
|
||||
("Washington Headquarters Services", "Washington Headquarters Services"),
|
||||
]
|
||||
|
||||
APP_MIGRATION = [
|
||||
("on_premise", translate("forms.task_order.app_migration.on_premise")),
|
||||
("cloud", translate("forms.task_order.app_migration.cloud")),
|
||||
("both", translate("forms.task_order.app_migration.both")),
|
||||
("none", translate("forms.task_order.app_migration.none")),
|
||||
("not_sure", translate("forms.task_order.app_migration.not_sure")),
|
||||
]
|
||||
|
||||
APPLICATION_COMPLEXITY = [
|
||||
("storage", translate("forms.task_order.complexity.storage")),
|
||||
("data_analytics", translate("forms.task_order.complexity.data_analytics")),
|
||||
("conus", translate("forms.task_order.complexity.conus")),
|
||||
("oconus", translate("forms.task_order.complexity.oconus")),
|
||||
("tactical_edge", translate("forms.task_order.complexity.tactical_edge")),
|
||||
("not_sure", translate("forms.task_order.complexity.not_sure")),
|
||||
("other", translate("forms.task_order.complexity.other")),
|
||||
]
|
||||
|
||||
DEV_TEAM = [
|
||||
("civilians", translate("forms.task_order.dev_team.civilians")),
|
||||
("military", translate("forms.task_order.dev_team.military")),
|
||||
("contractor", translate("forms.task_order.dev_team.contractor")),
|
||||
("other", translate("forms.task_order.dev_team.other")),
|
||||
]
|
||||
|
||||
TEAM_EXPERIENCE = [
|
||||
("none", translate("forms.task_order.team_experience.none")),
|
||||
("planned", translate("forms.task_order.team_experience.planned")),
|
||||
("built_1", translate("forms.task_order.team_experience.built_1")),
|
||||
("built_3", translate("forms.task_order.team_experience.built_3")),
|
||||
("built_many", translate("forms.task_order.team_experience.built_many")),
|
||||
("navy", translate("forms.portfolio.defense_component.choices.navy")),
|
||||
("other", translate("forms.portfolio.defense_component.choices.other")),
|
||||
]
|
||||
|
||||
ENV_ROLE_NO_ACCESS = "No Access"
|
||||
|
@ -1,33 +1,25 @@
|
||||
from wtforms.fields import (
|
||||
RadioField,
|
||||
SelectField,
|
||||
SelectMultipleField,
|
||||
StringField,
|
||||
TextAreaField,
|
||||
)
|
||||
from wtforms.validators import Length, Optional
|
||||
from wtforms.validators import Length, InputRequired
|
||||
from wtforms.widgets import ListWidget, CheckboxInput
|
||||
|
||||
from .forms import BaseForm, remove_empty_string
|
||||
from .forms import BaseForm
|
||||
from atst.utils.localization import translate
|
||||
|
||||
from .data import (
|
||||
APPLICATION_COMPLEXITY,
|
||||
APP_MIGRATION,
|
||||
DEV_TEAM,
|
||||
SERVICE_BRANCHES,
|
||||
TEAM_EXPERIENCE,
|
||||
)
|
||||
from .data import SERVICE_BRANCHES
|
||||
|
||||
|
||||
class PortfolioForm(BaseForm):
|
||||
name = StringField(
|
||||
translate("forms.portfolio.name_label"),
|
||||
translate("forms.portfolio.name.label"),
|
||||
validators=[
|
||||
Length(
|
||||
min=4,
|
||||
max=100,
|
||||
message=translate("forms.portfolio.name_length_validation_message"),
|
||||
message=translate("forms.portfolio.name.length_validation_message"),
|
||||
)
|
||||
],
|
||||
)
|
||||
@ -35,78 +27,21 @@ class PortfolioForm(BaseForm):
|
||||
|
||||
class PortfolioCreationForm(BaseForm):
|
||||
name = StringField(
|
||||
translate("forms.portfolio.name_label"),
|
||||
translate("forms.portfolio.name.label"),
|
||||
validators=[
|
||||
Length(
|
||||
min=4,
|
||||
max=100,
|
||||
message=translate("forms.portfolio.name_length_validation_message"),
|
||||
message=translate("forms.portfolio.name.length_validation_message"),
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
defense_component = SelectField(
|
||||
translate("forms.task_order.defense_component_label"),
|
||||
description = TextAreaField(translate("forms.portfolio.description.label"),)
|
||||
defense_component = SelectMultipleField(
|
||||
choices=SERVICE_BRANCHES,
|
||||
default="",
|
||||
filters=[remove_empty_string],
|
||||
)
|
||||
|
||||
description = TextAreaField(
|
||||
translate("forms.task_order.scope_label"),
|
||||
description=translate("forms.task_order.scope_description"),
|
||||
)
|
||||
|
||||
app_migration = RadioField(
|
||||
translate("forms.task_order.app_migration.label"),
|
||||
description=translate("forms.task_order.app_migration.description"),
|
||||
choices=APP_MIGRATION,
|
||||
default="",
|
||||
validators=[Optional()],
|
||||
)
|
||||
|
||||
native_apps = RadioField(
|
||||
translate("forms.task_order.native_apps.label"),
|
||||
description=translate("forms.task_order.native_apps.description"),
|
||||
choices=[("yes", "Yes"), ("no", "No"), ("not_sure", "Not Sure")],
|
||||
default="",
|
||||
validators=[Optional()],
|
||||
)
|
||||
|
||||
complexity = SelectMultipleField(
|
||||
translate("forms.task_order.complexity.label"),
|
||||
description=translate("forms.task_order.complexity.description"),
|
||||
choices=APPLICATION_COMPLEXITY,
|
||||
default=None,
|
||||
widget=ListWidget(prefix_label=False),
|
||||
option_widget=CheckboxInput(),
|
||||
)
|
||||
|
||||
complexity_other = StringField(
|
||||
translate("forms.task_order.complexity_other_label"),
|
||||
default=None,
|
||||
filters=[remove_empty_string],
|
||||
)
|
||||
|
||||
dev_team = SelectMultipleField(
|
||||
translate("forms.task_order.dev_team.label"),
|
||||
description=translate("forms.task_order.dev_team.description"),
|
||||
choices=DEV_TEAM,
|
||||
default=None,
|
||||
widget=ListWidget(prefix_label=False),
|
||||
option_widget=CheckboxInput(),
|
||||
)
|
||||
|
||||
dev_team_other = StringField(
|
||||
translate("forms.task_order.dev_team_other_label"),
|
||||
default=None,
|
||||
filters=[remove_empty_string],
|
||||
)
|
||||
|
||||
team_experience = RadioField(
|
||||
translate("forms.task_order.team_experience.label"),
|
||||
description=translate("forms.task_order.team_experience.description"),
|
||||
choices=TEAM_EXPERIENCE,
|
||||
default="",
|
||||
validators=[Optional()],
|
||||
validators=[
|
||||
InputRequired(message="You must select at least one defense component.")
|
||||
],
|
||||
)
|
||||
|
@ -1,6 +1,5 @@
|
||||
from sqlalchemy import Column, String
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.types import ARRAY
|
||||
from itertools import chain
|
||||
|
||||
from atst.models.base import Base
|
||||
@ -19,19 +18,11 @@ class Portfolio(
|
||||
|
||||
id = types.Id()
|
||||
name = Column(String, nullable=False)
|
||||
description = Column(String)
|
||||
defense_component = Column(
|
||||
String, nullable=False
|
||||
) # Department of Defense Component
|
||||
|
||||
app_migration = Column(String) # App Migration
|
||||
complexity = Column(ARRAY(String)) # Application Complexity
|
||||
complexity_other = Column(String)
|
||||
description = Column(String)
|
||||
dev_team = Column(ARRAY(String)) # Development Team
|
||||
dev_team_other = Column(String)
|
||||
native_apps = Column(String) # Native Apps
|
||||
team_experience = Column(String) # Team Experience
|
||||
|
||||
applications = relationship(
|
||||
"Application",
|
||||
back_populates="portfolio",
|
||||
|
@ -12,10 +12,9 @@ from atst.utils.flash import formatted_flash as flash
|
||||
|
||||
|
||||
@portfolios_bp.route("/portfolios/new")
|
||||
def new_portfolio():
|
||||
def new_portfolio_step_1():
|
||||
form = PortfolioCreationForm()
|
||||
|
||||
return render_template("portfolios/new.html", form=form)
|
||||
return render_template("portfolios/new/step_1.html", form=form)
|
||||
|
||||
|
||||
@portfolios_bp.route("/portfolios", methods=["POST"])
|
||||
@ -28,7 +27,7 @@ def create_portfolio():
|
||||
url_for("applications.portfolio_applications", portfolio_id=portfolio.id)
|
||||
)
|
||||
else:
|
||||
return render_template("portfolios/new.html", form=form), 400
|
||||
return render_template("portfolios/new/step_1.html", form=form), 400
|
||||
|
||||
|
||||
@portfolios_bp.route("/portfolios/<portfolio_id>/reports")
|
||||
|
@ -15,7 +15,7 @@ PASSWORD = os.getenv("ATAT_BA_PASSWORD", "")
|
||||
DISABLE_VERIFY = os.getenv("DISABLE_VERIFY", "true").lower() == "true"
|
||||
|
||||
# Alpha numerics for random entity names
|
||||
LETTERS = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890"
|
||||
LETTERS = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890" #pragma: allowlist secret
|
||||
|
||||
NEW_PORTFOLIO_CHANCE = 10
|
||||
NEW_APPLICATION_CHANCE = 10
|
||||
@ -141,15 +141,8 @@ def create_portfolio(l):
|
||||
new_portfolio_form = l.client.get("/portfolios/new")
|
||||
new_portfolio_body = {
|
||||
"name": f"Load Test Created - {''.join(choices(LETTERS, k=5))}",
|
||||
"defense_component": "Army, Department of the",
|
||||
"defense_component": "army",
|
||||
"description": "Test",
|
||||
"app_migration": "none",
|
||||
"native_apps": "yes",
|
||||
"complexity": "storage",
|
||||
"complexity_other": "",
|
||||
"dev_team": "civilians",
|
||||
"dev_team_other": "",
|
||||
"team_experience": "none",
|
||||
"csrf_token": get_csrf_token(new_portfolio_form),
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
{% endif %}
|
||||
|
||||
{% call StickyCTA(sticky_header) %}
|
||||
<a href="{{ url_for("portfolios.new_portfolio") }}" class="usa-button-primary">
|
||||
<a href="{{ url_for("portfolios.new_portfolio_step_1") }}" class="usa-button-primary">
|
||||
{{ "home.add_portfolio_button_text" | translate }}
|
||||
</a>
|
||||
{% endcall %}
|
||||
|
@ -1,54 +0,0 @@
|
||||
{% from "components/multi_checkbox_input.html" import MultiCheckboxInput %}
|
||||
{% from "components/options_input.html" import OptionsInput %}
|
||||
{% from "components/save_button.html" import SaveButton %}
|
||||
{% from "components/text_input.html" import TextInput %}
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<main class="usa-section usa-content">
|
||||
{% include "fragments/flash.html" %}
|
||||
<h1>New Portfolio Form</h1>
|
||||
<base-form inline-template>
|
||||
<form class="panel__content" id="portfolio-create" action="{{ url_for('portfolios.create_portfolio') }}" method="POST">
|
||||
{{ form.csrf_token }}
|
||||
|
||||
{{ TextInput(form.name, optional=False) }}
|
||||
{{ OptionsInput(form.defense_component, optional=False) }}
|
||||
{{ TextInput(form.description, paragraph=True) }}
|
||||
|
||||
<h3 id="reporting" class="subheading">{{ "task_orders.new.app_info.project_title" | translate }}</h3>
|
||||
|
||||
<hr>
|
||||
|
||||
{{ OptionsInput(form.app_migration) }}
|
||||
|
||||
{{ OptionsInput(form.native_apps) }}
|
||||
<p>{{ "forms.task_order.native_apps.not_sure_help" | translate }}</p>
|
||||
{{ MultiCheckboxInput(form.complexity, form.complexity_other) }}
|
||||
|
||||
<hr>
|
||||
|
||||
<h3 class="subheading">{{ "task_orders.new.app_info.team_title" | translate }}</h3>
|
||||
<p>{{ "task_orders.new.app_info.subtitle" | translate }}</p>
|
||||
{{ MultiCheckboxInput(form.dev_team, form.dev_team_other) }}
|
||||
{{ OptionsInput(form.team_experience) }}
|
||||
|
||||
<hr>
|
||||
|
||||
<div class='action-group'>
|
||||
{{
|
||||
SaveButton(
|
||||
text=('common.save' | translate),
|
||||
form="portfolio-create",
|
||||
element="input",
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</form>
|
||||
</base-form>
|
||||
</main>
|
||||
|
||||
{% endblock %}
|
||||
|
45
templates/portfolios/new/step_1.html
Normal file
45
templates/portfolios/new/step_1.html
Normal file
@ -0,0 +1,45 @@
|
||||
{% from "components/multi_checkbox_input.html" import MultiCheckboxInput %}
|
||||
{% from "components/options_input.html" import OptionsInput %}
|
||||
{% from "components/save_button.html" import SaveButton %}
|
||||
{% from "components/text_input.html" import TextInput %}
|
||||
{% from "components/sticky_cta.html" import StickyCTA %}
|
||||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<main class="usa-section usa-content">
|
||||
{% include "fragments/flash.html" %}
|
||||
<div class='portfolio-header__name'>
|
||||
<p>{{ "portfolios.header" | translate }}</p>
|
||||
<h1>{{ "New Portfolio" }}</h1>
|
||||
</div>
|
||||
{{ StickyCTA(text="Name and Describe Portfolio", context="Step 1 of 2") }}
|
||||
<base-form inline-template>
|
||||
<form id="portfolio-create" action="{{ url_for('portfolios.create_portfolio') }}" method="POST">
|
||||
{{ form.csrf_token }}
|
||||
{{ TextInput(form.name, optional=False) }}
|
||||
{{"forms.portfolio.name.help_text" | translate | safe }}
|
||||
<div>
|
||||
{{ TextInput(form.description, paragraph=True) }}
|
||||
{{"forms.portfolio.description.help_text" | translate | safe }}
|
||||
</div>
|
||||
<div>
|
||||
<strong class="h4">{{ "forms.portfolio.defense_component.label" | translate }}</strong>
|
||||
{{ "forms.portfolio.defense_component.help_text" | translate | safe }}
|
||||
{{ MultiCheckboxInput(form.defense_component, optional=False) }}
|
||||
</div>
|
||||
<div class='action-group'>
|
||||
{{
|
||||
SaveButton(
|
||||
text=('common.save' | translate),
|
||||
form="portfolio-create",
|
||||
element="input",
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</form>
|
||||
</base-form>
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
@ -105,13 +105,7 @@ class PortfolioFactory(Base):
|
||||
|
||||
name = factory.Faker("domain_word")
|
||||
defense_component = factory.LazyFunction(random_service_branch)
|
||||
|
||||
app_migration = random_choice(data.APP_MIGRATION)
|
||||
complexity = [random_choice(data.APPLICATION_COMPLEXITY)]
|
||||
description = factory.Faker("sentence")
|
||||
dev_team = [random_choice(data.DEV_TEAM)]
|
||||
native_apps = random.choice(["yes", "no", "not_sure"])
|
||||
team_experience = random_choice(data.TEAM_EXPERIENCE)
|
||||
|
||||
@classmethod
|
||||
def _create(cls, model_class, *args, **kwargs):
|
||||
|
@ -18,7 +18,7 @@ def test_new_portfolio(client, user_session):
|
||||
user = UserFactory.create()
|
||||
user_session(user)
|
||||
|
||||
response = client.get(url_for("portfolios.new_portfolio"))
|
||||
response = client.get(url_for("portfolios.new_portfolio_step_1"))
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
@ -34,7 +34,7 @@ def test_create_portfolio_success(client, user_session):
|
||||
data={
|
||||
"name": "My project name",
|
||||
"description": "My project description",
|
||||
"defense_component": "Air Force, Department of the",
|
||||
"defense_component": "army",
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -25,7 +25,7 @@ _NO_ACCESS_CHECK_REQUIRED = _NO_LOGIN_REQUIRED + [
|
||||
"dev.test_email", # dev tool
|
||||
"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.new_portfolio_step_1", # all users can create a portfolio
|
||||
"task_orders.get_started", # all users can start a new TO
|
||||
"users.update_user", # available to all users
|
||||
"users.user", # available to all users
|
||||
|
@ -166,8 +166,49 @@ forms:
|
||||
portfolio_mgmt: Portfolio management
|
||||
reporting: Reporting
|
||||
portfolio:
|
||||
name_label: Portfolio name
|
||||
name_length_validation_message: Portfolio names can be between 4-100 characters
|
||||
name:
|
||||
label: Portfolio Name
|
||||
length_validation_message: Portfolio names can be between 4-100 characters
|
||||
help_text: |
|
||||
<div>
|
||||
<p>
|
||||
Naming can be difficult. Choose a name that is descriptive enough for users to identify the Portfolio. You may consider naming based on your organization.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Writer's Block? A naming example</strong>
|
||||
<ul>
|
||||
<li>Design Support for Army Developers</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
description:
|
||||
label: Portfolio Description
|
||||
help_text: |
|
||||
<div>
|
||||
<p>
|
||||
Add a brief one to two sentence description of your Portfolio. Consider this your statement of work.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Writer's Block? A description example includes:</strong>
|
||||
<ul>
|
||||
<li>Build security applications for FOB Clark</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
defense_component:
|
||||
label: "Select DoD component(s) funding your Portfolio:"
|
||||
choices:
|
||||
air_force: Air Force
|
||||
army: Army
|
||||
marine_corps: Marine Corps
|
||||
navy: Navy
|
||||
other: Other
|
||||
help_text: |
|
||||
<p>
|
||||
Select the DOD component(s) that will fund all Applications within this Portfolio.
|
||||
In JEDI, multiple DoD organizations can fund the same Portfolio.<br/>
|
||||
Select all that apply.<br/>
|
||||
</p>
|
||||
attachment:
|
||||
object_name:
|
||||
length_error: Object name may be no longer than 40 characters.
|
||||
@ -176,41 +217,8 @@ forms:
|
||||
task_order:
|
||||
upload_error: There was an error uploading your file. Please try again. If you encounter repeated problems uploading this file, please contact CCPO.
|
||||
size_error: The file you have selected is too large. Please choose a file no larger than 64MB.
|
||||
app_migration:
|
||||
both: 'Yes, migrating from both an on-premise data center <strong>and</strong> another cloud provider'
|
||||
cloud: 'Yes, migrating from another cloud provider'
|
||||
description: Do you plan to migrate one or more existing application(s) to the cloud?
|
||||
label: App migration
|
||||
none: Not planning to migrate any applications
|
||||
not_sure: Not sure
|
||||
on_premise: 'Yes, migrating from an on-premise data center'
|
||||
complexity:
|
||||
conus: CONUS access
|
||||
data_analytics: Data analytics
|
||||
description: Which of these describes how complex your team's use of the cloud will be? Select all that apply.
|
||||
label: Project complexity
|
||||
not_sure: Not sure
|
||||
oconus: OCONUS access
|
||||
other: Other
|
||||
storage: Storage
|
||||
tactical_edge: Tactical edge access
|
||||
complexity_other_label: Project Complexity Other
|
||||
defense_component_label: Department of Defense component
|
||||
dev_team:
|
||||
civilians: Government civilians
|
||||
contractor: Contractor
|
||||
description: Who is building your cloud application(s)? Select all that apply.
|
||||
label: Development team
|
||||
military: Military
|
||||
other: Other <em class='description'>(E.g. University or other partner)</em>
|
||||
dev_team_other_label: Development Team Other
|
||||
defense_component_label: Select DoD component(s) funding your Portfolio
|
||||
file_format_not_allowed: Only PDF or PNG files can be uploaded.
|
||||
native_apps:
|
||||
description: Do you plan to develop any applications in the cloud?
|
||||
label: Native apps
|
||||
'no': 'No, not planning to develop natively in the cloud'
|
||||
not_sure: 'Not sure, unsure if planning to develop natively in the cloud'
|
||||
not_sure_help: Not sure? Talk to your technical lead about where and how they plan on developing your application.
|
||||
number_description: Task order number (13 digits)
|
||||
pop_errors:
|
||||
date_order: PoP start date must be before end date.
|
||||
@ -219,8 +227,6 @@ forms:
|
||||
end_pre_contract: PoP end date must be after or on {date}.
|
||||
start_past_contract: PoP start date must be before or on {date}.
|
||||
start_pre_contract: PoP start date must be on or after {date}.
|
||||
scope_description: 'What do you plan to do on the cloud? Some examples might include migrating an existing application or creating a prototype. You don’t need to include a detailed plan of execution, but should list key requirements. This section will be reviewed by your contracting officer, but won’t be sent to the CCPO. <p>Not sure how to describe your scope? <a href="#">Read some examples</a> to get some inspiration.</p>'
|
||||
scope_label: Cloud project scope
|
||||
clin_funding_errors:
|
||||
obligated_amount_error: Obligated amount must be less than or equal to total amount
|
||||
funding_range_error: Dollar amount must be from $0.00 to $1,000,000,000.00
|
||||
|
Loading…
x
Reference in New Issue
Block a user