From 55c65b79a577ce08b7c1ec31609e149aa28bb112 Mon Sep 17 00:00:00 2001 From: kiddhustle Date: Fri, 28 Apr 2017 13:27:47 +0100 Subject: [PATCH 1/6] fix #DH176 sidebar navigation on details page to match designs and re-factor assets add tabwindow and add sourcemaps to npm run develop re-factor side tab nav js fix collapse icon state bug in tabbed window when switching between tablet and desktop views rename component to TabbedWindow --- package.json | 4 +- src/controllers/investmentcontroller.js | 13 ++- src/lib/components/TabbedWindow.js | 108 +++++++++++++++++++++ src/lib/pages/investment/details.js | 4 + src/sass/_leftnav.scss | 4 +- src/sass/application.scss | 1 + src/sass/components/_contentheader.scss | 4 +- src/sass/components/_projectstatusbar.scss | 9 +- src/sass/components/_tabbedwindow.scss | 63 ++++++++++++ src/sass/settings/_variables.scss | 6 +- src/views/investment/container.html | 24 ++++- webpack.config.js | 3 +- 12 files changed, 225 insertions(+), 18 deletions(-) create mode 100644 src/lib/components/TabbedWindow.js create mode 100644 src/lib/pages/investment/details.js create mode 100644 src/sass/components/_tabbedwindow.scss diff --git a/package.json b/package.json index 7a48d8b..e906349 100644 --- a/package.json +++ b/package.json @@ -64,14 +64,14 @@ "lint": "npm run lint:js & npm run lint:sass", "lint:js": "standard", "lint:sass": "sass-lint -c .sass-lint.yml 'src/**/*.scss' -v -q", - "develop": "parallel sass:watch js:client:watch js:server:watch", + "develop": "npm run build && parallel sass:watch js:client:watch js:server:watch", "client:watch": "parallel sass:watch js:client:watch", "js": "npm run js:client", "js:client": "webpack", "js:client:watch": "webpack --watch", "js:server": "babel src -d build", "js:server:watch": "nodemon --debug src/server.js", - "sass:watch": "node-sass src/sass/application.scss build/css/application.css --include-path ./node_modules/@uktrade/trade_elements/dist/sass/ --include-path ./node_modules/font-awesome/scss/ -r -w", + "sass:watch": "node-sass src/sass/application.scss build/css/application.css --source-map true --source-map-embed --include-path ./node_modules/@uktrade/trade_elements/dist/sass/ --include-path ./node_modules/font-awesome/scss/ -r -w", "sass": "node-sass src/sass/application.scss build/css/application.css --include-path ./node_modules/@uktrade/trade_elements/dist/sass/ --include-path ./node_modules/font-awesome/scss/ -x --output-style compressed", "postinstall": "npm run build", "sync": "browser-sync start --proxy http://localhost:3000 --files build/css/*.css build/javascripts/*.js" diff --git a/src/controllers/investmentcontroller.js b/src/controllers/investmentcontroller.js index 86adaf1..29c9112 100644 --- a/src/controllers/investmentcontroller.js +++ b/src/controllers/investmentcontroller.js @@ -301,6 +301,16 @@ function details (req, res) { advisor: ldetails.referral_source_manager.name } const tab = 'summary' + + const navItems = [ + {label: 'Project', url: `/investment/${sourceId}/details`, tab: 'summary'}, + {label: 'Client', url: `/investment/${sourceId}/details`, tab: 'contacts'}, + {label: 'Project Team', url: `/investment/${sourceId}/details`, tab: 'interactions'}, + {label: 'Interactions', url: `/investment/${sourceId}/details`, tab: 'interactions'}, + {label: 'Documents', url: `/investment/${sourceId}/details`, tab: 'interactions'}, + {label: 'Evaluation', url: `/investment/${sourceId}/details`, tab: 'interactions'}, + {label: 'Audit history', url: `/investment/${sourceId}/details`, tab: 'interactions'} + ] res.render('investment/details', { prospectStage, @@ -317,7 +327,8 @@ function details (req, res) { requirementsLabels, requirementsOrder, sourceId, - tab + tab, + navItems }) }) } diff --git a/src/lib/components/TabbedWindow.js b/src/lib/components/TabbedWindow.js new file mode 100644 index 0000000..c794f0c --- /dev/null +++ b/src/lib/components/TabbedWindow.js @@ -0,0 +1,108 @@ +// TODO move globally or into the config ? has to be the same as the media query for tablets +const enableIfScreenSmallerThan = 641 +const DOM_EL_WINDOW = '.tabbedwindow' +const DOM_EL_TAB = '.tabbedwindow__tab' +const DOM_EL_TAB_ICON = '.tabbedwindow__tab__icon' +const DOM_EL_NAV = '.tabbedwindow__nav' +const DOM_EL_PANE = '.tabbedwindow__pane' + +const CSS_CLASS_TAB = `${DOM_EL_TAB}`.replace('.', '') +const CSS_CLASS_TAB_ACTIVE = `${CSS_CLASS_TAB}--active` + +let toggleEnabled = null + +function TabbedWindow () { + this.toggleEnabled = null + this.domEl = document.querySelector(DOM_EL_WINDOW) + this.domElTabs = [].slice.call(document.querySelectorAll(DOM_EL_TAB)) + this.domElNav = document.querySelector(DOM_EL_NAV) + this.domElPane = document.querySelector(DOM_EL_PANE) +} + +TabbedWindow.prototype = { + onActiveItems: function () { + return document.querySelectorAll(CSS_CLASS_TAB_ACTIVE) + }, + getNonActiveItems: function () { + return this.domElTabs.filter(tab => tab.classList.contains(CSS_CLASS_TAB_ACTIVE) === false) + }, + disableActiveLink: function () { + return document.querySelector('.' + CSS_CLASS_TAB_ACTIVE).setAttribute('onclick', 'return false') + }, + + isItemCollapsed: function (item) { + let chevron = item.querySelector(DOM_EL_TAB_ICON) + return chevron.classList.contains('fa-chevron-down') + }, + + getActiveItem: function () { + return document.querySelector(`.${CSS_CLASS_TAB_ACTIVE}`) + }, + + setChevronUp: function (item) { + let chevron = item.querySelector(DOM_EL_TAB_ICON) + chevron.classList.remove('fa-chevron-down') + chevron.className += ' fa-chevron-up ' + }, + + setChevronDown: function (item) { + let chevron = item.querySelector(DOM_EL_TAB_ICON) + chevron.classList.remove('fa-chevron-up') + chevron.className += ' fa-chevron-down ' + }, + + show: function (items) { + [].forEach.call(items, function (a) { + a.style.display = null + }) + }, + + hide: function (items) { + [].forEach.call(items, function (a) { + a.style.display = 'none' + }) + }, + updateDisplay: function () { + // @TODO stop calling updateDisplay resize + // running dom queries on window resize is bad for performance + // switch to event driven UI (JS Events) + let screenIsSmall = document.documentElement.clientWidth <= enableIfScreenSmallerThan + if (screenIsSmall) { + this.hide(this.getNonActiveItems()) + this.toggleEnabled = true + this.disableActiveLink() + } else { + this.setChevronDown(this.getActiveItem()) + this.show(this.domElTabs) + this.toggleEnabled = false + } + }, + onTabClick: function (e) { + let el = e.target + if (!el.classList.contains(CSS_CLASS_TAB)) { + return + } + if (!this.toggleEnabled) { + return + } + if (this.isItemCollapsed(el)) { + this.setChevronUp(el) + this.show(this.getNonActiveItems()) + } else { + this.setChevronDown(el) + this.hide(this.getNonActiveItems()) + } + }, + init: function () { + // @TODO setinterval on resize callback being fired for performance reason + window.addEventListener('resize', this.updateDisplay.bind(this)) + window.addEventListener('load', this.updateDisplay.bind(this)) + // expand/collapse logic + + this.domEl.addEventListener('click', this.onTabClick.bind(this)) + console.log('TabWindow initialied!') + console.log(this) + } +} + +module.exports = TabbedWindow diff --git a/src/lib/pages/investment/details.js b/src/lib/pages/investment/details.js new file mode 100644 index 0000000..46ff083 --- /dev/null +++ b/src/lib/pages/investment/details.js @@ -0,0 +1,4 @@ +const TabbedWindow = require('../../components/TabbedWindow') +let oTabbedWindow = new TabbedWindow() + +oTabbedWindow.init() diff --git a/src/sass/_leftnav.scss b/src/sass/_leftnav.scss index 001ff23..f057354 100644 --- a/src/sass/_leftnav.scss +++ b/src/sass/_leftnav.scss @@ -22,7 +22,7 @@ color: $black; text-decoration: underline; } - + .collapse-filter { float: right; color: $white; @@ -66,5 +66,3 @@ } } - - diff --git a/src/sass/application.scss b/src/sass/application.scss index 29da94e..2144a67 100644 --- a/src/sass/application.scss +++ b/src/sass/application.scss @@ -10,6 +10,7 @@ $images-dir: '/images/'; // components @import 'components/contentheader'; @import 'components/projectstatusbar'; +@import 'components/tabbedwindow'; // application specific @import 'address'; @import 'facets'; diff --git a/src/sass/components/_contentheader.scss b/src/sass/components/_contentheader.scss index c4682cd..28f09cc 100644 --- a/src/sass/components/_contentheader.scss +++ b/src/sass/components/_contentheader.scss @@ -3,11 +3,11 @@ */ .contentheader { @extend .clearfix; - padding:1rem 0; + padding:1.5rem 0; margin-bottom: 1rem; border-bottom: 1px solid $grey-2; } -@media screen and (min-width:$mobile-break-point-max) { +@media screen and (min-width:$mobile-breakpoint-max) { .contentheader { clear:both; diff --git a/src/sass/components/_projectstatusbar.scss b/src/sass/components/_projectstatusbar.scss index 763cfe7..39278a9 100644 --- a/src/sass/components/_projectstatusbar.scss +++ b/src/sass/components/_projectstatusbar.scss @@ -3,10 +3,13 @@ */ .projectstatusbar { @extend .clearfix; + border-left: 10px solid $grey-2; + padding-left:1rem; + margin: 2rem 0; &__col { width:100%; float:left; - height:60px; + // height:60px; } &__col__heading, &__col__value { @@ -17,9 +20,9 @@ } } -@media screen and (min-width:$mobile-break-point-max) { +@media screen and (min-width:$mobile-breakpoint-max) { .projectstatusbar { - width:$mobile-break-point-max; + width:$mobile-breakpoint-max; &__col { width:50%; } diff --git a/src/sass/components/_tabbedwindow.scss b/src/sass/components/_tabbedwindow.scss new file mode 100644 index 0000000..b21b61f --- /dev/null +++ b/src/sass/components/_tabbedwindow.scss @@ -0,0 +1,63 @@ +.tabbedwindow { + @extend .clearfix; + // layout + &__nav, &__pane { + float:left; + } + &__nav { + width:25%; + // @extend %site-width-container; + // @extend %contain-floats; + // clear: both; + + } + &__pane { + margin-left:10%; + width:65%; + } + &__tab { + display:block; + height:50px; + padding-left:1rem; + vertical-align:middle; + line-height: 50px; + text-decoration: underline; + position:relative; + &:hover, &:visited { + color:$black; + } + &--active { + background:$govuk-blue; + color:$white; + font-weight: bold; + text-decoration: none; + &:hover, &:visited { + color:$white; + } + & .tabbedwindow__tab__icon { + display: block; + } + @include media (tablet) { + & .tabbedwindow__tab__icon { + display: none; + } + } + + } + &__icon { + color:$white; + position: absolute; + right:1rem; + top: 50%; + transform:translate3d(0, -50%, 0); + display: none; + @include media(tablet) { + display: none; + } + // &.fa { + // display:none; + // } + } + } + +} diff --git a/src/sass/settings/_variables.scss b/src/sass/settings/_variables.scss index edef56b..20ef125 100644 --- a/src/sass/settings/_variables.scss +++ b/src/sass/settings/_variables.scss @@ -3,4 +3,8 @@ * Eventually variables can be split across multiple files * @type {[type]} */ -$mobile-break-point-max:320px; + +// $tablet-breakpoint: 641px !default; +// $desktop-breakpoint: 769px !default; +$mobile-breakpoint-max:320px; +$tablet-breakpoint-min:640px; diff --git a/src/views/investment/container.html b/src/views/investment/container.html index c6feb12..ea60334 100644 --- a/src/views/investment/container.html +++ b/src/views/investment/container.html @@ -5,8 +5,6 @@ {% import "macros/trade.html" as trade %} {% import "./investment_macros.html" as local %} - -

{{ projectTitle | default('Project title goes here') }}

@@ -19,7 +17,22 @@

{{ projectTitle | default('Project title goes here') } {% include "./partials/projectstatusbar.html" %}

-
+
+ +
+ {% block tabmain%}{% endblock %} +
+
+ + + {#
#}
- + {# #} + diff --git a/webpack.config.js b/webpack.config.js index 83dd781..23b0ee7 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -9,7 +9,8 @@ module.exports = { companyedit: './src/forms/companyedit', investment: './src/forms/investment', createinvestment: './src/forms/createinvestment', - leftnav: './src/lib/leftnav' + leftnav: './src/lib/leftnav', + investment_details: './src/lib/pages/investment/details' }, output: { path: 'build/javascripts', From cd0718b6d77828a5d164022be3a5f6286e058155 Mon Sep 17 00:00:00 2001 From: kiddhustle Date: Thu, 4 May 2017 17:10:48 +0100 Subject: [PATCH 2/6] removed console logs and implement event-based architecture --- package.json | 1 + src/lib/components/TabbedWindow.js | 122 ++++++++++++++++++------- src/sass/components/_tabbedwindow.scss | 8 -- 3 files changed, 91 insertions(+), 40 deletions(-) diff --git a/package.json b/package.json index e906349..51b70a7 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "eslint-config-standard": "^6.2.1", "eslint-plugin-promise": "^3.4.0", "eslint-plugin-standard": "^2.0.1", + "eventemitter3": "^2.0.3", "express": "^4.14.0", "express-session": "^1.14.1", "express-validator": "^3.1.1", diff --git a/src/lib/components/TabbedWindow.js b/src/lib/components/TabbedWindow.js index c794f0c..63d02d2 100644 --- a/src/lib/components/TabbedWindow.js +++ b/src/lib/components/TabbedWindow.js @@ -1,3 +1,5 @@ +const {debounce, extend} = require('lodash') +const EventEmitter = require('eventemitter3') // TODO move globally or into the config ? has to be the same as the media query for tablets const enableIfScreenSmallerThan = 641 const DOM_EL_WINDOW = '.tabbedwindow' @@ -9,20 +11,44 @@ const DOM_EL_PANE = '.tabbedwindow__pane' const CSS_CLASS_TAB = `${DOM_EL_TAB}`.replace('.', '') const CSS_CLASS_TAB_ACTIVE = `${CSS_CLASS_TAB}--active` -let toggleEnabled = null +/** + * TabbedWindow constructor + * @extends EventEmitter + * + * toggleaccordion event + * @event TabbedWindow#toggleaccordion + * @type {object} + * @property {string} accordionState - (open|closed) + * + * accordionenabled event fired when the mobile breakpoint is triggered + * @event TabbedWindow#accordionenabled + * @type {boolean} + * accordiondisabled event fired when the desktop breakpoint is triggered + * @event TabbedWindow#accordiondisabled + * @type {boolean} + * + + * accordionopened event + * @event TabbedWindow#accordionopened + * @type {object} + * + * accordionclosed event + * @event TabbedWindow#accordionclosed + * @type {object} + */ function TabbedWindow () { - this.toggleEnabled = null + // initialise EvemtEmitter + EventEmitter.call(this) + this.accordionEnabled = false + this.accordionState = 'closed' this.domEl = document.querySelector(DOM_EL_WINDOW) this.domElTabs = [].slice.call(document.querySelectorAll(DOM_EL_TAB)) this.domElNav = document.querySelector(DOM_EL_NAV) this.domElPane = document.querySelector(DOM_EL_PANE) } -TabbedWindow.prototype = { - onActiveItems: function () { - return document.querySelectorAll(CSS_CLASS_TAB_ACTIVE) - }, +TabbedWindow.prototype = extend(EventEmitter.prototype, { getNonActiveItems: function () { return this.domElTabs.filter(tab => tab.classList.contains(CSS_CLASS_TAB_ACTIVE) === false) }, @@ -62,47 +88,79 @@ TabbedWindow.prototype = { a.style.display = 'none' }) }, + /** + * called (via a debounce) on window resize. Emits an event when breakpoint is changed (tabletbreakpoint|desktopbreakpoint) + * @return {void} + */ updateDisplay: function () { - // @TODO stop calling updateDisplay resize - // running dom queries on window resize is bad for performance - // switch to event driven UI (JS Events) let screenIsSmall = document.documentElement.clientWidth <= enableIfScreenSmallerThan - if (screenIsSmall) { - this.hide(this.getNonActiveItems()) - this.toggleEnabled = true - this.disableActiveLink() - } else { - this.setChevronDown(this.getActiveItem()) - this.show(this.domElTabs) - this.toggleEnabled = false + if (screenIsSmall && this.accordionEnabled === false) { + this.emit('tabletbreakpoint') + } else if (!screenIsSmall && this.accordionEnabled === true) { + this.emit('desktopbreakpoint') } }, + /** + * click event callback for tab navigation items + * @param {DOMEvent} e dom event + * @return {void} + */ onTabClick: function (e) { let el = e.target - if (!el.classList.contains(CSS_CLASS_TAB)) { - return - } - if (!this.toggleEnabled) { + let payload = {target: el} + if (!el.classList.contains(CSS_CLASS_TAB) || !this.accordionEnabled) { return } if (this.isItemCollapsed(el)) { - this.setChevronUp(el) - this.show(this.getNonActiveItems()) + this.accordionState = 'open' + this.emit('toggleaccordion', payload) } else { - this.setChevronDown(el) - this.hide(this.getNonActiveItems()) + this.accordionState = 'closed' + this.emit('toggleaccordion', payload) + } + }, + /** + * callback for toggleaccordion event + * @param {object} payload contains event data + * @return {void} + */ + onAccordionStateToggled: function (payload) { + if (this.accordionState === 'open') { + this.openAccordion(payload) + } else if (this.accordionState === 'closed') { + this.closeAccordion(payload) } }, + openAccordion: function (payload) { + this.setChevronUp(payload.target) + this.show(this.getNonActiveItems()) + this.emit('accordionopened') + }, + closeAccordion: function (payload) { + this.setChevronDown(payload.target) + this.hide(this.getNonActiveItems()) + this.emit('accordionclosed') + }, init: function () { - // @TODO setinterval on resize callback being fired for performance reason - window.addEventListener('resize', this.updateDisplay.bind(this)) + // event listeners + // dom events + window.addEventListener('resize', debounce(this.updateDisplay.bind(this), 100)) window.addEventListener('load', this.updateDisplay.bind(this)) - // expand/collapse logic - this.domEl.addEventListener('click', this.onTabClick.bind(this)) - console.log('TabWindow initialied!') - console.log(this) + // custom component events + this.on('toggleaccordion', this.onAccordionStateToggled.bind(this)) + this.on('tabletbreakpoint', function (e) { + this.accordionEnabled = true + this.hide(this.getNonActiveItems()) + this.disableActiveLink() + }) + this.on('desktopbreakpoint', function (e) { + this.accordionEnabled = false + this.accordionState = 'closed' + this.setChevronDown(this.getActiveItem()) + this.show(this.domElTabs) + }) } -} +}) module.exports = TabbedWindow diff --git a/src/sass/components/_tabbedwindow.scss b/src/sass/components/_tabbedwindow.scss index b21b61f..556c4db 100644 --- a/src/sass/components/_tabbedwindow.scss +++ b/src/sass/components/_tabbedwindow.scss @@ -6,10 +6,6 @@ } &__nav { width:25%; - // @extend %site-width-container; - // @extend %contain-floats; - // clear: both; - } &__pane { margin-left:10%; @@ -42,7 +38,6 @@ display: none; } } - } &__icon { color:$white; @@ -54,9 +49,6 @@ @include media(tablet) { display: none; } - // &.fa { - // display:none; - // } } } From 8ed07352f21f58d698eca570664f770487168c1f Mon Sep 17 00:00:00 2001 From: kiddhustle Date: Thu, 4 May 2017 17:14:13 +0100 Subject: [PATCH 3/6] stray sass comment --- src/sass/settings/_variables.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sass/settings/_variables.scss b/src/sass/settings/_variables.scss index 20ef125..bc7c653 100644 --- a/src/sass/settings/_variables.scss +++ b/src/sass/settings/_variables.scss @@ -4,7 +4,5 @@ * @type {[type]} */ -// $tablet-breakpoint: 641px !default; -// $desktop-breakpoint: 769px !default; $mobile-breakpoint-max:320px; $tablet-breakpoint-min:640px; From 2ea3e0cd1812888bce61a97a25e2da44dcd45dc3 Mon Sep 17 00:00:00 2001 From: kiddhustle Date: Thu, 4 May 2017 17:15:16 +0100 Subject: [PATCH 4/6] stray template comment --- src/views/investment/container.html | 41 ----------------------------- 1 file changed, 41 deletions(-) diff --git a/src/views/investment/container.html b/src/views/investment/container.html index ea60334..605c631 100644 --- a/src/views/investment/container.html +++ b/src/views/investment/container.html @@ -30,47 +30,6 @@

{{ projectTitle | default('Project title goes here') } {% block tabmain%}{% endblock %}

- - - {#
-
- -
- -
- {% block tabmain%}{% endblock %} - - -
#}
From e0937483de5093a3cb4ca7cb054f08bf63951106 Mon Sep 17 00:00:00 2001 From: kiddhustle Date: Mon, 8 May 2017 12:38:02 +0100 Subject: [PATCH 5/6] remove inline styling from JS use classes instead --- src/lib/components/TabbedWindow.js | 5 +++-- src/sass/components/_tabbedwindow.scss | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib/components/TabbedWindow.js b/src/lib/components/TabbedWindow.js index 63d02d2..619f83b 100644 --- a/src/lib/components/TabbedWindow.js +++ b/src/lib/components/TabbedWindow.js @@ -10,6 +10,7 @@ const DOM_EL_PANE = '.tabbedwindow__pane' const CSS_CLASS_TAB = `${DOM_EL_TAB}`.replace('.', '') const CSS_CLASS_TAB_ACTIVE = `${CSS_CLASS_TAB}--active` +const CSS_CLASS_TAB_HIDDEN = `${CSS_CLASS_TAB}--hidden` /** * TabbedWindow constructor @@ -79,13 +80,13 @@ TabbedWindow.prototype = extend(EventEmitter.prototype, { show: function (items) { [].forEach.call(items, function (a) { - a.style.display = null + a.classList.remove(CSS_CLASS_TAB_HIDDEN) }) }, hide: function (items) { [].forEach.call(items, function (a) { - a.style.display = 'none' + a.classList.add(CSS_CLASS_TAB_HIDDEN) }) }, /** diff --git a/src/sass/components/_tabbedwindow.scss b/src/sass/components/_tabbedwindow.scss index 556c4db..f478023 100644 --- a/src/sass/components/_tabbedwindow.scss +++ b/src/sass/components/_tabbedwindow.scss @@ -39,6 +39,9 @@ } } } + &--hidden { + display: none; + } &__icon { color:$white; position: absolute; From f77d2173fe6b7b33b45b0fc8f94223f389bf6b6d Mon Sep 17 00:00:00 2001 From: kiddhustle Date: Mon, 8 May 2017 12:49:48 +0100 Subject: [PATCH 6/6] fix styling cascade for tab icons --- src/sass/components/_tabbedwindow.scss | 3 +++ src/views/layouts/ukti.html | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sass/components/_tabbedwindow.scss b/src/sass/components/_tabbedwindow.scss index f478023..abf7601 100644 --- a/src/sass/components/_tabbedwindow.scss +++ b/src/sass/components/_tabbedwindow.scss @@ -19,6 +19,9 @@ line-height: 50px; text-decoration: underline; position:relative; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; &:hover, &:visited { color:$black; } diff --git a/src/views/layouts/ukti.html b/src/views/layouts/ukti.html index b58082d..bdf65e7 100644 --- a/src/views/layouts/ukti.html +++ b/src/views/layouts/ukti.html @@ -5,8 +5,8 @@ {% block head_styles %} - + {% endblock %} {% block head_script %} @@ -32,7 +32,7 @@ {% endif %} - +
{% block main %}{% endblock %}