diff --git a/atst/filters.py b/atst/filters.py index d0fe5bf7..524d39bb 100644 --- a/atst/filters.py +++ b/atst/filters.py @@ -1,6 +1,6 @@ import re import datetime -from atst.utils.localization import translate +from atst.utils.localization import translate, translate_duration from flask import current_app as app, render_template from jinja2 import contextfilter from jinja2.exceptions import TemplateNotFound @@ -97,6 +97,19 @@ def renderAuditEvent(event): return render_template("audit_log/events/default.html", event=event) +def removeHtml(text): + html_tags = re.compile("<.*?>") + return re.sub(html_tags, "", text) + + +def normalizeOrder(title): + # reorders titles from "Army, Department of the" to "Department of the Army" + text = title.split(", ") + reordered_text = text[0:-1] + reordered_text.insert(0, text[-1]) + return " ".join(reordered_text) + + def register_filters(app): app.jinja_env.filters["iconSvg"] = iconSvg app.jinja_env.filters["dollars"] = dollars @@ -110,6 +123,9 @@ def register_filters(app): app.jinja_env.filters["dateFromString"] = dateFromString app.jinja_env.filters["pageWindow"] = pageWindow app.jinja_env.filters["renderAuditEvent"] = renderAuditEvent + app.jinja_env.filters["removeHtml"] = removeHtml + app.jinja_env.filters["normalizeOrder"] = normalizeOrder + app.jinja_env.filters["translateDuration"] = translate_duration @contextfilter def translateWithoutCache(context, *kwargs): diff --git a/atst/forms/data.py b/atst/forms/data.py index 8aeda1e4..3afb2ec7 100644 --- a/atst/forms/data.py +++ b/atst/forms/data.py @@ -1,4 +1,6 @@ from atst.domain.roles import PORTFOLIO_ROLES as PORTFOLIO_ROLE_DEFINITIONS +from atst.utils.localization import translate, translate_duration + SERVICE_BRANCHES = [ ("", "Select an option"), @@ -176,67 +178,41 @@ FUNDING_TYPES = [ TASK_ORDER_SOURCES = [("MANUAL", "Manual"), ("EDA", "EDA")] APP_MIGRATION = [ - ("on_premise", "Yes, migrating from an on-premise data center"), - ("cloud", "Yes, migrating from another cloud provider"), - ( - "both", - "Yes, migrating from an on-premise data center and another cloud provider", - ), - ("none", "Not planning to migrate any applications"), - ("not_sure", "Not Sure"), + ("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", "Storage "), - ("data_analytics", "Data Analytics "), - ("conus", "CONUS Access "), - ("oconus", "OCONUS Access "), - ("tactical_edge", "Tactical Edge Access "), - ("not_sure", "Not Sure "), - ("other", "Other"), + ("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 = [ - ("government_civilians", "Government Civilians"), - ("military", "Military "), - ("contractor", "Contractor "), - ("other", "Other (E.g. University or other partner)"), + ( + "government_civilians", + translate("forms.task_order.dev_team.government_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", "No previous experience"), - ("planned", "Researched or planned a cloud build or migration"), - ("built_1", "Built or Migrated 1-2 applications"), - ("built_3", "Built or Migrated 3-5 applications"), - ( - "built_many", - "Built or migrated many applications, or consulted on several such applications", - ), + ("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")), ] PERIOD_OF_PERFORMANCE_LENGTH = [ - ("1", "1 Month"), - ("2", "2 Months"), - ("3", "3 Months"), - ("4", "4 Months"), - ("5", "5 Months"), - ("6", "6 Months"), - ("7", "7 Months"), - ("8", "8 Months"), - ("9", "9 Months"), - ("10", "10 Months"), - ("11", "11 Months"), - ("12", "1 Year"), - ("13", "1 Year, 1 Month"), - ("14", "1 Year, 2 Months"), - ("15", "1 Year, 3 Months"), - ("16", "1 Year, 4 Months"), - ("17", "1 Year, 5 Months"), - ("18", "1 Year, 6 Months"), - ("19", "1 Year, 7 Months"), - ("20", "1 Year, 8 Months"), - ("21", "1 Year, 9 Months"), - ("22", "1 Year, 10 Months"), - ("23", "1 Year, 11 Months"), - ("24", "2 Years"), + (str(x + 1), translate_duration(x + 1)) for x in range(24) ] diff --git a/atst/forms/task_order.py b/atst/forms/task_order.py index ce33476d..4935702f 100644 --- a/atst/forms/task_order.py +++ b/atst/forms/task_order.py @@ -39,8 +39,8 @@ class AppInfoForm(CacheableForm): translate("forms.task_order.defense_component_label"), choices=SERVICE_BRANCHES ) app_migration = RadioField( - translate("forms.task_order.app_migration_label"), - description=translate("forms.task_order.app_migration_description"), + translate("forms.task_order.app_migration.label"), + description=translate("forms.task_order.app_migration.description"), choices=APP_MIGRATION, default="", ) @@ -50,8 +50,8 @@ class AppInfoForm(CacheableForm): choices=[("yes", "Yes"), ("no", "No"), ("not_sure", "Not Sure")], ) complexity = SelectMultipleField( - translate("forms.task_order.complexity_label"), - description=translate("forms.task_order.complexity_description"), + translate("forms.task_order.complexity.label"), + description=translate("forms.task_order.complexity.description"), choices=APPLICATION_COMPLEXITY, default="", widget=ListWidget(prefix_label=False), @@ -59,8 +59,8 @@ class AppInfoForm(CacheableForm): ) complexity_other = StringField(translate("forms.task_order.complexity_other_label")) dev_team = SelectMultipleField( - translate("forms.task_order.dev_team_label"), - description=translate("forms.task_order.dev_team_description"), + translate("forms.task_order.dev_team.label"), + description=translate("forms.task_order.dev_team.description"), choices=DEV_TEAM, default="", widget=ListWidget(prefix_label=False), @@ -68,8 +68,8 @@ class AppInfoForm(CacheableForm): ) dev_team_other = StringField(translate("forms.task_order.dev_team_other_label")) team_experience = RadioField( - translate("forms.task_order.team_experience_label"), - description=translate("forms.task_order.team_experience_description"), + translate("forms.task_order.team_experience.label"), + description=translate("forms.task_order.team_experience.description"), choices=TEAM_EXPERIENCE, default="", ) @@ -77,7 +77,7 @@ class AppInfoForm(CacheableForm): class FundingForm(CacheableForm): performance_length = SelectField( - translate("forms.task_order.performance_length_label"), + translate("forms.task_order.performance_length.label"), choices=PERIOD_OF_PERFORMANCE_LENGTH, ) start_date = DateField( diff --git a/atst/utils/localization.py b/atst/utils/localization.py index 53802fca..fff301fd 100644 --- a/atst/utils/localization.py +++ b/atst/utils/localization.py @@ -1,6 +1,8 @@ import yaml import os from functools import lru_cache +import math +from gettext import ngettext from flask import current_app as app from atst.utils import getattr_path @@ -41,3 +43,14 @@ def translate(key, variables=None): raise LocalizationInvalidKeyError(key, variables) return value.format(**variables).replace("\n", "") + + +def translate_duration(duration_in_months): + duration = [] + years = math.floor(duration_in_months / 12) + months = duration_in_months % 12 + if years > 0: + duration.append("{} {}".format(years, ngettext("year", "years", years))) + if months > 0: + duration.append("{} {}".format(months, ngettext("month", "months", months))) + return (", ").join(duration) diff --git a/styles/atat.scss b/styles/atat.scss index 63889846..f2e91d6e 100644 --- a/styles/atat.scss +++ b/styles/atat.scss @@ -38,6 +38,7 @@ @import 'components/budget_chart'; @import 'components/audit_log'; @import 'components/usa_banner'; +@import 'components/checklist'; @import 'sections/login'; @import 'sections/home'; diff --git a/styles/components/_checklist.scss b/styles/components/_checklist.scss new file mode 100644 index 00000000..01921bfd --- /dev/null +++ b/styles/components/_checklist.scss @@ -0,0 +1,9 @@ +.checklist { + padding-left: 0; + list-style: none; + margin-top: 5px; + + li { + margin-bottom: 0; + } +} diff --git a/styles/elements/_icon_link.scss b/styles/elements/_icon_link.scss index 88e340ff..1c13e558 100644 --- a/styles/elements/_icon_link.scss +++ b/styles/elements/_icon_link.scss @@ -68,4 +68,8 @@ opacity: 0.3; pointer-events: none; } + + &.icon-link--left { + padding-left: 0; + } } diff --git a/styles/elements/_icons.scss b/styles/elements/_icons.scss index 3116125c..5703356d 100644 --- a/styles/elements/_icons.scss +++ b/styles/elements/_icons.scss @@ -55,7 +55,19 @@ @include icon-size(24); } - &.icon--remove { + &.icon--remove, &.icon--red { @include icon-color($color-red); } + + &.icon--green { + @include icon-color($color-green); + } + + &.icon--gray { + @include icon-color($color-gray); + } + + &.icon--medium { + @include icon-size(12); + } } diff --git a/styles/sections/_task_order.scss b/styles/sections/_task_order.scss index faae653f..3b85a4a7 100644 --- a/styles/sections/_task_order.scss +++ b/styles/sections/_task_order.scss @@ -179,3 +179,40 @@ } } } + +.task-order-form { + .task-order-form__heading { + margin-bottom: 0; + + &.inactive { + color: $color-gray-light; + } + + &.subheading { + color: $color-gray; + } + } + + .funding-summary__table { + tr td { + border-bottom: 0; + padding: 0.5rem 1.5rem; + + .funding-summary__td { + margin-top: 0; + } + } + } + + .task-order-invite-message { + &.not-sent { + color: $color-red; + font-weight: $font-bold; + } + + &.sent { + color: $color-green; + font-weight: $font-bold; + } + } +} diff --git a/templates/components/multi_checkbox_input.html b/templates/components/multi_checkbox_input.html index 99235e21..a5477dc0 100644 --- a/templates/components/multi_checkbox_input.html +++ b/templates/components/multi_checkbox_input.html @@ -33,10 +33,10 @@
{{ "task_orders.new.app_info.sample_scope" | translate | safe }}
@@ -20,20 +20,20 @@{{ "task_orders.new.app_info.market_research_paragraph" | translate | safe }}
{% endblock %} diff --git a/templates/task_orders/new/funding.html b/templates/task_orders/new/funding.html index 56f8f968..82417e2c 100644 --- a/templates/task_orders/new/funding.html +++ b/templates/task_orders/new/funding.html @@ -15,14 +15,14 @@{{ "task_orders.new.funding.performance_period_description" | translate }}
{{ "task_orders.new.funding.performance_period_paragraph" | translate }}
{{ OptionsInput(form.performance_length) }}{{ "task_orders.new.funding.estimate_usage_description" | translate }}