diff --git a/atst/routes/portfolios/applications.py b/atst/routes/portfolios/applications.py index e675d438..20603b3c 100644 --- a/atst/routes/portfolios/applications.py +++ b/atst/routes/portfolios/applications.py @@ -64,11 +64,16 @@ def edit_application(portfolio_id, application_id): application = Applications.get(application_id) form = ApplicationForm(name=application.name, description=application.description) + environments_obj = {} + for env in application.environments: + environments_obj[env.name] = [user.full_name for user in env.users] + return render_template( "portfolios/applications/edit.html", portfolio=portfolio, application=application, form=form, + environments_obj=environments_obj, ) diff --git a/js/components/tables/application_environments.js b/js/components/tables/application_environments.js new file mode 100644 index 00000000..c91e63a2 --- /dev/null +++ b/js/components/tables/application_environments.js @@ -0,0 +1,34 @@ +import { set } from 'vue/dist/vue' + +export default { + name: 'environments-table', + + props: { + environments: Object, + }, + + data: function() { + return { + environmentsState: this.environments, + } + }, + + created: function() { + Object.keys(this.environments).forEach(environment => { + set(this.environmentsState[environment], 'isVisible', false) + }) + }, + + methods: { + toggle: function(e, environmentName) { + this.environmentsState = Object.assign(this.environmentsState, { + [environmentName]: Object.assign( + this.environmentsState[environmentName], + { + isVisible: !this.environmentsState[environmentName].isVisible, + } + ), + }) + }, + }, +} diff --git a/js/index.js b/js/index.js index 0a419224..dee29320 100644 --- a/js/index.js +++ b/js/index.js @@ -25,6 +25,7 @@ import Modal from './mixins/modal' import selector from './components/selector' import BudgetChart from './components/charts/budget_chart' import SpendTable from './components/tables/spend_table' +import EnvironmentsTable from './components/tables/application_environments' import TaskOrderList from './components/tables/task_order_list.js' import MembersList from './components/members_list' import LocalDatetime from './components/local_datetime' @@ -56,6 +57,7 @@ const app = new Vue({ selector, BudgetChart, SpendTable, + EnvironmentsTable, TaskOrderList, MembersList, LocalDatetime, diff --git a/styles/atat.scss b/styles/atat.scss index fc13952f..bd947aea 100644 --- a/styles/atat.scss +++ b/styles/atat.scss @@ -12,7 +12,7 @@ @import 'elements/buttons'; @import 'elements/panels'; @import 'elements/block_lists'; -@import 'elements/accordians'; +@import 'elements/accordions'; @import 'elements/tables'; @import 'elements/sidenav'; @import 'elements/action_group'; @@ -23,6 +23,7 @@ @import 'elements/graphs'; @import 'elements/menu'; +@import 'components/accordion_table'; @import 'components/topbar'; @import 'components/top_message'; @import 'components/global_layout'; diff --git a/styles/components/_accordion_table.scss b/styles/components/_accordion_table.scss new file mode 100644 index 00000000..53153470 --- /dev/null +++ b/styles/components/_accordion_table.scss @@ -0,0 +1,66 @@ +.accordion-table { + + table { + thead th { + text-transform: uppercase; + border-bottom: 1px solid $color-gray-lightest; + border-top: none; + } + + th, td { + white-space: nowrap; + + button { + margin: 0; + } + } + + .accordion-table__items { + .accordion-table__item__toggler { + @include icon-link-color($color-blue, $color-gray-lightest); + float: right; + margin-left: -$gap; + color: $color-blue; + + .icon { + @include icon-size(12); + margin-right: $gap; + } + + .open-indicator { + position: absolute; + bottom: 0; + left: 5 * $gap; + width: 0; + height: 0; + border-left: 10px solid transparent; + border-right: 10px solid transparent; + + border-bottom: 10px solid $color-blue-light; + } + } + + th, td, tr { + border-bottom: 1px dashed $color-gray-lightest; + } + + th[scope=rowgroup] { + position: relative; + } + + .accordion-table__item__expanded { + margin-left: 2 * $gap; + + th, td { + .icon-link { + font-weight: $font-normal; + font-size: $base-font-size; + } + + border-bottom: 1px dashed $color-white; + background-color: $color-gray-lightest; + } + } + } + } +} diff --git a/styles/components/_portfolio_layout.scss b/styles/components/_portfolio_layout.scss index 359e4a5f..906e14a6 100644 --- a/styles/components/_portfolio_layout.scss +++ b/styles/components/_portfolio_layout.scss @@ -171,26 +171,6 @@ padding-bottom: 0; } - .member-list-header { - margin: 2 * $gap 5 * $gap; - padding: inherit; - overflow: auto; - - .left { - float: left; - padding-bottom: 0.8rem; - } - - .icon-link { - float: right; - margin-top: 0.8rem; - } - - .icon { - - } - } - .subheading { font-size: 1.4rem; color: $color-gray; @@ -326,15 +306,6 @@ } } - .members-table-footer { - float: right; - padding: 3 * $gap 0; - - .action-group.save { - padding-right: 3 * $gap; - } - } - a.modal-link.icon-link { float: right; diff --git a/styles/elements/_accordians.scss b/styles/elements/_accordions.scss similarity index 91% rename from styles/elements/_accordians.scss rename to styles/elements/_accordions.scss index 61ef3c68..62a8d66a 100644 --- a/styles/elements/_accordians.scss +++ b/styles/elements/_accordions.scss @@ -1,4 +1,4 @@ -.accordian { +.accordion { @include block-list; box-shadow: 0 4px 10px 0 rgba(193,193,193,0.5); @@ -21,7 +21,7 @@ } } -.accordian__header { +.accordion__header { @include block-list-header; border-top: 3px solid $color-blue; border-bottom: none; @@ -32,7 +32,7 @@ } } -.accordian__title { +.accordion__title { @include block-list__title; color: $color-blue; @include h3; @@ -45,14 +45,14 @@ } } -.accordian__description { +.accordion__description { @include block-list__description; font-style: italic; font-size: $small-font-size; color: $color-gray; } -.accordian__actions { +.accordion__actions { margin-top: $gap; display: flex; flex-direction: row; @@ -76,14 +76,14 @@ } } -.accordian__item { +.accordion__item { @include block-list-item; opacity: 0.75; background-color: $color-blue-light; border-bottom: 1px solid rgba($color-gray-light, 0.5); - &.accordian__item--selectable { + &.accordion__item--selectable { > div { display: flex; flex-direction: row-reverse; @@ -117,7 +117,7 @@ } } -.accordian__footer { +.accordion__footer { @include block-list__footer; border-top: 0; } diff --git a/styles/elements/_panels.scss b/styles/elements/_panels.scss index 572a165a..0036e474 100644 --- a/styles/elements/_panels.scss +++ b/styles/elements/_panels.scss @@ -107,6 +107,10 @@ } } + .panel__footer { + padding: 3 * $gap; + } + hr { border: 0; border-bottom: 1px dashed $color-gray-light; diff --git a/styles/elements/_tables.scss b/styles/elements/_tables.scss index a4a418ce..7b8da846 100644 --- a/styles/elements/_tables.scss +++ b/styles/elements/_tables.scss @@ -102,6 +102,25 @@ overflow-x: auto; @include panel-margin; + .responsive-table-wrapper__header { + @include panel-base; + @include panel-theme-default; + border-top: none; + border-bottom: 0; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + padding: $gap * 2; + + .responsive-table-wrapper__title { + @include h4; + font-size: $lead-font-size; + flex: 2; + } + } + table { margin-bottom: 0; } diff --git a/styles/sections/_application_list.scss b/styles/sections/_application_list.scss index 101c249e..eaf41127 100644 --- a/styles/sections/_application_list.scss +++ b/styles/sections/_application_list.scss @@ -24,7 +24,41 @@ } } - header.accordian__header { - padding: 1.6rem; + .list-header { + margin: 2 * $gap 5 * $gap; + padding: inherit; + overflow: auto; + } + + .icon-link { + .icon--info { + bottom: -1px; + left: 4px; + } + } + + table { + + thead { + td { + font-weight: bold; + font-size: 1.4rem; + border-top: 0; + } + } + + tbody { + th { + font-weight: bold; + font-size: 1.6rem; + } + td { + font-size: 1.6rem; + border-bottom: 1px solid $color-gray-lightest; + border-top: 0; + padding: 3 * $gap 2 * $gap; + } + } + } } diff --git a/styles/sections/_reports.scss b/styles/sections/_reports.scss index b881cbad..6aa1e5e1 100644 --- a/styles/sections/_reports.scss +++ b/styles/sections/_reports.scss @@ -201,143 +201,60 @@ } } +.spend-table__month-select { + margin: 0; + flex: 1; +} -.spend-table { - box-shadow: 0 6px 18px 0 rgba(144,164,183,0.3); - - .spend-table__header { - @include panel-base; - @include panel-theme-default; - border-top: none; - border-bottom: 0; - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: space-between; - align-items: center; - padding: $gap * 2; - - .spend-table__title { - @include h4; - font-size: $lead-font-size; - flex: 2; - } - - .spend-table__month-select { - margin: 0; - flex: 1; +table { + .spend-table__portfolio { + th, td { + font-weight: bold; + border-bottom: 1px solid $color-gray-lightest; } } - table { - thead th { - text-transform: uppercase; - border-bottom: 1px solid $color-gray-lightest; - border-top: none; + th, td { + &.previous-month { + color: $color-gray; } - th, td { - white-space: nowrap; + &.meter-cell { + padding-left: 0; + position: relative; + min-width: 4rem; - button { - margin: 0; + @include media($medium-screen) { + min-width: 12rem; } - &.previous-month { - color: $color-gray; - } - - &.meter-cell { - padding-left: 0; - position: relative; - min-width: 4rem; + meter { + width: 100%; + height: 3rem; + background: $color-white; + display: none; @include media($medium-screen) { - min-width: 12rem; + display: block; } - meter { - width: 100%; - height: 3rem; + &::-webkit-meter-bar { background: $color-white; - display: none; - - @include media($medium-screen) { - display: block; - } - - &::-webkit-meter-bar { - background: $color-white; - } - } - - .spend-table__meter-value { - @include h5; - - @include media($medium-screen) { - display: block; - color: $color-white; - background-color: rgba($color-blue, 0.65); - border-radius: $gap/2; - position: absolute; - top: 2.3rem; - left: $gap / 2; - padding: 0 ($gap / 2); - } } } - } - .spend-table__portfolio { - th, td { - font-weight: bold; - border-bottom: 1px solid $color-gray-lightest; - } - } + .spend-table__meter-value { + @include h5; - .spend-table__application { - .spend-table__application__toggler { - @include icon-link-color($color-blue, $color-gray-lightest); - margin-left: -$gap; - color: $color-blue; - - .icon { - @include icon-size(12); - margin-right: $gap; - } - - .open-indicator { + @include media($medium-screen) { + display: block; + color: $color-white; + background-color: rgba($color-blue, 0.65); + border-radius: $gap/2; position: absolute; - bottom: 0; - left: 5 * $gap; - width: 0; - height: 0; - border-left: 10px solid transparent; - border-right: 10px solid transparent; - - border-bottom: 10px solid $color-blue-light; - } - } - - th, td { - border-bottom: none; - } - - th[scope=rowgroup] { - position: relative; - } - - .spend-table__application__env { - margin-left: 2 * $gap; - - th, td { - .icon-link { - font-weight: $font-normal; - font-size: $base-font-size; - } - - border-bottom: 1px dashed $color-white; - background-color: $color-blue-light; + top: 2.3rem; + left: $gap / 2; + padding: 0 ($gap / 2); } } } diff --git a/templates/fragments/admin/portfolio_members.html b/templates/fragments/admin/portfolio_members.html index 75b90078..0c87abe8 100644 --- a/templates/fragments/admin/portfolio_members.html +++ b/templates/fragments/admin/portfolio_members.html @@ -6,7 +6,7 @@ {% from "components/alert.html" import Alert %}
-
+
{% if g.matchesPath("portfolio-members") %} {% include "fragments/flash.html" %} {% endif %} @@ -14,53 +14,62 @@
{{ member_perms_form.csrf_token }} -
-
-
{{ "portfolios.admin.portfolio_members_title" | translate }}
-
- {{ "portfolios.admin.portfolio_members_subheading" | translate }} +
+
+
+
+
{{ "portfolios.admin.portfolio_members_title" | translate }}
+
+ {{ "portfolios.admin.portfolio_members_subheading" | translate }} +
+
+ + + {{ Icon('info') }} + {{ "portfolios.admin.settings_info" | translate }} +
+
+ + {% if not portfolio.members %} +

{{ "portfolios.admin.no_members" | translate }}

+ {% else %} + + + + + + + + + + + + + + + {% if user_can(permissions.EDIT_PORTFOLIO_USERS) %} + {% include "fragments/admin/members_edit.html" %} + {% elif user_can(permissions.VIEW_PORTFOLIO_USERS) %} + {% include "fragments/admin/members_view.html" %} + {% endif %} + + +
{{ "portfolios.members.permissions.name" | translate }}{{ "portfolios.members.permissions.app_mgmt" | translate }}{{ "portfolios.members.permissions.funding" | translate }}{{ "portfolios.members.permissions.reporting" | translate }}{{ "portfolios.members.permissions.portfolio_mgmt" | translate }}
- - {{ Icon('info') }} - {{ "portfolios.admin.settings_info" | translate }} - -
- - {% if not portfolio.members %} -

{{ "portfolios.admin.no_members" | translate }}

- {% else %} - - - - - - - - - - - - - - - {% if user_can(permissions.EDIT_PORTFOLIO_USERS) %} - {% include "fragments/admin/members_edit.html" %} - {% elif user_can(permissions.VIEW_PORTFOLIO_USERS) %} - {% include "fragments/admin/members_view.html" %} - {% endif %} - - -
{{ "portfolios.members.permissions.name" | translate }}{{ "portfolios.members.permissions.app_mgmt" | translate }}{{ "portfolios.members.permissions.funding" | translate }}{{ "portfolios.members.permissions.reporting" | translate }}{{ "portfolios.members.permissions.portfolio_mgmt" | translate }}
-
diff --git a/templates/fragments/edit_application_form.html b/templates/fragments/applications/edit_application_form.html similarity index 100% rename from templates/fragments/edit_application_form.html rename to templates/fragments/applications/edit_application_form.html diff --git a/templates/fragments/applications/environments.html b/templates/fragments/applications/environments.html new file mode 100644 index 00000000..6300cd51 --- /dev/null +++ b/templates/fragments/applications/environments.html @@ -0,0 +1,50 @@ +{% from "components/icon.html" import Icon %} + +
+
+
+
+
{{ 'portfolios.applications.environments_heading' | translate }}
+
+ + {{ Icon('info') }} + {{ "portfolios.admin.settings_info" | translate }} + +
+
+ + + + + + + + + + + + + + + + + + + + + + +
{{ "portfolios.applications.environments.name" | translate }}{{ "portfolios.applications.environments.members" | translate }}
+
+ +
+
+
+
diff --git a/templates/portfolios/applications/edit.html b/templates/portfolios/applications/edit.html index c648f2f7..db842f80 100644 --- a/templates/portfolios/applications/edit.html +++ b/templates/portfolios/applications/edit.html @@ -1,6 +1,7 @@ {% extends "portfolios/applications/base.html" %} {% from "components/text_input.html" import TextInput %} +{% from "components/icon.html" import Icon %} {% set secondary_breadcrumb = 'portfolios.applications.existing_application_title' | translate({ "application_name": application.name }) %} @@ -12,33 +13,28 @@
- {% include "fragments/edit_application_form.html" %} - -
-
-

{{ 'portfolios.applications.environments_heading' | translate }}

-

- {{ 'portfolios.applications.environments_description' | translate }} -

-
- -
    - {% for environment in application.environments %} -
  • -
    - - -
    -
  • - {% endfor %} -
-
+ {% include "fragments/applications/edit_application_form.html" %}
-
-
- +
+
+ {% include "fragments/applications/environments.html" %} + +
+ {% endblock %} diff --git a/templates/portfolios/applications/index.html b/templates/portfolios/applications/index.html index 51a3ce56..0ecddde4 100644 --- a/templates/portfolios/applications/index.html +++ b/templates/portfolios/applications/index.html @@ -34,13 +34,13 @@
{% for application in portfolio.applications|sort(attribute='name') %} -
+