diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..a842d4515 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,47 @@ +module.exports = { + 'env': { + 'browser': true, + 'es6': true, + 'node': true + }, + 'extends': [ + 'eslint:recommended', + 'plugin:vue/recommended' + ], + 'globals': { + 'Atomics': 'readonly', + 'SharedArrayBuffer': 'readonly' + }, + 'parserOptions': { + 'ecmaVersion': 2018, + 'sourceType': 'module' + }, + 'plugins': [ + 'vue' + ], + 'rules': { + 'indent': [ + 'error', + 2 + ], + 'linebreak-style': [ + 'error', + 'unix' + ], + 'quotes': [ + 'error', + 'single' + ], + 'semi': [ + 'error', + 'never' + ], + 'padding-line-between-statements': [ + 'error', + { 'blankLine': 'always', 'prev': '*', 'next': 'return' }, + { 'blankLine': 'always', 'prev': ['const', 'let', 'var'], 'next': '*' }, + { 'blankLine': 'any', 'prev': ['const', 'let', 'var'], 'next': ['const', 'let', 'var'] } + ], + 'no-console': 'off' + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2b4fc0a9d..b5f4dced2 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,7 @@ tmp/cache/webpacker .byebug_history -.vscode/ \ No newline at end of file +.vscode/ + +# Special gemfile with GDAL v3 +Gemfile_GDALV3 \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index e9e019370..fa9550625 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "db"] path = db url = https://github.com/unepwcmc/protectedplanet-db.git - branch = refresh + branch = ticket-13 \ No newline at end of file diff --git a/Gemfile b/Gemfile index c4b32dc9c..fcb113ab7 100644 --- a/Gemfile +++ b/Gemfile @@ -1,8 +1,13 @@ source 'https://rubygems.org' +source 'https://wcmc-gems:SDvUM6ZG@gem-server.unep-wcmc.org/' gem 'rails', '5.2.0' gem 'webpacker', '~> 4.0.2' +#gem 'wcmc-components', path: "../web-components/gems/wcmc_components" +gem 'wcmc-components', '~>0.0.5' + + gem 'bourbon' gem "neat" diff --git a/Gemfile.lock b/Gemfile.lock index 6bbf04532..d933b0b20 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,6 +7,7 @@ GIT GEM remote: https://rubygems.org/ + remote: https://wcmc-gems:SDvUM6ZG@gem-server.unep-wcmc.org/ specs: actioncable (5.2.0) actionpack (= 5.2.0) @@ -1283,6 +1284,7 @@ GEM vuejs-rails (2.3.2) warden (1.2.8) rack (>= 2.0.6) + wcmc-components (0.0.5) webmock (1.22.6) addressable (>= 2.3.6) crack (>= 0.3.2) @@ -1366,6 +1368,7 @@ DEPENDENCIES turnout (~> 2.5.0) uglifier (~> 4.1.17) vuejs-rails (~> 2.3.2) + wcmc-components (~> 0.0.5) webmock (~> 1.22.0) webpacker (~> 4.0.2) whenever diff --git a/app/assets/images/icons/cross-white.svg b/app/assets/images/icons/cross-white.svg new file mode 100644 index 000000000..b9c36f07f --- /dev/null +++ b/app/assets/images/icons/cross-white.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/images/icons/cross.svg b/app/assets/images/icons/cross.svg index a3d264a67..b5e224721 100644 --- a/app/assets/images/icons/cross.svg +++ b/app/assets/images/icons/cross.svg @@ -1,3 +1,3 @@ - + diff --git a/app/assets/images/icons/minus-white.svg b/app/assets/images/icons/minus-white.svg new file mode 100644 index 000000000..fce4033b8 --- /dev/null +++ b/app/assets/images/icons/minus-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/minus.svg b/app/assets/images/icons/minus.svg new file mode 100644 index 000000000..b038188da --- /dev/null +++ b/app/assets/images/icons/minus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/icons/pin-outline.svg b/app/assets/images/icons/pin-outline.svg new file mode 100644 index 000000000..664e3d49a --- /dev/null +++ b/app/assets/images/icons/pin-outline.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/app/assets/images/icons/pin.svg b/app/assets/images/icons/pin.svg new file mode 100644 index 000000000..4fd38c1de --- /dev/null +++ b/app/assets/images/icons/pin.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/app/assets/images/icons/plus.svg b/app/assets/images/icons/plus.svg new file mode 100644 index 000000000..bb70d937e --- /dev/null +++ b/app/assets/images/icons/plus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/assets/images/logos/green-list-black.png b/app/assets/images/logos/green-list-black.png new file mode 100644 index 000000000..bed241a46 Binary files /dev/null and b/app/assets/images/logos/green-list-black.png differ diff --git a/app/assets/images/logos/green-list.png b/app/assets/images/logos/green-list.png index 140d43def..66e1670b5 100644 Binary files a/app/assets/images/logos/green-list.png and b/app/assets/images/logos/green-list.png differ diff --git a/app/assets/stylesheets/_settings.scss b/app/assets/stylesheets/_settings.scss index 7d0aa36d8..b2d3568c8 100644 --- a/app/assets/stylesheets/_settings.scss +++ b/app/assets/stylesheets/_settings.scss @@ -116,6 +116,7 @@ $fw-black: 900; // breakpoints //-------------------------------------------------- // must be in px not rem! +// IF MODIFIED, PLEASE CHECK ELSEWHERE IN PROJECT e.g. in mixin-responsive.js $small: 767px; $medium: 1024px; $large: 1200px; @@ -130,7 +131,7 @@ $z-300: 300; //-------------------------------------------------- // padding //-------------------------------------------------- -$gutter-small: rem-calc(25); +$gutter-small: rem-calc(20); $gutter-medium: rem-calc(25); $gutter-large: rem-calc(30); @@ -165,6 +166,8 @@ $site-width-rem: rem-calc($site-width); // $site-width-medium-laptop: 78%; $site-width-medium-desktop: rem-calc(750); +$filters-menu-max-height: rem-calc(300); + //-------------------------------------------------- // tables //-------------------------------------------------- diff --git a/app/assets/stylesheets/base/_buttons.scss b/app/assets/stylesheets/base/_buttons.scss index b3d3cf820..ff0a42bfc 100644 --- a/app/assets/stylesheets/base/_buttons.scss +++ b/app/assets/stylesheets/base/_buttons.scss @@ -79,18 +79,33 @@ $padding-medium: rem-calc(27); display: inline-block; } } -@mixin button-outline ($colour: $black) { +@mixin button-outline ($colour: $black, $border-size: 1px) { @include button-basic; @include button-font; @include button-height; @include button-padding; @include button-radius; - border: solid 2px $colour; + border: solid $border-size $colour; color: $colour; } + //-------------------------------------------------- // mixins for classes //-------------------------------------------------- +@mixin button-all($small: false) { + @include button-with-icon; + @include flex-no-shrink; + &::after { @include icon-circle-chevron-black; } + + @if $small { + @include breakpoint($medium) { font-size: rem-calc(16); } + + &::after { + height: rem-calc(21); + width: rem-calc(21); + } + } +} @mixin button-burger { @include button-basic; @include icon-burger; @@ -121,6 +136,30 @@ $padding-medium: rem-calc(27); @include button-with-icon; &::after { @include icon-chevron-black-up; } } + +@mixin button-dropdown-filter { + @include button-dropdown; + @include button-outline($black, 1px); + + @include breakpoint-down($medium) { + font-size: rem-calc(16); + padding: rem-calc(1 11); + height: rem-calc(34); + } + + &:after { + @include breakpoint-down($medium) { display: none; } + } +} + +@mixin button-external { + @include button-with-icon; + &::after { + @include icon-arrow-external; + display: inline-block; + + } +} @mixin button-filter-trigger { @include button-with-icon; @include button-outline; @@ -134,8 +173,19 @@ $padding-medium: rem-calc(27); @mixin button-map-trigger { @include button-with-icon; @include button-outline; + white-space: nowrap; + &::after { @include icon-pin-outline; } + + &.active { + &::after { + @include icon-cross-small; + + width: rem-calc(14); + } + } } +@mixin button-primary { @include button-block; } @mixin button-next { @include button-basic; } @mixin button-prev { @include button-basic; } @mixin button-search { @@ -180,13 +230,22 @@ $padding-medium: rem-calc(27); //-------------------------------------------------- .button { &--accent { @include button-block; } - &--all { + &--all { @include button-all; } + &--download { @include button-with-icon; - @include flex-no-shrink; - &::after { @include icon-circle-chevron-black; } + &::after { + @include icon-download; + display: inline-block; + } } - &--download { + &--download-pdf { @include button-with-icon; + color: $primary; + font-size: rem-calc(16); + font-weight: $bold; + @include breakpoint($small) { font-size: rem-calc(16); } + @include breakpoint($medium) { font-size: rem-calc(16); } + &::after { @include icon-download; display: inline-block; @@ -198,14 +257,16 @@ $padding-medium: rem-calc(27); font-weight: $bold; &::after { @include icon-arrow-external-white; } } - &--link-external { - @include button-with-icon; - &::after { - @include icon-arrow-external; - display: inline-block; - } + &--link-external { @include button-external; } + &--link-external-primary { + @include button-external; + color: $primary; + font-size: rem-calc(16); + font-weight: $bold; + @include breakpoint($small) { font-size: rem-calc(16); } + @include breakpoint($medium) { font-size: rem-calc(16); } } &--outline-black { @include button-outline($black); } &--outline-white { @include button-outline($white); } - &--primary { @include button-block; } -} \ No newline at end of file + &--primary { @include button-primary; } +} diff --git a/app/assets/stylesheets/base/_svgs.scss b/app/assets/stylesheets/base/_svgs.scss new file mode 100644 index 000000000..5a91e76f7 --- /dev/null +++ b/app/assets/stylesheets/base/_svgs.scss @@ -0,0 +1,14 @@ +//-------------------------------------------------- +// classes +//-------------------------------------------------- +.svg { + &--info { + .circle { fill: $grey-dark; } + .i { fill: none; } + } + + &--info-light { + .circle { fill: $grey-xlight; } + .i { fill: none; } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/base/mixins/_icons.scss b/app/assets/stylesheets/base/mixins/_icons.scss index 55572508a..daf858287 100644 --- a/app/assets/stylesheets/base/mixins/_icons.scss +++ b/app/assets/stylesheets/base/mixins/_icons.scss @@ -82,6 +82,16 @@ $icon-search-width: rem-calc(21); @include icon-image('cross.svg', rem-calc(20), rem-calc(20)); } +@mixin icon-cross-small() { + @include icon-basic; + @include icon-image('cross.svg', rem-calc(11), rem-calc(11)); +} + +@mixin icon-cross-white() { + @include icon-basic; + @include icon-image('cross-white.svg', rem-calc(20), rem-calc(20)); +} + @mixin icon-chevron-black-down() { @include icon-basic; @include icon-image('chevron-black-down.svg', rem-calc(14), rem-calc(8)); @@ -179,6 +189,11 @@ $icon-search-width: rem-calc(21); @include icon-image('loading-spinner.svg', rem-calc(40), rem-calc(40)); } +@mixin icon-minus-white() { + @include icon-basic; + @include icon-image('minus-white.svg', rem-calc(22), rem-calc(20)); +} + @mixin icon-pin ($circle: #ccc, $outline: #000) { @include icon-basic; width: rem-calc(29); height: rem-calc(38); @@ -200,6 +215,11 @@ $icon-search-width: rem-calc(21); @include icon-image('pin-outline.svg', rem-calc(14), rem-calc(20)); } +@mixin icon-pin-map { + @include icon-basic; + @include icon-image('pin.svg', rem-calc(13), rem-calc(18)); +} + @mixin icon-placeholder-image() { @include icon-basic; @include icon-image('image.svg', rem-calc(114), rem-calc(91)); diff --git a/app/assets/stylesheets/components/_autocomplete.scss b/app/assets/stylesheets/components/_autocomplete.scss new file mode 100644 index 000000000..bf84a3725 --- /dev/null +++ b/app/assets/stylesheets/components/_autocomplete.scss @@ -0,0 +1,105 @@ +.autocomplete { + background-color: $grey-dark; + border-radius: rem-calc(32); + border-radius: rem-calc(32); + border: rem-calc(1) solid $white; + margin-bottom: rem-calc(12); + + display: flex; + + position: relative; + z-index: 2; + + ::placeholder { + color: $grey-light; + } + + &--error { + background-color: $red; + color: $white; + + + ::placeholder { + color: $white; + } + + } + + &__container { + position: relative; + } + + &__input { + background-color: rgba($black, 0); + border: none; + color: $white; + font-size: rem-calc(18); + padding-left: rem-calc(50); + + display: flex; + flex: 1; + } + + &__magnifying-glass { + @include icon-search-white; + + height: 100%; + + position: absolute; + left: rem-calc(16); + + align-self: center; + } + + &__delete { + @include button-basic; + @include icon-cross-white; + + background-color: inherit; + border-radius: rem-calc(14); + + height: rem-calc(14); + width: rem-calc(14); + + position: absolute; + right: rem-calc(16); + + align-self: center; + } + + &__results-container { + background-color: $grey-dark; + border: rem-calc(1) solid $white; + border-bottom-left-radius: rem-calc(24); + border-bottom-right-radius: rem-calc(24); + padding-top: rem-calc(25); + padding-bottom: rem-calc(25); + + width: 100%; + + position: absolute; + top: 50%; + left: 0; + } + + &__results { + @include beautify-scrollbar(4); + + margin: 0 rem-calc(25); + + overflow-y: scroll; + + height: rem-calc(150); + } + + &__result { + padding: rem-calc(12 16); + + &:focus, + &:hover { + background-color: $grey-xlight; + color: $grey-black; + } + } + +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/_cards.scss b/app/assets/stylesheets/components/_cards.scss index ae5186669..649339b72 100644 --- a/app/assets/stylesheets/components/_cards.scss +++ b/app/assets/stylesheets/components/_cards.scss @@ -4,16 +4,6 @@ //-------------------------------------------------- // mixins //-------------------------------------------------- -@mixin card-stats-padding { - padding: rem-calc(28 30); -} - -@mixin card-stats-number { - color: $primary; - font-weight: $bold; - line-height: 1; -} - //-------------------------------------------------- // classes //-------------------------------------------------- diff --git a/app/assets/stylesheets/components/_filters.scss b/app/assets/stylesheets/components/_filters.scss index 5f97ae93d..b4342ba70 100644 --- a/app/assets/stylesheets/components/_filters.scss +++ b/app/assets/stylesheets/components/_filters.scss @@ -1,83 +1,11 @@ //-------------------------------------------------- // variables //-------------------------------------------------- -$filters-button-close: 70; -$filters-title-height: 54; //-------------------------------------------------- // classes //-------------------------------------------------- -.filter { - &__pane { - @include responsive(background-color, $grey-xlight, transparent, transparent); - @include responsive(top, 0, unset, unset); - @include responsive(left, 0, unset, unset); - width: 100%; height: 100vh; - - position: fixed; - top: 0; - bottom: 0; - z-index: $z-100; - - @include breakpoint($small) { - border-right: solid 1px $grey; - margin-right: rem-calc(24); - height: 100%; - - position: initial; - top: unset; - bottom: unset; - } - } - - &__pane-view { - @include flex; - @include flex-center; - background-color: $grey-xdark; - color: $white; - font-size: rem-calc(20); - font-weight: $bold; - width: 100%; height: rem-calc(63); - - position: absolute; - bottom: 0; - - @include breakpoint($small) { display: none; } - } - - &__pane-topbar { - @include flex; - @include flex-v-center; - border-bottom: solid 1px $grey; - padding-right: rem-calc(24); - padding-left: rem-calc(24); - width: 100%; height: calc(#{$filters-title-height}px); - - @include breakpoint($small) { display: none; } - } - - &__pane-title { font-size: rem-calc(18); } - - &__filter-groups { - @include responsive(overflow, scroll, initial, initial); - @include responsive(padding, rem-calc(24 22), rem-calc(24 22 24 0), rem-calc(24 22 24 0)); - @include responsive(height, calc(100vh - #{$filters-title-height}px - #{$filters-button-close}px), 100%, 100%); - width: 100%; - } - - &__group { - margin-bottom: rem-calc(24); - } - - &__options { - @include text-filter; - overflow-y: scroll; - max-height: rem-calc(250); - } - - &__trigger { - @include button-filter-trigger; - - &.disabled { @include button-disabled; } - } -} \ No newline at end of file +.filters { + @import './filters/filters-pame'; + @import './filters/filters-sidebar'; +} diff --git a/app/assets/stylesheets/components/_lists.scss b/app/assets/stylesheets/components/_lists.scss index 165b7d2ca..2f062cfee 100644 --- a/app/assets/stylesheets/components/_lists.scss +++ b/app/assets/stylesheets/components/_lists.scss @@ -6,6 +6,29 @@ // classes //-------------------------------------------------- .list { +//-------------------------------------------------- +// links +//-------------------------------------------------- + &--links { + @include ul-unstyled; + + .list { + &__li { + @include flex; + @include flex-h-between; + @include flex-v-center; + background-color: $grey-xlight; + margin-top: rem-calc(10); + padding: rem-calc(14 20); + max-width: rem-calc(700); + } + + &__a { margin-left: rem-calc(14); } + } + } +//-------------------------------------------------- +// stripes +//-------------------------------------------------- &--stripes { @include ul-unstyled; @@ -26,7 +49,9 @@ } } } - +//-------------------------------------------------- +// underline +//-------------------------------------------------- &--underline { @include ul-unstyled; max-height: rem-calc(280); @@ -62,17 +87,7 @@ justify-content: flex-end; } - &__a { - @include button-with-icon; - @include flex-no-shrink; - font-size: rem-calc(16); - - &::after { - @include icon-circle-chevron-black; - height: rem-calc(21); - width: rem-calc(21); - } - } + &__a { @include button-all($small: true); } } } } diff --git a/app/assets/stylesheets/components/_map.scss b/app/assets/stylesheets/components/_map.scss index 2130af6a2..7d7062f7f 100644 --- a/app/assets/stylesheets/components/_map.scss +++ b/app/assets/stylesheets/components/_map.scss @@ -1,16 +1,67 @@ +@import 'maps/v-map-baselayer-controls'; +@import "maps/v-map-disclaimer"; +@import 'maps/v-map-filter'; +@import 'maps/v-map-filters'; +@import 'maps/v-map-header'; +@import 'maps/v-map-pa-search'; +@import 'maps/v-map-popup'; +@import 'maps/v-map-toggler'; + +.map-section { + @include gutters; +} + .map { &--main { - @include gutters; - - .map { + @include breakpoint($small) { + position: relative; + } + + /** + * The following is part of a quick solution to the problem of having + * the header component within the filters component when it must + * appear above the map and filters below on mobile devices. + */ + @include breakpoint($small) { + & > .v-map-header { + display: none; + } + } + + .map__mapbox { + height: rem-calc(360); + + position: relative; - &__mapbox { + @include breakpoint($small) { height: rem-calc(700); } } } - &__trigger { + &--header { + width: 100%; + + .map__mapbox { + height: rem-calc(400); + + position: relative; + + @include breakpoint($small) { + height: rem-calc(425); + } + } + } + + &--search { + .map__map { + height: rem-calc(460); + } + } +} + +.map { + &__trigger { @include button-map-trigger; margin-right: rem-calc(16); @@ -18,12 +69,61 @@ &.disabled { @include button-disabled; } } +} - &--search { +.mapboxgl-ctrl-top-right .mapboxgl-ctrl.mapboxgl-ctrl-group { + margin-top: rem-calc(13); + margin-right: rem-calc(10); + + @include breakpoint($small) { + margin-top: rem-calc(17); + } + + @include breakpoint($medium) { + margin-top: rem-calc(32); + margin-right: rem-calc(16); + } +} + +.mapboxgl-ctrl-bottom-left { + display: flex; + flex-direction: column-reverse; +} + +.mapboxgl-ctrl.mapboxgl-ctrl-group:not(:empty) { + box-shadow: none; +} + +.mapboxgl-ctrl-group { + .mapboxgl-ctrl-icon.mapboxgl-ctrl-zoom-in, + .mapboxgl-ctrl-icon.mapboxgl-ctrl-zoom-out { + border: solid rem-calc(1) $grey-dark; + background-color: $white; + background-position: center; + background-repeat: no-repeat; + background-size: rem-calc(12 12); + width: 30px; height: 33px; + @include breakpoint($medium) { + background-size: rem-calc(20 20); + width: 42px; height: 45px; + } + + &:focus:first-child, + &:focus:last-child { + border-radius: 0; + } - .map { - &__map {height: rem-calc(460);} + &:hover { + background-color: $grey-xlight; } } -} \ No newline at end of file + + .mapboxgl-ctrl-icon.mapboxgl-ctrl-zoom-in { + background-image: image-url('icons/plus.svg'); + } + + .mapboxgl-ctrl-icon.mapboxgl-ctrl-zoom-out { + background-image: image-url('icons/minus.svg'); + } +} diff --git a/app/assets/stylesheets/components/_select.scss b/app/assets/stylesheets/components/_select.scss index 5d61265fa..19c426fa8 100644 --- a/app/assets/stylesheets/components/_select.scss +++ b/app/assets/stylesheets/components/_select.scss @@ -20,6 +20,7 @@ $select-selected-color: $grey-light; //-------------------------------------------------- @mixin vselect-icon-basic() { @include button-plain; + background-color: transparent; margin-right: $search-icon-padding-right; margin-left: $search-icon-padding-left; padding: 0; @@ -36,3 +37,6 @@ $select-selected-color: $grey-light; @import './select/select-equity'; @import './select/select-searchable'; } + + +@import './select/selector'; \ No newline at end of file diff --git a/app/assets/stylesheets/components/_table.scss b/app/assets/stylesheets/components/_table.scss index 2a5271b0a..8edd958c6 100644 --- a/app/assets/stylesheets/components/_table.scss +++ b/app/assets/stylesheets/components/_table.scss @@ -11,7 +11,6 @@ $table-horizontal-scroll-row-width-mobile: $table-horizontal-scroll-cell-width-m $table-horizontal-scroll-row-width-tablet: $table-horizontal-scroll-cell-width-tablet * 4; $table-horizontal-scroll-row-width-desktop: 80vw; - //-------------------------------------------------- // mixins //-------------------------------------------------- @@ -25,131 +24,39 @@ $table-horizontal-scroll-row-width-desktop: 80vw; @include responsive(margin-bottom, rem-calc(30), rem-calc(32), rem-calc(32), rem-calc(16)); } +// fix the columns widths so that they don't change +// size when you use the pagination +@mixin table-pame-column-widths() { + &:first-child { width: 142px; } + &:nth-child(2) { width: 138px; } + &:nth-child(3) { width: 127px; } + &:nth-child(4) { width: 128px; } + &:nth-child(5) { width: 100px; } + &:nth-child(6) { width: 138px; } + &:nth-child(7) { width: 128px; } + &:nth-child(8) { width: 128px; } + &:nth-child(9) { width: 110px; } +} + +//-------------------------------------------------- +// classes +//-------------------------------------------------- +.filtered-table { + min-height: $filters-menu-max-height + rem-calc(80); +} + //-------------------------------------------------- // table head //-------------------------------------------------- .table-head { - &--horizontal-scroll { - @include responsive(display, none, none, none, flex); - @include flex-nowrap; - @include box-shadow-grey; - background-color: $table-head-bg-color; - color: $white; - margin-top: rem-calc(26); - } - - .table-head { - &__cell { - @include table-cell-basic; - align-items: center; //temp-refresh - justify-content: space-between; //temp-refresh - - display: flex; //temp-refresh - } - - &__title-wrapper { - align-items: center; //temp-refresh - flex-wrap: nowrap; //temp-refresh - - display: flex; //temp-refresh - } - - &__title { - margin-right: rem-calc(6); - } - - &__sort { - @include button-plain; - @include icon-sort; - padding: rem-calc(3 6); - } - } - .tooltip__target { - color: $grey-xdark; - } + @import './table/table-head-horizontal-scroll'; + @import './table/table-head-pame'; } -.table { //-------------------------------------------------- -// tables +// table body //-------------------------------------------------- - &--horizontal-scroll { - - .table { - &__row { - @include table-row-spacing; - border: none; //temp-refresh needed to override conflicting old styles - - @include breakpoint($large) { - border: solid 1px $grey; - } - } - - &__row-title { - @include responsive(margin-left, $gutter-small, $gutter-medium, $gutter-medium, 0); - @include responsive(margin-right, $gutter-small, $gutter-medium, $gutter-medium, 0); - @include responsive(display, block, block, block, none); - font-size: rem-calc(18); - font-weight: 700; - margin-bottom: rem-calc(10); - } - - &__scroll-wrapper { - overflow-x: scroll; - @include breakpoint($large) { overflow-x: inherit; } - } - - &__scroll { - @include flex-nowrap; - @include responsive(width, $table-horizontal-scroll-row-width-mobile, $table-horizontal-scroll-row-width-tablet, $table-horizontal-scroll-row-width-tablet, 100%); - display: flex; - } - - &__cell { - @include table-cell-basic; - @include box-shadow-grey-light; - border: solid 1px $grey-light; - - display: flex; //temp-refresh - flex-direction: column; //temp-refresh - - @include breakpoint($large) { - border: none; - box-shadow: none; - } - - &:first-child { - @include responsive(display, none, none, none, flex); - } - - &:nth-child(2) { - @include responsive(margin-left, $gutter-small, $gutter-medium, $gutter-medium, 0); - } - } - - &__cell-titles { - @include responsive(margin-left, $gutter-small, $gutter-medium, $gutter-medium, 0); - @include responsive(display, flex, flex, flex, none); - justify-content: space-between; //temp refresh - } - - &__cell-title { - font-size: rem-calc(17); - font-weight: 500; - } - - &__cell-link { - font-weight: 900; - } - - &__cell-chart:nth-child(2) { margin-bottom: rem-calc(12); } - - &__cell-index { - @include responsive(display, block, block, block, none); - font-size: rem-calc(12); - font-weight: 500; - margin-top: auto; - } - } - } +.table { + @import './table/table-horizontal-scroll'; + @import './table/table-pame'; } \ No newline at end of file diff --git a/app/assets/stylesheets/components/_tabs.scss b/app/assets/stylesheets/components/_tabs.scss index 01f6fe13b..6cc640dfb 100644 --- a/app/assets/stylesheets/components/_tabs.scss +++ b/app/assets/stylesheets/components/_tabs.scss @@ -58,9 +58,11 @@ //-------------------------------------------------- &--hero { .tabs__triggers { + @include container; + @include site-width; + @include tabs-horizontal-scroll; @include ul-unstyled; @include ul-inline; - @include tabs-horizontal-scroll; transform: translateY(-100%); } diff --git a/app/assets/stylesheets/components/cards/card/_card-stats.scss b/app/assets/stylesheets/components/cards/card/_card-stats.scss index 0fa020175..393b4cda3 100644 --- a/app/assets/stylesheets/components/cards/card/_card-stats.scss +++ b/app/assets/stylesheets/components/cards/card/_card-stats.scss @@ -1,6 +1,13 @@ //-------------------------------------------------- // mixins //-------------------------------------------------- +@mixin card-stat-content { + @include flex; + @include flex-column; + + @include breakpoint($small) { @include flex-row; } +} + @mixin card-stats-padding { padding: rem-calc(16 18); @@ -13,9 +20,27 @@ line-height: 1; } +@mixin card-stats { + @include card-stats-padding; + background-color: $white; + margin-top: rem-calc(30); +} + +@mixin card-stats-h2 { margin-top: 0; } + +@mixin card-stats-third { width: calc(33% - 16px); } + +@mixin card-button-external { @include button-all($small: true); } + //-------------------------------------------------- // classes //-------------------------------------------------- +&--stats-wrapper-chart { + @include container-medium; + + @include breakpoint-down($small) { padding: 0; } +} + &--stats-wrapper { display: flex; flex-wrap: wrap; @@ -26,103 +51,18 @@ } } -&--stats-wrapper-chart { - @include container-medium; - - @include breakpoint-down($small) { padding: 0; } -} - -&--stats { - @include card-stats-padding; - background-color: $white; - margin-top: rem-calc(30); - - &--half { - width: 100%; - - @include breakpoint($small) { width: calc(50% - 15px); } - } - - .card { - &__chart { - margin-bottom: rem-calc(14); - width: 50%; - - @include breakpoint($small) { - margin-right: rem-calc(20); - margin-bottom: 0; - width: 30%; - } - } - - &__content { - @include flex; - @include flex-column; - - @include breakpoint($small) { @include flex-row; } - } - - &__logos { - @include flex; - @include ul-unstyled; - } - - &__logo { - margin-left: rem-calc(24); +&--stats-half { + width: 100%; - &:first-child { margin-left: 0; } - } - - &__logo-image { - width: rem-calc(100); height: auto; - - display: block; - } - - &__number { - font-size: rem-calc(16); - font-weight: $bold; - line-height: .9; - } - - &__number-large { - font-size: rem-calc(40); - font-weight: $bold; - line-height: .9; - } - - &__stat-box { - @include bg-grey-xdark; - @include flex; - @include flex-center; - font-size: rem-calc(20); - font-weight: $bold; - margin-right: rem-calc(20); - height: rem-calc(280); - } - - &__stat { - font-size: rem-calc(14); - line-height: 1.3; - margin: rem-calc(6 0 14 0); - } - - &__stat-large { margin-bottom: rem-calc(14); } - - &__subsection { border-top: solid 1px $grey-light; } - - &__subtitle { - font-size: rem-calc(20); - font-weight: $bold; - margin: rem-calc(12 0 0 0); - } - - &__title { - margin-top: 0; - } - - &__third { width: calc(33% - 16px); } - &__two-thirds { width: calc(100% - 33%); } - } + @include breakpoint($small) { width: calc(50% - 15px); } } +@import './stats/card-stats-affiliations'; +@import './stats/card-stats-attributes'; +@import './stats/card-stats-coverage'; +@import './stats/card-stats-designations'; +@import './stats/card-stats-governance'; +@import './stats/card-stats-iucn'; +@import './stats/card-stats-management'; +@import './stats/card-stats-overview'; +@import './stats/card-stats-sources'; \ No newline at end of file diff --git a/app/assets/stylesheets/components/cards/card/stats/_card-stats-affiliations.scss b/app/assets/stylesheets/components/cards/card/stats/_card-stats-affiliations.scss new file mode 100644 index 000000000..a70faf698 --- /dev/null +++ b/app/assets/stylesheets/components/cards/card/stats/_card-stats-affiliations.scss @@ -0,0 +1,42 @@ +//-------------------------------------------------- +// classes +//-------------------------------------------------- +&--stats-affliations { + @include card-stats; + + .card { + &__button { + @include card-button-external; + margin-top: auto; + padding-top: rem-calc(14); + + display: none; //to be added later + } + + &__h2 { @include card-stats-h2; } + + &__logos { + @include flex; + @include ul-unstyled; + } + + &__logo { + margin-left: rem-calc(30); + max-width: rem-calc(200); + + &:first-child { margin-left: 0; } + } + + &__logo-image { + width: rem-calc(100); height: auto; + + display: block; + } + + &__subtitle { + font-weight: $bold; + line-height: 1.2; + margin: rem-calc(14 0 0 0); + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/cards/card/stats/_card-stats-attributes.scss b/app/assets/stylesheets/components/cards/card/stats/_card-stats-attributes.scss new file mode 100644 index 000000000..f423ac5f6 --- /dev/null +++ b/app/assets/stylesheets/components/cards/card/stats/_card-stats-attributes.scss @@ -0,0 +1,10 @@ +//-------------------------------------------------- +// classes +//-------------------------------------------------- +&--stats-attributes { + @include card-stats; + + .card { + &__h2 { @include card-stats-h2; } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/cards/card/stats/_card-stats-coverage.scss b/app/assets/stylesheets/components/cards/card/stats/_card-stats-coverage.scss new file mode 100644 index 000000000..1ef6d507d --- /dev/null +++ b/app/assets/stylesheets/components/cards/card/stats/_card-stats-coverage.scss @@ -0,0 +1,51 @@ +//-------------------------------------------------- +// classes +//-------------------------------------------------- +&--stats-coverage { + @include card-stats; + + .card { + &__chart { + margin-bottom: rem-calc(14); + width: 50%; + + @include breakpoint($small) { + margin-right: rem-calc(20); + margin-bottom: 0; + width: 30%; + } + } + + &__content { @include card-stat-content; } + + &__h2 { @include card-stats-h2; } + + &__number { + font-size: rem-calc(16); + font-weight: $bold; + line-height: .9; + } + + &__number-large { + font-size: rem-calc(40); + font-weight: $bold; + line-height: .9; + } + + &__stat { + font-size: rem-calc(14); + line-height: 1.3; + margin: rem-calc(6 0 14 0); + } + + &__stat-large { margin-bottom: rem-calc(14); } + + &__subsection { border-top: solid 1px $grey-light; } + + &__subtitle { + font-size: rem-calc(20); + font-weight: $bold; + margin: rem-calc(12 0 0 0); + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/cards/card/stats/_card-stats-designations.scss b/app/assets/stylesheets/components/cards/card/stats/_card-stats-designations.scss new file mode 100644 index 000000000..46082ddad --- /dev/null +++ b/app/assets/stylesheets/components/cards/card/stats/_card-stats-designations.scss @@ -0,0 +1,12 @@ +//-------------------------------------------------- +// classes +//-------------------------------------------------- +&--stats-designations { + @include card-stats; + + .card { + &__h2 { @include card-stats-h2; } + + &__third { @include card-stats-third; } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/cards/card/stats/_card-stats-governance.scss b/app/assets/stylesheets/components/cards/card/stats/_card-stats-governance.scss new file mode 100644 index 000000000..2e57cd8fa --- /dev/null +++ b/app/assets/stylesheets/components/cards/card/stats/_card-stats-governance.scss @@ -0,0 +1,10 @@ +//-------------------------------------------------- +// classes +//-------------------------------------------------- +&--stats-governance { + @include card-stats; + + .card { + &__h2 { @include card-stats-h2; } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/cards/card/stats/_card-stats-iucn.scss b/app/assets/stylesheets/components/cards/card/stats/_card-stats-iucn.scss new file mode 100644 index 000000000..3fa68696e --- /dev/null +++ b/app/assets/stylesheets/components/cards/card/stats/_card-stats-iucn.scss @@ -0,0 +1,10 @@ +//-------------------------------------------------- +// classes +//-------------------------------------------------- +&--stats-iucn { + @include card-stats; + + .card { + &__h2 { @include card-stats-h2; } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/cards/card/stats/_card-stats-management.scss b/app/assets/stylesheets/components/cards/card/stats/_card-stats-management.scss new file mode 100644 index 000000000..5ef9f65d2 --- /dev/null +++ b/app/assets/stylesheets/components/cards/card/stats/_card-stats-management.scss @@ -0,0 +1,10 @@ +//-------------------------------------------------- +// classes +//-------------------------------------------------- +&--stats-management { + @include card-stats; + + .card { + &__h2 { @include card-stats-h2; } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/cards/card/_card-stats-overview.scss b/app/assets/stylesheets/components/cards/card/stats/_card-stats-overview.scss similarity index 100% rename from app/assets/stylesheets/components/cards/card/_card-stats-overview.scss rename to app/assets/stylesheets/components/cards/card/stats/_card-stats-overview.scss diff --git a/app/assets/stylesheets/components/cards/card/stats/_card-stats-sources.scss b/app/assets/stylesheets/components/cards/card/stats/_card-stats-sources.scss new file mode 100644 index 000000000..b6620baa8 --- /dev/null +++ b/app/assets/stylesheets/components/cards/card/stats/_card-stats-sources.scss @@ -0,0 +1,25 @@ +//-------------------------------------------------- +// classes +//-------------------------------------------------- +&--stats-sources { + @include card-stats; + + .card { + &__content { @include card-stat-content; } + + &__h2 { @include card-stats-h2; } + + &__stat-box { + @include bg-grey-black; + @include flex; + @include flex-center; + font-size: rem-calc(20); + font-weight: $bold; + margin-right: rem-calc(20); + height: rem-calc(280); + } + + &__third { @include card-stats-third; } + &__two-thirds { width: calc(100% - 33%); } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/cards/card/stats/_cards-stats-overlap.scss b/app/assets/stylesheets/components/cards/card/stats/_cards-stats-overlap.scss new file mode 100644 index 000000000..49c77fd75 --- /dev/null +++ b/app/assets/stylesheets/components/cards/card/stats/_cards-stats-overlap.scss @@ -0,0 +1,10 @@ +//-------------------------------------------------- +// classes +//-------------------------------------------------- +&--stats-overlap { + @include card-stats; + + .card { + &__h2 { @include card-stats-h2; } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/cards/cards/_cards-articles.scss b/app/assets/stylesheets/components/cards/cards/_cards-articles.scss index b8380c4a2..443e327d5 100644 --- a/app/assets/stylesheets/components/cards/cards/_cards-articles.scss +++ b/app/assets/stylesheets/components/cards/cards/_cards-articles.scss @@ -39,4 +39,4 @@ @include image-placeholder(); } } -} \ No newline at end of file +} diff --git a/app/assets/stylesheets/components/cards/cards/_cards-search-results-areas.scss b/app/assets/stylesheets/components/cards/cards/_cards-search-results-areas.scss index 621ab5cf8..7d938ef36 100644 --- a/app/assets/stylesheets/components/cards/cards/_cards-search-results-areas.scss +++ b/app/assets/stylesheets/components/cards/cards/_cards-search-results-areas.scss @@ -60,4 +60,4 @@ $card-search-results-areas-height: rem-calc(155); margin: rem-calc(12 0 2 0); } } -} \ No newline at end of file +} diff --git a/app/assets/stylesheets/components/charts/_chart-dial.scss b/app/assets/stylesheets/components/charts/_chart-dial.scss index 4104c33d1..1391203c1 100644 --- a/app/assets/stylesheets/components/charts/_chart-dial.scss +++ b/app/assets/stylesheets/components/charts/_chart-dial.scss @@ -19,9 +19,9 @@ $chart-dial-font-size-desktop: rem-calc(26); .chart { &__arc { &--default, &--background { fill: $chart-item-background; } - &--global { fill: $global; } - &--marine { fill: $marine; } - &--terrestrial { fill: $terrestrial; } + &--global { fill: $chart-purple; } + &--marine { fill: $chart-aqua; } + &--terrestrial { fill: $chart-green; } } &__arrow-circle { fill: $chart-dial-color; } diff --git a/app/assets/stylesheets/components/charts/_chart-row-target.scss b/app/assets/stylesheets/components/charts/_chart-row-target.scss index 8d7793b91..272a5ab78 100644 --- a/app/assets/stylesheets/components/charts/_chart-row-target.scss +++ b/app/assets/stylesheets/components/charts/_chart-row-target.scss @@ -29,9 +29,9 @@ $chart-row-target-font-size: rem-calc(18); &__stroke { &--default { stroke: $chart-item-background; } - &--marine { stroke: $marine; } - &--global { stroke: $global; } - &--terrestrial { stroke: $terrestrial; } + &--marine { stroke: $chart-aqua; } + &--global { stroke: $chart-purple; } + &--terrestrial { stroke: $chart-green; } } &__title { diff --git a/app/assets/stylesheets/components/filters/_filters-pame.scss b/app/assets/stylesheets/components/filters/_filters-pame.scss new file mode 100644 index 000000000..eecedfef1 --- /dev/null +++ b/app/assets/stylesheets/components/filters/_filters-pame.scss @@ -0,0 +1,234 @@ +//-------------------------------------------------- +// variables +//-------------------------------------------------- +$activeBackgroundColour: $primary; +$activeTextColour: white; +$checkbox-height: rem-calc(22); +$focus-outline-margin: rem-calc(4); + +//-------------------------------------------------- +// classes +//-------------------------------------------------- +&--pame { + @include breakpoint($small) { + @include flex; + @include flex-v-center; + } + + .filter { + margin-right: rem-calc(10); + margin-bottom: rem-calc(10); + + display: inline-block; + position: relative; + + @include breakpoint($medium) { + margin-right: rem-calc(20); + margin-bottom: 0; + } + + &__title { + font-size: rem-calc(20); + margin-right: rem-calc(20); + } + + //************************************************** + // button + //************************************************** + &__button { + @include button-dropdown-filter; + color: $black; + cursor: pointer; + margin: 0; + + position: relative; + + &:hover { + background-color: $activeBackgroundColour; + border-color: $activeBackgroundColour; + color: $activeTextColour; + + &:after { background-image: image-url('icons/chevron-white-down.svg'); } + } + + &:after { + @include flex-v-center; + content: ''; + width: rem-calc(8); height: rem-calc(6); + + right: rem-calc(24); + } + + &--active { + background-color: $activeBackgroundColour; + border-color: $activeBackgroundColour; + color: $activeTextColour; + + &:after, + &:hover:after { background-image: image-url('icons/chevron-white-up.svg'); } + } + + &--has-selected { + background-color: $activeBackgroundColour; + color: $activeTextColour; + + &:after, + &:hover:after { visibility: hidden; } + } + + &-total { + $total-width: rem-calc(24); + + @include flex-v-center; + background-color: $activeTextColour; + border-radius: 100%; + color: $activeBackgroundColour; + font-size: rem-calc(18); + line-height: $total-width; + text-align: center; + width: $total-width; height: $total-width; + + display: block; + + position: absolute; + right: rem-calc(16); + } + } + + //************************************************** + // options + //************************************************** + &__options { + background-color: white; + font-size: rem-calc(16); + padding: rem-calc(30 25); + width: 100%; height: 100vh; + + display: none; + position: fixed; + top: 0; + left: 0; + + z-index: 1; + + @include breakpoint($small) { + border: solid rem-calc(1) $black; + border-radius: $radius-global; + margin-top: rem-calc(18); + min-width: rem-calc(460); + width: auto; height: auto; + + position: absolute; + top: unset; + right: unset; + bottom: unset; + left: unset; + } + + &--active { + display: block; + } + + &-list { + padding: $focus-outline-margin; + margin-bottom: rem-calc(20); + max-height: 85vh; + overflow-y: scroll; + overflow-x: hidden; //for IE11 + white-space: nowrap; + + @include breakpoint($small) { + max-height: $filters-menu-max-height; + } + } + + &--donors { + column-count: 2; + flex-wrap: wrap; + width: 928px; + white-space: normal; + + display: flex; + + li { + flex: 1 0 50%; + + float: left; + } + } + + &--category { max-width: rem-calc(803); } + &--country { max-width: rem-calc(480); } + &--ocean-region { max-width: rem-calc(300); } + } + + &__option { + font-size: rem-calc(16); + margin-bottom: rem-calc(16); + width: 100%; + + display: block; + position: relative; + + label { cursor: pointer; } + } + +//************************************************** +// checkbox +//************************************************** + &__checkbox { + border: solid rem-calc(1) $grey; + cursor: pointer; + margin: 0; + padding: 0; + width: $checkbox-height; height: $checkbox-height; + + -webkit-appearance: none; + -moz-appearance: none; + -ms-appearance: none; + + display: block; + position: absolute; + left: 0; + + &--active:after { + @include icon-tick; + content: ''; + width: rem-calc(20); height: rem-calc(20); + + display: block; + top: 0; + left: 0; + } + } + + &__checkbox-label { + line-height: $checkbox-height; + padding: rem-calc(0 20 0 38); + + display: inline-block; + } +//************************************************** +// button +//************************************************** + &__buttons { + @include flex; + @include flex-h-between; + margin-top: rem-calc(18); + text-align: right; + } + + &__button-apply { @include button-primary; } + + &__button-download { + @include breakpoint($small) { margin-left: auto; } + } + + &__button-cancel { + @include button-outline; + margin: rem-calc(0 20 0) auto; + } + + &__button-clear { @include button-outline; } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/filters/_filters-sidebar.scss b/app/assets/stylesheets/components/filters/_filters-sidebar.scss new file mode 100644 index 000000000..09a98df7a --- /dev/null +++ b/app/assets/stylesheets/components/filters/_filters-sidebar.scss @@ -0,0 +1,88 @@ +//-------------------------------------------------- +// variables +//-------------------------------------------------- +$filters-button-close: 70; +$filters-title-height: 54; + +//-------------------------------------------------- +// classes +//-------------------------------------------------- +&--sidebar { + .filter { + &__pane { + background-color: $grey-xlight; + width: 100%; height: 100vh; + + position: fixed; + top: 0; + left: 0; + bottom: 0; + z-index: $z-100; + + @include breakpoint($small) { + background-color: transparent; + border-right: solid 1px $grey; + margin-right: rem-calc(24); + height: 100%; + + position: initial; + top: unset; + left: unset; + bottom: unset; + } + } + + &__pane-view { + @include flex; + @include flex-center; + background-color: $grey-xdark; + color: $white; + font-size: rem-calc(20); + font-weight: $bold; + width: 100%; height: rem-calc(63); + + position: absolute; + bottom: 0; + + @include breakpoint($small) { display: none; } + } + + &__pane-topbar { + @include flex; + @include flex-v-center; + border-bottom: solid 1px $grey; + padding-right: rem-calc(24); + padding-left: rem-calc(24); + width: 100%; height: calc(#{$filters-title-height}px); + + @include breakpoint($small) { display: none; } + } + + &__pane-title { + font-size: rem-calc(18); + + display: inline; + + @include breakpoint($small) { + display: none; + } + } + + &__filter-groups { + @include responsive(overflow, scroll, initial, initial); + @include responsive(padding, rem-calc(24 22), rem-calc(24 22 24 0), rem-calc(24 22 24 0)); + @include responsive(height, calc(100vh - #{$filters-title-height}px - #{$filters-button-close}px), 100%, 100%); + width: 100%; + } + + &__group { + margin-bottom: rem-calc(24); + } + + &__options { + @include text-filter; + overflow-y: scroll; + max-height: rem-calc(250); + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/flickity/_flickity-basic.scss b/app/assets/stylesheets/components/flickity/_flickity-basic.scss index 8141256c4..517c98174 100644 --- a/app/assets/stylesheets/components/flickity/_flickity-basic.scss +++ b/app/assets/stylesheets/components/flickity/_flickity-basic.scss @@ -60,10 +60,19 @@ $flickity-basic-slide-width-desktop: 20%; width: $flickity-basic-slide-width-desktop; height: rem-calc(312); } - &:last-child { - @include breakpoint($large) { - // @include tooltip-last-slide; + @include breakpoint($medium) { + .tooltip__target { + left: inherit; + right: rem-calc(-10); + transform: none; + + &::after { + left: inherit; + right: rem-calc(10); + transform: none; + } + } } } } @@ -76,10 +85,16 @@ $flickity-basic-slide-width-desktop: 20%; width: 100%; height: 100%; position: relative; + + @include breakpoint($medium) { padding: rem-calc(16 12); } } .carousel-cell__title { @include breakpoint($small) { margin: rem-calc(0 5); } + @include breakpoint($small) { + font-size: rem-calc(16); + margin: rem-calc(0 20 0 10); + } @include breakpoint($large) { margin: rem-calc(0 10); } } diff --git a/app/assets/stylesheets/components/maps/_v-map-baselayer-controls.scss b/app/assets/stylesheets/components/maps/_v-map-baselayer-controls.scss new file mode 100644 index 000000000..b26744348 --- /dev/null +++ b/app/assets/stylesheets/components/maps/_v-map-baselayer-controls.scss @@ -0,0 +1,50 @@ +.v-map { + position: relative; +} +.v-map-baselayer-controls { + position: absolute; + bottom: rem-calc(10); + right: rem-calc(10); + + @include breakpoint($small) { + bottom: rem-calc(16); + } + + @include breakpoint($medium) { + right: rem-calc(16); + } + + &__control { + min-width: rem-calc(100); + + background-color: $white; + border: rem-calc(1) solid $grey-dark; + border-radius: 0; + color: $black; + cursor: pointer; + font-size: rem-calc(14); + width: rem-calc(82); height: rem-calc(35); + + @include breakpoint($small) { + font-size: rem-calc(16); + width: rem-calc(140); height: rem-calc(46); + } + + &:not(:last-child) { + border-right: none; + } + + &.selected { + background: $grey-dark; + color: $white; + } + + &:hover:not(.selected) { + background-color: $grey-xlight; + } + + &:active { + background-color: darken($grey-xlight, 10%); + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/maps/_v-map-disclaimer.scss b/app/assets/stylesheets/components/maps/_v-map-disclaimer.scss new file mode 100644 index 000000000..c4e3d2432 --- /dev/null +++ b/app/assets/stylesheets/components/maps/_v-map-disclaimer.scss @@ -0,0 +1,26 @@ +.v-map-disclaimer { + background: $white; + color: $grey-xdark; + font-size: rem-calc(14); + padding: rem-calc(17, 17); + + justify-self: flex-end; + + @include breakpoint($small) { + padding: rem-calc(12 22); + } + + &__heading { + font-weight: $semi-bold; + line-height: rem-calc(26); + } + + &--embedded { + &.v-map-disclaimer { + background: transparent; + color: $grey-xlight; + margin-top: rem-calc(24); + padding: 0; + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/maps/_v-map-filter.scss b/app/assets/stylesheets/components/maps/_v-map-filter.scss new file mode 100644 index 000000000..bacdbc94e --- /dev/null +++ b/app/assets/stylesheets/components/maps/_v-map-filter.scss @@ -0,0 +1,30 @@ +.v-map-filter { + color: $white; + margin-bottom: rem-calc(15); + user-select: none; + + display: flex; + align-items: center; + flex-direction: row; + justify-content: flex-start; + + &--toggleable { + cursor: pointer; + } + + &__color { + background-color: $grey; + border-radius: rem-calc(19); + border: 1px solid $white; + + height: rem-calc(19); + width: rem-calc(19); + + flex-shrink: 0; + } + + &__color, + &__description { + margin-right: rem-calc(12); + } +} diff --git a/app/assets/stylesheets/components/maps/_v-map-filters.scss b/app/assets/stylesheets/components/maps/_v-map-filters.scss new file mode 100644 index 000000000..39c0b1ce3 --- /dev/null +++ b/app/assets/stylesheets/components/maps/_v-map-filters.scss @@ -0,0 +1,68 @@ +.v-map-filters { + background-color: $grey-xdark; + color: $grey-light; + + @include breakpoint($small) { + width: rem-calc(340); + + position: absolute; + top: rem-calc(17); + left: rem-calc(18); + z-index: 1; + } + + @include breakpoint($medium) { + width: rem-calc(496); + + top: rem-calc(32); + left: rem-calc(43); + } + + .v-map-header { + @include breakpoint-down($small) { + display: none; + } + } + + &__body { + @include responsive( + padding, + rem-calc(30 18), + rem-calc(18 18 40), + rem-calc(18 18 40), + rem-calc(25 25 60), + ); + + display: flex; + flex-direction: column; + justify-content: space-between; + } + + &--hidden { + @include screen-reader; + } +} + +.v-map-pa-search { + &__dropdown { + margin-bottom: rem-calc(16); + + display: flex; + align-items: center; + justify-content: flex-start; + + .selector { + margin-left: rem-calc(16); + + max-width: rem-calc(162); + + flex: 1; + + &__options { + overflow-y: auto; + + height: auto; + } + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/maps/_v-map-header.scss b/app/assets/stylesheets/components/maps/_v-map-header.scss new file mode 100644 index 000000000..89cecb4dd --- /dev/null +++ b/app/assets/stylesheets/components/maps/_v-map-header.scss @@ -0,0 +1,26 @@ +.v-map-header { + background-color: $grey-black; + padding: rem-calc(14 24); + + display: flex; + justify-content: space-between; + align-items: center; + + &__title { + color: $grey-xlight; + font-family: $header-font; + font-size: rem-calc(25); + font-weight: $bold; + line-height: $header-font-line-height; + } + + &__close { + margin-left: rem-calc(12); + @include icon-cross-white; + cursor: pointer; + + &.closed { + @include icon-minus-white; + } + } +} diff --git a/app/assets/stylesheets/components/maps/_v-map-pa-search.scss b/app/assets/stylesheets/components/maps/_v-map-pa-search.scss new file mode 100644 index 000000000..747d22ee7 --- /dev/null +++ b/app/assets/stylesheets/components/maps/_v-map-pa-search.scss @@ -0,0 +1,7 @@ +.v-map-pa-search { + margin-bottom: rem-calc(32); + + &__dropdown { + color: $white; + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/maps/_v-map-popup.scss b/app/assets/stylesheets/components/maps/_v-map-popup.scss new file mode 100644 index 000000000..6a9912aa6 --- /dev/null +++ b/app/assets/stylesheets/components/maps/_v-map-popup.scss @@ -0,0 +1,42 @@ +.v-map-pin { + @include icon-pin-map; +} + +.mapboxgl-popup-anchor-top, +.mapboxgl-popup-anchor-top-right, +.mapboxgl-popup-anchor-top-left { + .mapboxgl-popup-tip { + border-bottom-color: $grey-xdark !important; + border-bottom-width: 6px; + } +} + +.mapboxgl-popup-anchor-right { + .mapboxgl-popup-tip { + border-left-color: $grey-xdark !important; + border-left-width: 6px; + } +} +.mapboxgl-popup-anchor-left { + .mapboxgl-popup-tip { + border-right-color: $grey-xdark !important; + border-right-width: 6px; + } +} +.mapboxgl-popup-anchor-bottom, +.mapboxgl-popup-anchor-bottom-right, +.mapboxgl-popup-anchor-bottom-left { + .mapboxgl-popup-tip { + border-top-color: $grey-xdark !important; + border-top-width: 6px; + } +} + +.mapboxgl-popup-content { + background-color: $grey-xdark !important; + color: $white !important; +} + +.mapboxgl-popup-close-button { + display: none !important; +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/maps/_v-map-toggler.scss b/app/assets/stylesheets/components/maps/_v-map-toggler.scss new file mode 100644 index 000000000..6f0570054 --- /dev/null +++ b/app/assets/stylesheets/components/maps/_v-map-toggler.scss @@ -0,0 +1,59 @@ +.v-map-toggler { + border: 1px solid $grey-xlight; + cursor: pointer; + padding: rem-calc(2); + user-select: none; + + display: inline-flex; + align-items: center; + justify-content: flex-start; + + min-width: rem-calc(53); + height: rem-calc(27); + + &, + &__switch { + border-radius: rem-calc(14); + } + + &__switch { + background-color: $grey; + color: $grey-xdark; + font-size: rem-calc(14); + font-weight: 600; + margin-left: auto; + margin-right: 0; + padding: rem-calc(2 4 0); + + height: 100%; + + display: inline-flex; + align-items: center; + } + + &--active { + justify-content: flex-end; + + .v-map-toggler__switch { + background-color: $grey-xlight; + margin-left: 0; + margin-right: auto; + + transition: all 100ms ease-in-out; + } + } + + &:hover { + &__switch { + background-color: lighten($grey, 10%); + color: lighten($grey-xdark, 10%); + } + + &__active { + .v-map-toggler__switch { + color: lighten($grey-dark, 10%); + background-color: lighten($grey-xlight, 10%); + } + } + } +} diff --git a/app/assets/stylesheets/components/search/_search-autocomplete.scss b/app/assets/stylesheets/components/search/_search-autocomplete.scss index 3d2259033..8e141adf9 100644 --- a/app/assets/stylesheets/components/search/_search-autocomplete.scss +++ b/app/assets/stylesheets/components/search/_search-autocomplete.scss @@ -51,6 +51,11 @@ $search-autocomplete-select-width: rem-calc(222); @include breakpoint($small) { height: $search-autocomplete-height-tablet; } + &:focus { + outline: none; + border-color: $black; + } + .search__bar & { height: rem-calc(49); } diff --git a/app/assets/stylesheets/components/search/_search-results-areas.scss b/app/assets/stylesheets/components/search/_search-results-areas.scss index 0ed4282c5..eb95f5d30 100644 --- a/app/assets/stylesheets/components/search/_search-results-areas.scss +++ b/app/assets/stylesheets/components/search/_search-results-areas.scss @@ -27,6 +27,14 @@ @include flex-v-center; } + &__map-container { + @include container; + @include site-width; + + padding-top: rem-calc(28); + padding-bottom: rem-calc(28); + } + &__main { @include container; @include site-width; @@ -34,6 +42,12 @@ @include breakpoint($small) { display: flex; } } + &__filter-trigger { + @include button-filter-trigger; + + &.disabled { @include button-disabled; } + } + &__filters { @include flex-no-shrink; @include responsive(padding-right, 0, rem-calc(24), rem-calc(24)); diff --git a/app/assets/stylesheets/components/select/_select-searchable.scss b/app/assets/stylesheets/components/select/_select-searchable.scss index 875eef2a8..7d148a213 100644 --- a/app/assets/stylesheets/components/select/_select-searchable.scss +++ b/app/assets/stylesheets/components/select/_select-searchable.scss @@ -1,8 +1,10 @@ &--searchable { - @include responsive(width, 100%, $select-width-desktop, $select-width-desktop); - + width: 100%; + position: relative; + @include breakpoint($small) { width: $select-width-desktop; } + .select { &__label { } diff --git a/app/assets/stylesheets/components/select/_selector.scss b/app/assets/stylesheets/components/select/_selector.scss new file mode 100644 index 000000000..87ce5167d --- /dev/null +++ b/app/assets/stylesheets/components/select/_selector.scss @@ -0,0 +1,89 @@ +.selector { + border: rem-calc(1) solid $grey-xlight; + border-radius: 0; + + display: block; + + position: relative; + z-index: 100; + + &__selected { + cursor: default; + user-select: none; + + display: flex; + align-items: center; + justify-content: space-between; + + &:focus { + box-shadow: 0 0 0 rem-calc(2) rgba($grey-light, 0.25); + } + } + + &__label { + color: $white; + + flex: 1; + } + + &__caret { + @include icon-chevron-white-down; + &--active { + @include icon-chevron-white-up; + } + } + + &__selected, + &__option { + padding: rem-calc(10 16); + } + + &__options { + @include beautify-scrollbar; + + background-color: $grey; + border: rem-calc(1) solid $grey-xlight; + color: $white; + overflow-y: scroll; + + height: rem-calc(100); + + position: absolute; + top: rem-calc(40); + right: - rem-calc(1); + left: - rem-calc(1); + } + + &__option { + user-select: none; + cursor: pointer; + + &:focus { + outline: none; + } + + &:focus, + &:hover { + background-color: $grey-xlight; + color: $grey-black; + } + + &--active { + background-color: $grey-light; + color: $grey-dark; + + &:focus, + &:hover { + background-color: lighten($grey-light, 5%); + } + } + + &:first-child { + border-top: rem-calc(1) solid rgba($black, 0); + } + + &:last-child { + border-bottom: rem-calc(1) solid rgba($black, 0); + } + } +} diff --git a/app/assets/stylesheets/components/table/_table-head-horizontal-scroll.scss b/app/assets/stylesheets/components/table/_table-head-horizontal-scroll.scss new file mode 100644 index 000000000..d3ad7e0e4 --- /dev/null +++ b/app/assets/stylesheets/components/table/_table-head-horizontal-scroll.scss @@ -0,0 +1,35 @@ +&--horizontal-scroll { + display: none; + + @include breakpoint($large) { + @include flex; + @include flex-nowrap; + @include box-shadow-grey; + background-color: $table-head-bg-color; + color: $white; + margin-top: rem-calc(26); + } + + .table-head { + &__cell { + @include flex; + @include flex-h-start; + @include flex-v-start; + @include table-cell-basic; + } + + &__title { margin-right: rem-calc(6); } + + &__tooltip { margin-left: auto; } + + &__sort { + @include button-plain; + @include icon-sort; + padding: rem-calc(3 6); + } + } + + .tooltip__target { + color: $grey-xdark; + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/table/_table-head-pame.scss b/app/assets/stylesheets/components/table/_table-head-pame.scss new file mode 100644 index 000000000..397a03342 --- /dev/null +++ b/app/assets/stylesheets/components/table/_table-head-pame.scss @@ -0,0 +1,59 @@ +//---------------------------------------- +// animations +//---------------------------------------- +@keyframes showHeader { + from { + transform: translateY(-100%); + } + to { + transform: translateY(0); + } +} + +//---------------------------------------- +// classes +//---------------------------------------- +&--pame { + margin-top: rem-calc(30); + height: rem-calc(56); + + display: none; + + @include breakpoint($medium) { display: block; } + + .table-head { + &--stuck { + position: fixed; + top: 0; + + width: rem-calc(1160); + + animation: showHeader .25s forwards linear; + } + + &__row { + display: flex; + } + + &__cell { + @include table-pame-column-widths; + background-color: black; + border-left: dotted white 2px; + color: $white; + font-size: rem-calc(18); + height: rem-calc(57); + padding: rem-calc(8 14 0 14); + + display: inline-block; + + &:first-child { border-left: none; } + } + + &__title { + margin-right: rem-calc(2); + vertical-align: middle; + + display: inline-block; + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/table/_table-horizontal-scroll.scss b/app/assets/stylesheets/components/table/_table-horizontal-scroll.scss new file mode 100644 index 000000000..e0b7bf6fe --- /dev/null +++ b/app/assets/stylesheets/components/table/_table-horizontal-scroll.scss @@ -0,0 +1,78 @@ +&--horizontal-scroll { + .table { + &__row { + @include table-row-spacing; + border: none; //temp-refresh needed to override conflicting old styles + + @include breakpoint($large) { + border: solid 1px $grey; + } + } + + &__row-title { + @include responsive(margin-left, $gutter-small, $gutter-medium, $gutter-medium, 0); + @include responsive(margin-right, $gutter-small, $gutter-medium, $gutter-medium, 0); + @include responsive(display, block, block, block, none); + font-size: rem-calc(18); + font-weight: 700; + margin-bottom: rem-calc(10); + } + + &__scroll-wrapper { + overflow-x: scroll; + @include breakpoint($large) { overflow-x: inherit; } + } + + &__scroll { + @include flex-nowrap; + @include responsive(width, $table-horizontal-scroll-row-width-mobile, $table-horizontal-scroll-row-width-tablet, $table-horizontal-scroll-row-width-tablet, 100%); + display: flex; + } + + &__cell { + @include table-cell-basic; + @include box-shadow-grey-light; + border: solid 1px $grey-light; + + display: flex; //temp-refresh + flex-direction: column; //temp-refresh + + @include breakpoint($large) { + border: none; + box-shadow: none; + } + + &:first-child { + @include responsive(display, none, none, none, flex); + } + + &:nth-child(2) { + @include responsive(margin-left, $gutter-small, $gutter-medium, $gutter-medium, 0); + } + } + + &__cell-titles { + @include responsive(margin-left, $gutter-small, $gutter-medium, $gutter-medium, 0); + @include responsive(display, flex, flex, flex, none); + justify-content: space-between; //temp refresh + } + + &__cell-title { + font-size: rem-calc(17); + font-weight: 500; + } + + &__cell-link { + font-weight: 900; + } + + &__cell-chart:nth-child(2) { margin-bottom: rem-calc(12); } + + &__cell-index { + @include responsive(display, block, block, block, none); + font-size: rem-calc(12); + font-weight: 500; + margin-top: auto; + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/table/_table-pame.scss b/app/assets/stylesheets/components/table/_table-pame.scss new file mode 100644 index 000000000..439fb6092 --- /dev/null +++ b/app/assets/stylesheets/components/table/_table-pame.scss @@ -0,0 +1,91 @@ +//---------------------------------------- +// classes +//---------------------------------------- +&--pame { + margin-top: rem-calc(30); + margin-bottom: rem-calc(24); + + @include breakpoint($medium) { margin-top: 0; } + + .table { + &__row { + background-color: $grey-xlight; + margin-bottom: rem-calc(18); + padding: rem-calc(6 0); + + @include breakpoint($medium) { + margin: 0; + padding: 0; + + display: flex; + } + + &:nth-child(even) { + @include breakpoint($medium) { background-color: $white; } + } + } + + &__cell { + margin: 0; + padding: rem-calc(4 14); + width: 100%; + + @include breakpoint($medium) { + @include table-pame-column-widths; + border-left: dotted white 2px; + padding: rem-calc(16 14); + + display: inline-block; + } + + &:first-child { border-left: none; } + + &:before { + font-weight: bold; + margin-right: rem-calc(6); + + @include breakpoint($medium){ display: none; } + } + + // Add copy for the table header + // These are shown on mobile and tablet + // Check pame_evalutation.rb for the copy + &:nth-child(1) { &:before { content: 'Name:'; } } + &:nth-child(2) { &:before { content: 'Designation:'; } } + &:nth-child(3) { &:before { content: 'WDPA ID:'; } } + &:nth-child(4) { &:before { content: 'Assessment ID:'; } } + &:nth-child(5) { &:before { content: 'Country:'; } } + &:nth-child(6) { &:before { content: 'Methodology:'; } } + &:nth-child(7) { &:before { content: 'Year of assessment:'; } } + &:nth-child(8) { &:before { content: 'Link to assessment:'; } } + &:nth-child(9) { &:before { content: 'Metadata ID:'; } } + } + + &__sorting { + opacity: .7; + vertical-align: middle; + + display: inline-block; + + &:hover { opacity: .9; } + } + + &__sort { + cursor: pointer; + width: rem-calc(8); height: rem-calc(6); + + display: block; + + &--ascending { + background-image: image-url('icons/arrow-up-white.svg'); + margin-bottom: rem-calc(1); + } + + &--descending { + background-image: image-url('icons/arrow-down-white.svg'); + } + } + + &__note { font-size: rem-calc(14); } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/helpers/_accessibility.scss b/app/assets/stylesheets/helpers/_accessibility.scss index 3d658dffa..96977ef98 100644 --- a/app/assets/stylesheets/helpers/_accessibility.scss +++ b/app/assets/stylesheets/helpers/_accessibility.scss @@ -1,4 +1,4 @@ -.screen-reader { +@mixin screen-reader { border: 0; clip: rect(0 0 0 0); height: rem-calc(1); @@ -8,4 +8,8 @@ position: absolute; white-space: nowrap; width: rem-calc(1); +} + +.screen-reader { + @include screen-reader; } \ No newline at end of file diff --git a/app/assets/stylesheets/helpers/_beautify-scrollbar.scss b/app/assets/stylesheets/helpers/_beautify-scrollbar.scss index 7ce86bb25..f62ca55bf 100644 --- a/app/assets/stylesheets/helpers/_beautify-scrollbar.scss +++ b/app/assets/stylesheets/helpers/_beautify-scrollbar.scss @@ -1,13 +1,13 @@ -@mixin beautify-scrollbar { +@mixin beautify-scrollbar($width: 9) { &::-webkit-scrollbar, &::-webkit-scrollbar-button { - width: rem-calc(9); - height: rem-calc(9); + width: rem-calc($width); + height: rem-calc($width); } &::-webkit-scrollbar-thumb, &::-webkit-scrollbar-track { background-clip: padding-box; - border-radius: rem-calc(9); + border-radius: rem-calc($width); border: rem-calc(1) solid $grey-xdark; } &::-webkit-scrollbar-thumb { diff --git a/app/assets/stylesheets/helpers/_helpers.scss b/app/assets/stylesheets/helpers/_helpers.scss index 9e9bdf9cb..d960682af 100644 --- a/app/assets/stylesheets/helpers/_helpers.scss +++ b/app/assets/stylesheets/helpers/_helpers.scss @@ -28,6 +28,15 @@ height: 100vh; } +.flex-stack-mobile { + display: flex; + flex-wrap: wrap; + + @include breakpoint($small) { + flex-wrap: nowrap; + } +} + //-------------------------------------------------- // fouc (flash of unstyled content) //-------------------------------------------------- diff --git a/app/assets/stylesheets/pages/_site.scss b/app/assets/stylesheets/pages/_site.scss new file mode 100644 index 000000000..f87adbc67 --- /dev/null +++ b/app/assets/stylesheets/pages/_site.scss @@ -0,0 +1,40 @@ +//-------------------------------------------------- +// classes +//-------------------------------------------------- +.page--site { + + .page { + &__container { + @include breakpoint($small) { + @include flex; + @include flex-wrap; + + } + } + + &__col-1 { + width: 100%; + @include breakpoint($medium) { width: calc(50% - 15px); } + } + + &__col-2 { + width: 100%; + + @include breakpoint($small) { + @include flex; + @include flex-h-between; + } + + @include breakpoint($medium) { + margin-left: auto; + width: calc(50% - 15px); + + display: block; + } + } + + &__col-child { + @include breakpoint-between($small, $medium) { width: calc(50% - 15px); } + } + } +} \ No newline at end of file diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7eb99c2e2..592dba2f2 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -5,16 +5,40 @@ class PageNotFound < StandardError; end; protect_from_forgery with: :exception - before_action :set_cms_site - before_action :set_locale + helper_method :opengraph + + before_action :load_cms_site before_action :load_cms_content + + before_action :set_locale before_action :check_for_pdf #Temporary fix for development. To test if it is required on staging/production before_action :set_host_for_local_storage after_action :store_location - def set_cms_site - @cms_site ||= Comfy::Cms::Site.first + def admin_path? + request.original_fullpath =~ %r{/(?:#{I18n.locale}/)?admin/?} + end + + def opengraph + return if admin_path? + + @opengraph ||= OpengraphBuilder.new({ + 'og': { + 'site_name': t('meta.site.name'), + 'title': t('meta.site.title'), + 'description': t('meta.site.description'), + 'url': request.original_url, + 'type': 'website', + 'image': URI.join(root_url, helpers.image_path(t('meta.image'))), + 'image:alt': t('meta.image_alt'), + 'locale': I18n.locale + }, + 'twitter': { + 'site': t('meta.twitter.site'), + 'creator': t('meta.twitter.creator') + } + }) end def default_url_options @@ -47,6 +71,23 @@ def after_sign_in_path_for(resource) private + def load_cms_site + return if admin_path? + + @cms_site ||= Comfy::Cms::Site.first + end + + def load_cms_content + return if admin_path? + + @cms_page ||= Comfy::Cms::Page.find_by_full_path(request.original_fullpath.gsub(%r{\A/#{I18n.locale}/?}, '/')) + + return unless @cms_page + + ComfyOpengraph.new({ 'social-title': 'title', 'social-description': 'description', 'theme_image': 'image' }) + .parse(opengraph: opengraph, page: @cms_page) + end + def record_invalid_error message = "We're sorry, but something went wrong" @@ -84,29 +125,6 @@ def render_404 "/users/sign_out" ] - def load_cms_content - cms_path = request.original_fullpath - locale = I18n.locale.to_s - home_page = "/#{locale}" - - if cms_path == home_page - cms_path = '/' - else - cms_path = cms_path.gsub("/#{locale}", "") - end - - @cms_page = Comfy::Cms::Page.find_by_full_path(cms_path) - end - - # def load_cms_pages - # @updates_and_news = Comfy::Cms::Category.find_by_label("Updates & News") - # @connectivity_page = Comfy::Cms::Page.find_by_label("Connectivity Conservation") - # @pame_page = Comfy::Cms::Page.find_by_label("Protected Areas Management Effectiveness (PAME)") - # @wdpa_page = Comfy::Cms::Page.find_by_label("World Database on Protected Areas") - # @green_list_page = Comfy::Cms::Page.find_by_slug("green-list") - # @equity_page = Comfy::Cms::Page.find_by_slug("equity") - # end - def check_for_pdf @for_pdf = params[:for_pdf].present? end diff --git a/app/controllers/country_controller.rb b/app/controllers/country_controller.rb index 5b395e0d0..da8b4188f 100644 --- a/app/controllers/country_controller.rb +++ b/app/controllers/country_controller.rb @@ -3,6 +3,7 @@ class CountryController < ApplicationController after_action :enable_caching before_action :load_vars, except: %i[codes compare] + include MapHelper def show @country_presenter = CountryPresenter.new @country @@ -32,11 +33,21 @@ def show @total_oecm = 0 # #TODO @total_pame = @country.protected_areas.with_pame_evaluations.count @total_wdpa = @country.protected_areas.count + + @map = { + overlays: MapOverlaysSerializer.new(map_overlays, map_yml).serialize + } + + @map_options = { + map: { boundsUrl: @country.extent_url } + } ##TODO need adding # protected_national_report: statistic_presenter.percentage_nr_marine_cover, # national_report_version: statistic_presenter.nr_version, + helpers.opengraph_title_and_description_with_suffix(@country.name) + respond_to do |format| format.html format.pdf do @@ -75,6 +86,10 @@ def protected_areas private + def map_overlays + overlays(['oecm', 'marine_wdpa', 'terrestrial_wdpa']) + end + def load_vars @country = if params[:iso].size == 2 Country.where(iso: params[:iso].upcase).first diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index 347c98705..be8bc38c4 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -1,4 +1,6 @@ class HomeController < ApplicationController + include MapHelper + def index @pa_coverage_percentage = 9999 #TODO Total PA coverage in % @@ -73,6 +75,16 @@ def index @regions_page = Comfy::Cms::Page.find_by_slug("unep-regions") @carousel_slides = HomeCarouselSlide.all.select{|slide| slide.published } + + @main_map = { + overlays: MapOverlaysSerializer.new(home_overlays, map_yml).serialize + } + end + + private + + def home_overlays + overlays(['oecm', 'marine_wdpa', 'terrestrial_wdpa']) end private @@ -80,8 +92,7 @@ def index def levels _levels = home_yml[:pas][:levels] _levels.map do |level| - geo_type = level.delete(:geo_type) - level[:url] = search_areas_path({geo_type: geo_type, filters: {db_type: ['wdpa']}}) + level[:url] = search_areas_path(geo_type: level[:geo_type]) level end end diff --git a/app/controllers/marine_controller.rb b/app/controllers/marine_controller.rb index e019020f2..f55d702e7 100644 --- a/app/controllers/marine_controller.rb +++ b/app/controllers/marine_controller.rb @@ -1,5 +1,6 @@ class MarineController < ApplicationController include ActionView::Helpers::NumberHelper + include MapHelper #Static stats before_action :marine_statistics, only: [:index, :download_designations] @@ -13,7 +14,6 @@ class MarineController < ApplicationController before_action :national_statistics, only: [:index] before_action :designations, only: [:index, :download_designations] - before_action :load_cms_content def index @marineSites = ProtectedArea.marine_areas.limit(3) ## FERDI 3 marine PAs @@ -27,6 +27,9 @@ def index @pas_km = @marine_statistics['total_ocean_area_protected'] @pas_percent = @marine_statistics['total_ocean_pa_coverage_percentage'] @pas_total = @marine_statistics['total_marine_protected_areas'] + @map = { + overlays: MapOverlaysSerializer.new(marine_overlays, map_yml).serialize + } @filters = { db_type: ['wdpa'], is_marine: true } end @@ -40,6 +43,10 @@ def download_designations private + def marine_overlays + overlays(['oecm', 'marine_wdpa']) + end + def generate_designations_csv columns = ["PA name", "Country", "Size", "Date of designation"] CSV.generate(headers: true) do |csv| diff --git a/app/controllers/oecm_controller.rb b/app/controllers/oecm_controller.rb index 1afd7a616..c97c085a5 100644 --- a/app/controllers/oecm_controller.rb +++ b/app/controllers/oecm_controller.rb @@ -1,4 +1,6 @@ class OecmController < ApplicationController + include MapHelper + def index @oecm_coverage_percentage = 10 ##TODO FERDI - percentage of the world covered by OECMs @@ -8,11 +10,23 @@ def index }.to_json @tabs = get_tabs(3).to_json + + @map = { + overlays: MapOverlaysSerializer.new(oecm_overlays, map_yml).serialize + } @filters = { db_type: ['oecm'] } end private + def oecm_overlays + overlays(['oecm'], { + 'oecm': { + isToggleable: false + } + }) + end + def get_tabs total_tabs tabs = [] @@ -24,5 +38,7 @@ def get_tabs total_tabs tabs << tab end + + tabs end end \ No newline at end of file diff --git a/app/controllers/pame_controller.rb b/app/controllers/pame_controller.rb index edadd5347..bebcddcb6 100644 --- a/app/controllers/pame_controller.rb +++ b/app/controllers/pame_controller.rb @@ -1,4 +1,49 @@ class PameController < ApplicationController + DEFAULT_PARAMS = + { + requested_page: 1, + filters: [] + }.to_json + + # Format for this date is: Month and Year (4 digits) + UPDATED_AT = "July 2019".freeze + def index + @table_attributes = PameEvaluation::TABLE_ATTRIBUTES.to_json + @filters = PameEvaluation.filters_to_json + @sources = PameEvaluation.sources_to_json + @json = PameEvaluation.paginate_evaluations(DEFAULT_PARAMS).to_json + @updated_at = UPDATED_AT + + @tabs = get_tabs(3).to_json end -end \ No newline at end of file + + def get_tabs total_tabs + tabs = [] + + total_tabs.times do |i| + tab = { + id: i+1, + title: @cms_page.fragments.where(identifier: "tab-title-#{i+1}").first.content + } + + tabs << tab + end + + tabs + end + + def list + @evaluations = PameEvaluation.paginate_evaluations(params.to_json) + + render json: @evaluations + end + + def download + send_data PameEvaluation.to_csv(params.to_json), { + type: "text/csv; charset=utf-8; header=present", + disposition: "attachment", + filename: "protectedplanet-pame.csv" } + end +end + diff --git a/app/controllers/protected_areas_controller.rb b/app/controllers/protected_areas_controller.rb index d54b445ce..48b056ba1 100644 --- a/app/controllers/protected_areas_controller.rb +++ b/app/controllers/protected_areas_controller.rb @@ -1,6 +1,7 @@ class ProtectedAreasController < ApplicationController after_action :record_visit after_action :enable_caching + include MapHelper def show id = params[:id] @@ -30,6 +31,18 @@ def show @wdpa_other = [] ## 3 other PAs from ...? + @map = { + overlays: MapOverlaysSerializer.new(map_overlays, map_yml).serialize + } + + @map_options = { + map: { + boundsUrl: @protected_area.extent_url + } + } + + helpers.opengraph_title_and_description_with_suffix(@protected_area.name) + respond_to do |format| format.html format.pdf { @@ -45,6 +58,10 @@ def show private + def map_overlays + overlays(['oecm', 'marine_wdpa', 'terrestrial_wdpa']) + end + def get_locations locations = [] diff --git a/app/controllers/region_controller.rb b/app/controllers/region_controller.rb index e6b778cd7..747c89844 100644 --- a/app/controllers/region_controller.rb +++ b/app/controllers/region_controller.rb @@ -1,5 +1,6 @@ class RegionController < ApplicationController before_action :load_vars + include MapHelper def show @iucn_categories = @region.protected_areas_per_iucn_category @@ -25,10 +26,24 @@ def show @total_wdpa = @region.protected_areas.count @wdpa = pas_sample + + @map = { + overlays: MapOverlaysSerializer.new(map_overlays, map_yml).serialize + } + + @map_options = { + map: { boundsUrl: @region.extent_url } + } + + helpers.opengraph_title_and_description_with_suffix(@region.name) end private + def map_overlays + overlays(['oecm', 'marine_wdpa', 'terrestrial_wdpa']) + end + def load_vars params[:iso]!="GL" or raise_404 @region = Region.where(iso: params[:iso].upcase).first diff --git a/app/controllers/search_areas_controller.rb b/app/controllers/search_areas_controller.rb index dbe3a77a2..9defe5561 100644 --- a/app/controllers/search_areas_controller.rb +++ b/app/controllers/search_areas_controller.rb @@ -1,5 +1,6 @@ class SearchAreasController < ApplicationController include Concerns::Searchable + include MapHelper after_action :enable_caching @@ -26,6 +27,11 @@ def index geo_type = search_params[:geo_type] || 'site' @filters = @db_type ? { db_type: @db_type } : {} @results = Search::AreasSerializer.new(@search, geo_type).serialize + + @map = { + overlays: MapOverlaysSerializer.new(search_overlays, map_yml).serialize, + areFiltersHidden: true + } end def search_results @@ -37,6 +43,10 @@ def search_results private + def search_overlays + overlays(['marine_wdpa', 'terrestrial_wdpa']) + end + def search_params params.permit( :search_term, :geo_type, :items_per_page, :requested_page, :filters, diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 49def77af..7137a415c 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -37,13 +37,25 @@ def map end def autocomplete - @results = Autocompletion.lookup(search_params[:search_term], search_params[:type]) + db_type = search_params[:type] + @results = Autocompletion.lookup(search_params[:search_term], db_type, search_index(db_type)) render json: @results end private + def search_index db_type + case db_type + when 'country' + Search::COUNTRY_INDEX + when 'region' + Search::REGION_INDEX + else + Search::PA_INDEX + end + end + def search_params params.permit(:search_term, :type, :requested_page, :items_per_page, :filters) end diff --git a/app/controllers/target_dashboard_controller.rb b/app/controllers/target_dashboard_controller.rb index 145019009..f6319244f 100644 --- a/app/controllers/target_dashboard_controller.rb +++ b/app/controllers/target_dashboard_controller.rb @@ -1,5 +1,4 @@ class TargetDashboardController < ApplicationController - def index countries = Country.paginate(per_page: CountrySerializer::PER_PAGE, page: 1) @countries = CountrySerializer.new({}, countries).serialize diff --git a/app/controllers/wdpa_controller.rb b/app/controllers/wdpa_controller.rb index 19a8e0c37..6a0c12db9 100644 --- a/app/controllers/wdpa_controller.rb +++ b/app/controllers/wdpa_controller.rb @@ -1,4 +1,6 @@ class WdpaController < ApplicationController + include MapHelper + def index @pa_coverage_percentage = 20 ##TODO FERDI - percentage of the world covered by PAs @@ -8,6 +10,17 @@ def index }.to_json @filters = { db_type: ['wdpa'] } + @tabs = helpers.get_cms_tabs(3).to_json + + @map = { + overlays: MapOverlaysSerializer.new(wdpa_overlays, map_yml).serialize + } + end + + private + + def wdpa_overlays + overlays(['marine_wdpa', 'terrestrial_wdpa']) end end \ No newline at end of file diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 989547c02..7ef92e6ab 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,3 +1,4 @@ +# coding: utf-8 module ApplicationHelper include BemHelper @@ -97,121 +98,10 @@ def region_cover(region, with_tag: true) ) end - def site_title - 'Protected Planet' - end - - def site_description - "Discover the world's protected areas" - end - - def page_title(here= false) - custom_title = content_for(:page_title) - - if custom_title - "#{custom_title} | #{site_title}".html_safe - else - site_title - end - end - def url_encode(text) ERB::Util.url_encode(text) end - def encoded_page_url - url_encode(request.original_url) - end - - def social_image - if content_for?(:social_image) - content_for(:social_image) - elsif yml_key.present? && I18n.exists?("#{yml_key}.social_image") - t("#{yml_key}.social_image") - else - URI.join(root_url, image_path('social.png')) - end - end - - def social_image_alt - if content_for?(:social_image_alt) - content_for(:social_image_alt) - elsif yml_key.present? && I18n.exists?("#{yml_key}.social_image_alt") - t("#{yml_key}.social_image_alt") - else - "Screenshot of the Protected Planet website which shows the menu bar and a map of the world that has protected areas highlighted in green." - end - end - - DEFAULT_SEO_DESC = """ - Protected Planet is the online interface for the - World Database on Protected Areas (WDPA), and the most comprehensive - global database on terrestrial and marine protected areas. - """ - - def seo_description - if content_for?(:seo) - content_for(:seo) - else - DEFAULT_SEO_DESC - end - end - - def twitter_card - if content_for?(:twitter_card) - content_for(:twitter_card) - elsif yml_key.present? && I18n.exists?("#{yml_key}.social_twitter_card") - t("#{yml_key}.social_twitter_card") - else - "summary" - end - end - - def social_title - if content_for?(:social_title) - sanitize content_for(:social_title) - elsif yml_key.present? && I18n.exists?("#{yml_key}.title") - t("#{yml_key}.title") - else - page_title 'Protected Planet' - end - end - - def social_description - if content_for?(:social_description) - sanitize content_for(:social_description) - elsif yml_key.present? && I18n.exists?("#{yml_key}.social_description") - t("#{yml_key}.social_description") - else - seo_description - end - end - - def create_sharing_facebook_link - title = url_encode('Share ' + page_title + ' on Facebook') - url = encoded_page_url - href = 'https://facebook.com/sharer/sharer.php?u=' + url - - link_to '', href, title: title, class: 'social__icon--facebook', target: '_blank' - end - - def create_sharing_twitter_link - title = url_encode('Share ' + page_title + ' on Twitter') - text = url_encode('Read about a year of impact in @unepwcmc’s 2018/19 Annual Review') - url = encoded_page_url - href = 'https://twitter.com/intent/tweet/?text=' + text + '&url=' + url - - link_to '', href, title: title, class: 'social__icon--twitter', target: '_blank' - end - - def create_sharing_linkedin_link - title = url_encode('Share ' + page_title + ' on LinkedIn') - url = encoded_page_url - href = 'https://www.linkedin.com/shareArticle?url=' + url - - link_to '', href, title: title, class: 'social__icon--linkedin', target: '_blank' - end - DOWNLOAD_TYPES = { csv: { content: '.CSV', diff --git a/app/helpers/cms_helper.rb b/app/helpers/cms_helper.rb index e7695bf0c..5de2606a2 100644 --- a/app/helpers/cms_helper.rb +++ b/app/helpers/cms_helper.rb @@ -88,6 +88,41 @@ def get_filtered_pages pages pages end + def load_categories + return [] unless @cms_page + layouts_categories = Comfy::Cms::LayoutsCategory.where(layout_id: @cms_page.layout_id) + + # TODO This is a workaround to load the custom categories also based on child pages + # in case the categories for the given page are empty. + # This seems to be necessary now because the layout used for the main page + # can be different from the layout used in the child pages + if layouts_categories.blank? + children_layouts = @cms_page.children.map(&:layout_id) + layouts_categories = Comfy::Cms::LayoutsCategory.where(layout_id: children_layouts) + end + + categories_yml = I18n.t('search')[:custom_categories] + layouts_categories.map do |lc| + name = categories_yml[lc.layout_category.label.to_sym][:name] + page_categories = lc.layout_category.page_categories + localised_pcs = categories_yml[name.to_sym][:items] + + items = page_categories.map do |pc| + { + id: pc.id, + name: localised_pcs[pc.label.to_sym] + } + end + + # frontend should return the list of selected categories as follows: + # 'group_name' => [category_ids] ; e.g. 'topics' => [1,2,3] + { + name: name, + items: items + } + end + end + def cta_api @cta_api ||= CallToAction.find_by_css_class('api') end diff --git a/app/helpers/map_helper.rb b/app/helpers/map_helper.rb new file mode 100644 index 000000000..b575af776 --- /dev/null +++ b/app/helpers/map_helper.rb @@ -0,0 +1,76 @@ +OVERLAYS = [ + { + id: 'terrestrial_wdpa', + isToggleable: false, + layers: ["https://data-gis.unep-wcmc.org/server/rest/services/ProtectedSites/The_World_Database_of_Protected_Areas/MapServer/tile/{z}/{y}/{x}"], + color: "#38A800", + isShownByDefault: true + }, + { + id: 'marine_wdpa', + isToggleable: false, + layers: ["https://data-gis.unep-wcmc.org/server/rest/services/ProtectedSites/The_World_Database_of_Protected_Areas/MapServer/tile/{z}/{y}/{x}"], + color: "#004DA8", + isShownByDefault: true + }, + { + id: 'oecm', + isToggleable: true, + layers: ["https://data-gis.unep-wcmc.org/server/rest/services/ProtectedSites/The_World_Database_on_other_effective_area_based_conservation_measures/MapServer/tile/{z}/{y}/{x}"], + color: "#D9B143", + isShownByDefault: true + } +].freeze + +WDPA_FEATURE_SERVER_URL = 'https://data-gis.unep-wcmc.org/server/rest/services/ProtectedSites/The_World_Database_of_Protected_Areas/FeatureServer' +OECM_FEATURE_SERVER_LAYER_URL = 'https://data-gis.unep-wcmc.org/server/rest/services/ProtectedSites/The_World_Database_on_other_effective_area_based_conservation_measures/FeatureServer/0/' + +SERVICES_FOR_POINT_QUERY = [ + { url: OECM_FEATURE_SERVER_LAYER_URL, isPoint: false }, + { url: WDPA_FEATURE_SERVER_URL + '/0/', isPoint: true }, + { url: WDPA_FEATURE_SERVER_URL + '/1/', isPoint: false } +].freeze + +module MapHelper + def overlays (ids, options={}) + includedOverlays = OVERLAYS.select {|o| ids.include?(o[:id])} + + includedOverlays.map do |defaultOptions| + overlayOptions = options[defaultOptions[:id].to_sym] + + overlayOptions.nil? ? defaultOptions : defaultOptions.merge(overlayOptions) + end + end + + def map_yml + I18n.t('map') + end + + def services_for_point_query + SERVICES_FOR_POINT_QUERY + end + + def country_extent_url (iso3) + { + url: "https://data-gis.unep-wcmc.org/server/rest/services/AdministrativeUnits/GADM_EEZ_Layer/FeatureServer/0/query?where=GID_0+%3D+%27#{iso3}%27&returnGeometry=false&returnExtentOnly=true&outSR=4326&f=pjson", + padding: 5 + } + end + + def region_extent_url (name) + { + url: "https://data-gis.unep-wcmc.org/server/rest/services/AdministrativeUnits/GADM_EEZ_Layer/FeatureServer/0/query?where=region+%3D+%27#{CGI.escape(name)}%27&returnGeometry=false&returnExtentOnly=true&outSR=4326&f=pjson", + padding: 5 + } + end + + def map_search_types + arr = [] + + t('map.search_types').each do |id, translations| + arr.push(translations.merge({id: id})) + end + + arr + end +end diff --git a/app/helpers/metadata_helper.rb b/app/helpers/metadata_helper.rb new file mode 100644 index 000000000..103832003 --- /dev/null +++ b/app/helpers/metadata_helper.rb @@ -0,0 +1,17 @@ +module MetadataHelper + def page_title + page_title = content_for(:page_title) + site_title = t('meta.site.title') + if page_title + "#{page_title} | #{site_title}".html_safe + else + site_title + end + end + + def opengraph_title_and_description_with_suffix(suffix) + opengraph.content('og', + title: t('meta.site.name_with_suffix', suffix: suffix), + description: t('meta.site.title_with_suffix', suffix: suffix)) + end +end diff --git a/app/javascript/classes/ErrorBag.js b/app/javascript/classes/ErrorBag.js new file mode 100644 index 000000000..86c93fd17 --- /dev/null +++ b/app/javascript/classes/ErrorBag.js @@ -0,0 +1,333 @@ +'use strict' + +const pregQuote = (str, delimiter) => (str + '') // equivalent of PHP's preg_quote + .replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + (delimiter || '') + '-]', 'g'), '\\$&') + +const strMatch = function (inputPattern, value) { + var patterns = Array.isArray(inputPattern) ? inputPattern : [inputPattern] + + if (patterns.length === 0) { + return false + } + + for (var pattern of patterns) { + // If the given value is an exact match we can of course return true right + // from the beginning. Otherwise, we will translate asterisks and do an + // actual pattern match against the two strings to see if they match. + if (pattern === value) { + return true + } + pattern = pregQuote(pattern, '/') + // Asterisks are translated into zero-or-more regular expression wildcards + // to make it convenient to check if the strings starts with the given + // pattern such as "library/*", making any string check convenient. + pattern = pattern.replace('*', '.*') + if (new RegExp('^' + pattern + '$', 'u').test(value)) { + return true + } + } + + return false +} + +export default class ErrorBag { + + /** + * Create a new message bag instance. + * + * @param Array messages + * @return void + */ + constructor(messages) { + this.messages = [] + this.format = ':message' + for (var key in messages) { + this.messages[key] = messages[key] + } + } + + + /** + * Get the keys present in the message bag. + * + * @return Array + */ + keys () { + return Object.keys(this.messages) + } + + /** + * Add a message to the message bag. + * + * @param String key + * @param String message + * @return this + */ + add (key, message) { + if (this.isUnique(key, message)) { + if (!Array.isArray(this.messages[key])) { + this.messages[key] = [this.messages[key]] + } + this.messages[key].push(message) + } + + return this + } + + /** + * Determine if a key and message combination already exists. + * + * @param String key + * @param String message + * @return bool + */ + isUnique (key, message) { + var messages = this.messages + + return !(typeof messages[key] !== 'undefined') || !messages[key].includes(message) + } + + /** + * Determine if messages exist for all of the given keys. + * + * @param Array|string key + * @return bool + */ + has (key) { + if (this.isEmpty()) { + return false + } + + if (typeof key === 'undefined') { + return this.any() + } + + var keys = Array.isArray(key) ? key : arguments + + for (var value of keys) { + if (this.first(value) === '') { + return false + } + } + + return true + } + + /** + * Determine if messages exist for any of the given keys. + * + * @param Array|string keys + * @return bool + */ + hasAny (keys = []) { + if (this.isEmpty()) { + return false + } + + keys = Array.isArray(keys) ? keys : arguments + + for (var key of keys) { + if (this.has(key)) { + return true + } + } + + return false + } + + /** + * Get the first message from the message bag for a given key. + * + * @param String key + * @param String format + * @return string + */ + first (key, format) { + var messages = typeof key === 'undefined' ? this.all(format) : this.get(key, format) + + var values = Object.values(messages) + + var firstMessage = values.hasOwnProperty(0) ? values[0] : '' + + return Array.isArray(firstMessage) ? this.first(firstMessage) : firstMessage + } + + /** + * Get all of the messages from the message bag for a given key. + * + * @param String key + * @return Array + */ + get (key, format) { + // If the message exists in the message bag, we will transform it and return + // the message. Otherwise, we will check if the key is implicit & collect + // all the messages that match the given key and output it as an Array. + if (this.messages.hasOwnProperty(key)) { + return this.transform( + this.messages[key], this.checkFormat(format), key + ) + } + + if (key.indexOf('*') !== -1) { + return this.getMessagesForWildcardKey(key, format) + } + + return [] + } + + /** + * Get the messages for a wildcard key. + * + * @param String key + * @param String|null format + * @return Array + */ + getMessagesForWildcardKey (key, format) { + var messages = {} + + for (var messageKey in this.messages) { + if (strMatch(key, messageKey)) { + messages[messageKey] = this.transform( + this.messages[messageKey], this.checkFormat(format), messageKey + ) + } + } + + return Object.values(messages) + } + + /** + * Get all of the messages for every key in the message bag. + * + * @param String format + * @return Array + */ + all (format) { + format = this.checkFormat(format) + + var all = [] + + for (var key in this.messages) { + all = all.concat(this.transform(this.messages[key], format, key)) + } + + return all + } + + /** + * Get all of the unique messages for every key in the message bag. + * + * @param String format + * @return Array + */ + unique (format) { + return [...new Set(this.all(format))] + } + + /** + * Format an Array of messages. + * + * @param Array messages + * @param String format + * @param String messageKey + * @return Array + */ + transform (messages, format, messageKey) { + return Object.values(messages) + .map(message => { + // We will simply spin through the given messages and transform each one + // replacing the :message place holder with the real message allowing + // the messages to be easily formatted to each developer's desires. + // + format = format.replace(':key', messageKey) + + return format.replace(':message', message) + }) + } + + /** + * Get the appropriate format based on the given format. + * + * @param String format + * @return String + */ + checkFormat (format) { + return format ? format : this.format + } + + /** + * Get the raw messages in the message bag. + * + * @return Array + */ + getMessages () { + return this.messages + } + + /** + * Get the default message format. + * + * @return string + */ + getFormat () { + return this.format + } + + /** + * Set the default message format. + * + * @param String format + * @return this + */ + setFormat (format = ':message') { + this.format = format + + return this + } + + /** + * Determine if the message bag has any messages. + * + * @return bool + */ + isEmpty () { + return !this.any() + } + + /** + * Determine if the message bag has any messages. + * + * @return bool + */ + isNotEmpty () { + return this.any() + } + + /** + * Determine if the message bag has any messages. + * + * @return bool + */ + any () { + return this.count() > 0 + } + + /** + * Get the number of messages in the message bag. + * + * @return int + */ + count () { + return Object.values(this.messages).length + } + + /** + * Get the instance as an Array. + * + * @return Array + */ + toArray () { + return this.getMessages() + } + +} diff --git a/app/javascript/components/autocomplete/Autocomplete.vue b/app/javascript/components/autocomplete/Autocomplete.vue new file mode 100644 index 000000000..acd3abf91 --- /dev/null +++ b/app/javascript/components/autocomplete/Autocomplete.vue @@ -0,0 +1,312 @@ +
+
+
+
+
+
+ {{ result.label }} +
+
+
+
+ + + \ No newline at end of file diff --git a/app/javascript/components/charts/chart-row-target/ChartRowTarget.vue b/app/javascript/components/charts/chart-row-target/ChartRowTarget.vue index d8723b624..50afd332e 100644 --- a/app/javascript/components/charts/chart-row-target/ChartRowTarget.vue +++ b/app/javascript/components/charts/chart-row-target/ChartRowTarget.vue @@ -89,8 +89,8 @@ export default { data () { return { rowHeight: 24, - svgStartX: -10, - svgStartY: -40, + svgStartX: -15, + svgStartY: -42, svgWidth: 230, svgHeight: 80, chartWidth: 200, diff --git a/app/javascript/components/filters/FiltersSearch.vue b/app/javascript/components/filters/FiltersSearch.vue index fb5a683b3..cda1db839 100644 --- a/app/javascript/components/filters/FiltersSearch.vue +++ b/app/javascript/components/filters/FiltersSearch.vue @@ -1,5 +1,8 @@ + + diff --git a/app/javascript/components/map/MapInteractive.vue b/app/javascript/components/map/MapInteractive.vue deleted file mode 100644 index 0e15e7902..000000000 --- a/app/javascript/components/map/MapInteractive.vue +++ /dev/null @@ -1,30 +0,0 @@ - - - \ No newline at end of file diff --git a/app/javascript/components/map/MapSearch.vue b/app/javascript/components/map/MapSearch.vue index 3889338c0..6beb54639 100644 --- a/app/javascript/components/map/MapSearch.vue +++ b/app/javascript/components/map/MapSearch.vue @@ -1,5 +1,8 @@ + + diff --git a/app/javascript/components/pame/DataFilterOption.vue b/app/javascript/components/pame/DataFilterOption.vue new file mode 100644 index 000000000..0858e884e --- /dev/null +++ b/app/javascript/components/pame/DataFilterOption.vue @@ -0,0 +1,36 @@ + + + diff --git a/app/javascript/components/pame/FilteredTable.vue b/app/javascript/components/pame/FilteredTable.vue new file mode 100644 index 000000000..f35c1ec5a --- /dev/null +++ b/app/javascript/components/pame/FilteredTable.vue @@ -0,0 +1,101 @@ + + + diff --git a/app/javascript/components/pame/Filters.vue b/app/javascript/components/pame/Filters.vue new file mode 100644 index 000000000..e0c002d1f --- /dev/null +++ b/app/javascript/components/pame/Filters.vue @@ -0,0 +1,79 @@ + + + diff --git a/app/javascript/components/pame/PamePagination.vue b/app/javascript/components/pame/PamePagination.vue new file mode 100644 index 000000000..5c91b53fe --- /dev/null +++ b/app/javascript/components/pame/PamePagination.vue @@ -0,0 +1,109 @@ + + + diff --git a/app/javascript/components/pame/PameTableHead.vue b/app/javascript/components/pame/PameTableHead.vue new file mode 100644 index 000000000..49b1f6fab --- /dev/null +++ b/app/javascript/components/pame/PameTableHead.vue @@ -0,0 +1,61 @@ + + + diff --git a/app/javascript/components/pame/Row.vue b/app/javascript/components/pame/Row.vue new file mode 100644 index 000000000..7ac83d30c --- /dev/null +++ b/app/javascript/components/pame/Row.vue @@ -0,0 +1,83 @@ + + + diff --git a/app/javascript/components/pame/SelectedFilter.vue b/app/javascript/components/pame/SelectedFilter.vue new file mode 100644 index 000000000..396a0e694 --- /dev/null +++ b/app/javascript/components/pame/SelectedFilter.vue @@ -0,0 +1,36 @@ + + + diff --git a/app/javascript/components/pame/TableHeader.vue b/app/javascript/components/pame/TableHeader.vue new file mode 100644 index 000000000..7f86e6584 --- /dev/null +++ b/app/javascript/components/pame/TableHeader.vue @@ -0,0 +1,46 @@ + + + diff --git a/app/javascript/components/search/SearchAreas.vue b/app/javascript/components/search/SearchAreas.vue index 74e920687..3ecfd054f 100644 --- a/app/javascript/components/search/SearchAreas.vue +++ b/app/javascript/components/search/SearchAreas.vue @@ -3,6 +3,7 @@
@@ -28,10 +30,12 @@
- +
+ +
{ + this.$eventHub.$emit('map:resize') + }) + } } } } diff --git a/app/javascript/components/search/SearchSiteInput.vue b/app/javascript/components/search/SearchSiteInput.vue index 131bcaf35..70a8040c5 100644 --- a/app/javascript/components/search/SearchSiteInput.vue +++ b/app/javascript/components/search/SearchSiteInput.vue @@ -11,6 +11,7 @@ > this.$refs.input.focus(), 0) + } } } diff --git a/app/javascript/components/select/Selector.vue b/app/javascript/components/select/Selector.vue new file mode 100644 index 000000000..d39ba2c5e --- /dev/null +++ b/app/javascript/components/select/Selector.vue @@ -0,0 +1,199 @@ + + \ No newline at end of file diff --git a/app/javascript/components/table/TableHead.vue b/app/javascript/components/table/TableHead.vue index 30e27edd6..0d2721fc2 100644 --- a/app/javascript/components/table/TableHead.vue +++ b/app/javascript/components/table/TableHead.vue @@ -5,18 +5,16 @@ :key="getVForKey('row', index)" class="table-head__cell" > - - {{ heading.title }} + {{ heading.title }} - - - - + + + -
@@ -63,8 +63,8 @@ export default { data () { return { - isActive: false, - id: `tooltip_${this._uid}` + id: `tooltip_${this._uid}`, + isActive: false } }, diff --git a/app/javascript/helpers/array-helpers.js b/app/javascript/helpers/array-helpers.js new file mode 100644 index 000000000..84990b8b4 --- /dev/null +++ b/app/javascript/helpers/array-helpers.js @@ -0,0 +1,11 @@ +export const containsObjectWithId = (array, id) => + Boolean(array.filter(x => x.id === id).length) + + +export const addObjectToArrayIfAbsent = (array, object) => { + if(!containsObjectWithId(array, object.id)) { + return array.concat([object]) + } + + return [...array] +} \ No newline at end of file diff --git a/app/javascript/helpers/axios-helpers.js b/app/javascript/helpers/axios-helpers.js new file mode 100644 index 000000000..2791f31c0 --- /dev/null +++ b/app/javascript/helpers/axios-helpers.js @@ -0,0 +1,9 @@ +export const setAxiosHeaders = axios => { + const token = document.head.querySelector('meta[name="csrf-token"]') + + if (token) { + axios.defaults.headers.common['X-CSRF-Token'] = token.content + } else { + console.error('CSRF token not found') + } +} \ No newline at end of file diff --git a/app/javascript/helpers/timing-helpers.js b/app/javascript/helpers/timing-helpers.js new file mode 100644 index 000000000..536416b23 --- /dev/null +++ b/app/javascript/helpers/timing-helpers.js @@ -0,0 +1,15 @@ +export const executeAfterCondition = (conditonCb, successCb, maxAttempts=0) => { + let attempts = 0 + + const interval = setInterval(() => { + attempts++ + + if ( + conditonCb() || + (maxAttempts && attempts > maxAttempts) + ) { + clearInterval(interval) + successCb() + } + }, 200) +} \ No newline at end of file diff --git a/app/javascript/mixins/mixin-axios-helpers.js b/app/javascript/mixins/mixin-axios-helpers.js index f04666e00..dd0dd7a36 100644 --- a/app/javascript/mixins/mixin-axios-helpers.js +++ b/app/javascript/mixins/mixin-axios-helpers.js @@ -1,11 +1,10 @@ +import { setAxiosHeaders } from '../helpers/axios-helpers' import axios from 'axios' export default { methods: { axiosSetHeaders () { - const csrf = document.querySelectorAll('meta[name="csrf-token"]')[0].getAttribute('content') - - axios.defaults.headers.common['X-CSRF-Token'] = csrf + setAxiosHeaders(axios) } } } \ No newline at end of file diff --git a/app/javascript/mixins/mixin-element-event-handler.js b/app/javascript/mixins/mixin-element-event-handler.js new file mode 100644 index 000000000..9465ba96f --- /dev/null +++ b/app/javascript/mixins/mixin-element-event-handler.js @@ -0,0 +1,10 @@ +export default (el, ev, handler) => { + return { + created () { + el.addEventListener(ev, handler.bind(this)) + }, + beforeDestroy() { + el.removeEventListener(ev, handler.bind(this)) + }, + } +} \ No newline at end of file diff --git a/app/javascript/mixins/mixin-responsive.js b/app/javascript/mixins/mixin-responsive.js index 607964146..4f5ebd801 100644 --- a/app/javascript/mixins/mixin-responsive.js +++ b/app/javascript/mixins/mixin-responsive.js @@ -4,7 +4,7 @@ export default { windowWidth: 0, currentBreakpoint: '', breakpoints: { - small: 768, //must match variables in assets/stylesheets/resources/_base-varibles + small: 767, // MUST MATCH VARIABLES IN assets/stylesheets/_settings medium: 1024, large: 1200 } diff --git a/app/javascript/store/_store-map.js b/app/javascript/store/_store-map.js new file mode 100644 index 000000000..550569be0 --- /dev/null +++ b/app/javascript/store/_store-map.js @@ -0,0 +1,49 @@ +import { addObjectToArrayIfAbsent } from '../helpers/array-helpers' + +export const storeMap = { + namespaced: true, + + state: { + visibleOverlays: [], + visibleLayers: [], + selectedBaselayer: {} + }, + + actions: { + addOverlay ({ commit }, overlay) { + commit('addOverlay', overlay) + overlay.layers.forEach(l => commit('addLayer', l)) + }, + + removeOverlay ({ commit }, overlay) { + commit('removeOverlay', overlay) + overlay.layers.forEach(l => commit('removeLayer', l)) + }, + + updateSelectedBaselayer ({ commit }, layer) { + commit('updateSelectedBaselayer', layer) + } + }, + + mutations: { + addOverlay (state, overlay) { + state.visibleOverlays = addObjectToArrayIfAbsent(state.visibleOverlays, overlay) + }, + + removeOverlay (state, overlay) { + state.visibleOverlays = state.visibleOverlays.filter( x => x.id !== overlay.id) + }, + + addLayer (state, layer) { + state.visibleLayers = addObjectToArrayIfAbsent(state.visibleLayers, layer) + }, + + removeLayer (state, layer) { + state.visibleLayers = state.visibleLayers.filter( x => x.id !== layer.id) + }, + + updateSelectedBaselayer (state, layer) { + state.selectedBaselayer = layer + } + }, +} diff --git a/app/javascript/store/_store-pame.js b/app/javascript/store/_store-pame.js new file mode 100644 index 000000000..9ad6cca77 --- /dev/null +++ b/app/javascript/store/_store-pame.js @@ -0,0 +1,64 @@ +export const storePame = { + namespaced: true, + + state: { + totalItemsOnCurrentPage: 0, + requestedPage: 1, + selectedFilterOptions: [], // an array containing an object for each filter that has an array of selected options + modalContent: {}, + sortDirection: '' + }, + + mutations: { + updateRequestedPage (state, page) { + state.requestedPage = page + }, + + updateTotalItemsOnCurrentPage (state, total) { + state.totalItemsOnCurrentPage = total + }, + + setFilterOptions (state, options) { + state.selectedFilterOptions = options + }, + + updateFilterOptions (state, newOptions) { + // find the correct filter to update + state.selectedFilterOptions.forEach(filter => { + if(filter.name == newOptions.filter){ + + // replace filter options array with newOptions array + filter.options = newOptions.options + } + }) + }, + + clearFilterOptions (state) { + state.selectedFilterOptions.forEach(filter => { + filter.options = [] + }) + }, + + removeFilterOption (state, removeOption) { + state.selectedFilterOptions.forEach(filter => { + if(filter.name == removeOption.name){ + filter.options.forEach(option => { + if(option == removeOption.option){ + const index = filter.options.indexOf(removeOption.option) + + filter.options.splice(index, 1) + } + }) + } + }) + }, + + updateModalContent (state, content) { + state.modalContent = content + }, + + updateSortDirection (state, direction) { + state.sortDirection = direction + } + } +} \ No newline at end of file diff --git a/app/javascript/store/store.js b/app/javascript/store/store.js index de34a5b5e..4af62474f 100644 --- a/app/javascript/store/store.js +++ b/app/javascript/store/store.js @@ -1,18 +1,19 @@ -// polyfills import { polyfill } from 'es6-promise' polyfill() -// dependencies import Vue from 'vue/dist/vue.esm' import Vuex from 'vuex/dist/vuex.esm' Vue.use(Vuex) -// stores +import { storeMap } from './_store-map.js' +import { storePame } from './_store-pame.js' import { storeTable } from './_store-table.js' export default new Vuex.Store({ modules: { + map: storeMap, + pame: storePame, table: storeTable } }) diff --git a/app/javascript/vue.js b/app/javascript/vue.js index 75d3fdf71..d99550d01 100644 --- a/app/javascript/vue.js +++ b/app/javascript/vue.js @@ -7,7 +7,6 @@ polyfill() import Vue from 'vue/dist/vue.esm' import VueAnalytics from 'vue-analytics' import Vue2TouchEvents from 'vue2-touch-events' -import ScrollMagic from 'scrollmagic' import VueLazyload from 'vue-lazyload' // store @@ -32,7 +31,7 @@ import ChartRowTarget from './components/charts/chart-row-target/ChartRowTarget' import ChartSunburst from './components/charts/chart-sunburst/ChartSunburst' import Flickity from 'vue-flickity'; import Download from './components/download/Download' -import MapInteractive from './components/map/MapInteractive' +import FilteredTable from './components/pame/FilteredTable' import NavBurger from './components/nav/NavBurger' import SearchAreas from './components/search/SearchAreas' import SearchAreasHome from './components/search/SearchAreasHome' @@ -47,9 +46,16 @@ import Tabs from './components/tabs/Tabs' import TabTarget from './components/tabs/TabTarget' import Target11Dashboard from './components/pages/Target11Dashboard' import Tooltip from './components/tooltip/Tooltip' +import VMapDisclaimer from './components/map/VMapDisclaimer' +import VMap from './components/map/VMap' +import VMapPASearch from './components/map/VMapPASearch' +import VMapHeader from './components/map/VMapHeader' +import VMapFilters from './components/map/VMapFilters' import VSelectSearchable from './components/select/VSelectSearchable' import VTable from './components/table/VTable' +export const eventHub = new Vue() + document.addEventListener('DOMContentLoaded', () => { if(document.getElementById('v-app')) { @@ -83,8 +89,8 @@ document.addEventListener('DOMContentLoaded', () => { ChartRowTarget, ChartSunburst, Download, + FilteredTable, Flickity, - MapInteractive, NavBurger, SearchAreas, SearchAreasHome, @@ -99,6 +105,11 @@ document.addEventListener('DOMContentLoaded', () => { TabTarget, Target11Dashboard, Tooltip, + VMap, + 'v-map-pa-search': VMapPASearch, + VMapDisclaimer, + VMapHeader, + VMapFilters, VSelectSearchable, VTable } diff --git a/app/models/comfy/cms/layout_category.rb b/app/models/comfy/cms/layout_category.rb new file mode 100644 index 000000000..5f8bdd2b5 --- /dev/null +++ b/app/models/comfy/cms/layout_category.rb @@ -0,0 +1,7 @@ +class Comfy::Cms::LayoutCategory < ApplicationRecord + self.table_name = 'comfy_cms_layout_categories' + + has_many :page_categories + has_many :layouts_categories + has_many :layouts, through: :layouts_categories +end \ No newline at end of file diff --git a/app/models/comfy/cms/layouts_category.rb b/app/models/comfy/cms/layouts_category.rb new file mode 100644 index 000000000..77dc7dfe1 --- /dev/null +++ b/app/models/comfy/cms/layouts_category.rb @@ -0,0 +1,6 @@ +class Comfy::Cms::LayoutsCategory < ApplicationRecord + self.table_name = 'comfy_cms_layouts_categories' + + belongs_to :layout + belongs_to :layout_category +end \ No newline at end of file diff --git a/app/models/comfy/cms/page_category.rb b/app/models/comfy/cms/page_category.rb new file mode 100644 index 000000000..6313f88ac --- /dev/null +++ b/app/models/comfy/cms/page_category.rb @@ -0,0 +1,7 @@ +class Comfy::Cms::PageCategory < ApplicationRecord + self.table_name = 'comfy_cms_page_categories' + + belongs_to :layout_category + has_many :pages_categories + has_many :pages, through: :pages_categories +end \ No newline at end of file diff --git a/app/models/comfy/cms/pages_category.rb b/app/models/comfy/cms/pages_category.rb new file mode 100644 index 000000000..310446755 --- /dev/null +++ b/app/models/comfy/cms/pages_category.rb @@ -0,0 +1,6 @@ +class Comfy::Cms::PagesCategory < ApplicationRecord + self.table_name = 'comfy_cms_pages_categories' + + belongs_to :page + belongs_to :page_category +end \ No newline at end of file diff --git a/app/models/comfy/cms/searchable_page.rb b/app/models/comfy/cms/searchable_page.rb index 23a8efa09..6e506893d 100644 --- a/app/models/comfy/cms/searchable_page.rb +++ b/app/models/comfy/cms/searchable_page.rb @@ -17,6 +17,8 @@ def as_indexed_json include: { fragments_for_index: { only: [:id, :content] } } }, categories: { only: [:id, :label] }, + topics: { only: [:id, :label] }, + page_types: { only: [:id, :label] }, ancestors: { only: [:id, :label] } } ) diff --git a/app/models/country.rb b/app/models/country.rb index a1c34826d..d143f7934 100644 --- a/app/models/country.rb +++ b/app/models/country.rb @@ -1,5 +1,6 @@ class Country < ApplicationRecord include GeometryConcern + include MapHelper has_and_belongs_to_many :protected_areas @@ -50,6 +51,10 @@ def as_indexed_json options={} js end + def extent_url + country_extent_url(iso_3) + end + def random_protected_areas wanted=1 random_offset = rand(protected_areas.count-wanted) protected_areas.offset(random_offset).limit(wanted) diff --git a/app/models/green_list_status.rb b/app/models/green_list_status.rb new file mode 100644 index 000000000..37a488a99 --- /dev/null +++ b/app/models/green_list_status.rb @@ -0,0 +1,3 @@ +class GreenListStatus < ApplicationRecord + has_one :protected_area +end \ No newline at end of file diff --git a/app/models/pame_evaluation.rb b/app/models/pame_evaluation.rb index 5be35bada..1ad02e853 100644 --- a/app/models/pame_evaluation.rb +++ b/app/models/pame_evaluation.rb @@ -1,7 +1,335 @@ +require 'csv' +require 'wcmc_components' + class PameEvaluation < ApplicationRecord - belongs_to :protected_area + include WcmcComponents::Loadable + + belongs_to :protected_area, optional: true belongs_to :pame_source has_and_belongs_to_many :countries - validates :methodology, :year, :metadata_id, :url, presence: true + validates :methodology, :year, :metadata_id, presence: true + + # config for loadable mixin + ignore_column :designation + + import_by protected_area: :wdpa_id + import_by countries: :iso_3 + + + TABLE_ATTRIBUTES = [ + { + title: 'Name', + field: 'name' + }, + { + title: 'Designation', + field: 'designation' + }, + { + title: 'WDPA ID', + field: 'wdpa_id', + tooltip: 'Unrestricted Protected Areas can be viewed on Protected Planet' + }, + { + title: 'Assessment ID', + field: 'id' + }, + { + title: 'Country', + field: 'iso3' + }, + { + title: 'Methodology', + field: 'methodology' + }, + { + title: 'Year of assessment', + field: 'year' + }, + { + title: 'Link to assessment', + field: 'url' + }, + { + title: 'Metadata ID', + field: 'metadata_id' + } + ].freeze + + def self.paginate_evaluations(json=nil, order=nil) + json_params = json.nil? ? nil : JSON.parse(json) + page = json_params.present? ? json_params["requested_page"].to_i : 1 + + order = (order && ['ASC', 'DESC'].include?(order.upcase)) ? order : 'DESC' + evaluations = generate_query(page, json_params["filters"]) + items = serialise(evaluations) + structure_data(page, items) + end + + def self.structure_data(page, items) + { + current_page: page, + per_page: 50, + total_entries: (items.count > 0 ? items[0][:total_entries] : 0), + total_pages: (items.count > 0 ? items[0][:total_pages] : 0), + items: items + } + end + + def self.generate_query(page, filter_params) + # if params are empty then return the paginated results without filtering + return PameEvaluation.where('protected_area_id IS NOT NULL AND restricted = false').order('id ASC').paginate(page: page || 1, per_page: 50) if filter_params.empty? + + filters = filter_params.select { |hash| hash["options"].present? } + + where_params = parse_filters(filters) + run_query(page, where_params) + end + + def self.parse_filters(filters) + site_ids = [] + country_ids = [] + where_params = {sites: "", methodology: "", year: "", iso3: ""} + filters.each do |filter| + options = filter["options"] + case filter['name'] + when 'iso3' + countries = options + site_ids << countries.map{ |iso3| Country.find_by(iso_3: iso3).protected_areas.pluck(:id) } + where_params[:sites] = site_ids.flatten.empty? ? nil : "pame_evaluations.protected_area_id IN (#{site_ids.join(',')})" + country_ids << countries.map{ |iso3| "#{ Country.find_by(iso_3: iso3).id }" } + where_params[:iso3] = country_ids.flatten.empty? ? nil : "countries.id IN (#{country_ids.join(',')})" + when 'methodology' + options = options.map{ |e| "'#{e}'" } + where_params[:methodology] = options.empty? ? nil : "methodology IN (#{options.join(',')})" + when 'year' + where_params[:year] = options.empty? ? nil : "pame_evaluations.year IN (#{options.join(',')})" + end + end + where_params + end + + def self.run_query(page, where_params) + if where_params[:sites].present? + query = PameEvaluation.connection.unprepared_statement { + "((#{pame_evaluations_from_pa_query(where_params)}) UNION (#{pame_evaluations_from_countries_query(where_params)})) AS pame_evaluations" + } + + PameEvaluation + .from(query) + else + PameEvaluation + .where(where_params[:methodology]) + .where(where_params[:year]) + end + .where("protected_area_id IS NOT NULL AND restricted = false") + .paginate(page: page || 1, per_page: 50).order('id ASC') + end + + def self.pame_evaluations_from_pa_query(where_params) + PameEvaluation + .joins(:protected_area) + .where(where_params[:sites]) + .where(where_params[:methodology]) + .where(where_params[:year]) + .to_sql + end + + def self.pame_evaluations_from_countries_query(where_params) + PameEvaluation + .joins(:countries) + .where(where_params[:iso3]) + .where(where_params[:methodology]) + .where(where_params[:year]) + .to_sql + end + + def self.serialise(evaluations) + evaluations.to_a.map! do |evaluation| + + wdpa_id = evaluation.protected_area&.wdpa_id || evaluation.wdpa_id + name = evaluation.protected_area&.name || evaluation.name + designation = evaluation.protected_area&.designation&.name || "N/A" + countries = evaluation.protected_area&.countries || evaluation.countries + iso3 = countries.pluck(:iso_3).sort + { + current_page: evaluations.current_page, + per_page: evaluations.per_page, + total_entries: evaluations.total_entries, + total_pages: evaluations.total_pages, + id: evaluation.id, + wdpa_id: wdpa_id, + wdpa_url: Rails.application.routes.url_helpers.protected_area_path(wdpa_id), + restricted: evaluation.restricted, + iso3: iso3, + methodology: evaluation.methodology, + year: evaluation.year.to_s, + url: evaluation.url, + metadata_id: evaluation.metadata_id, + source_id: evaluation.pame_source&.id, + name: name, + designation: designation, + data_title: evaluation.pame_source&.data_title, + resp_party: evaluation.pame_source&.resp_party, + language: evaluation.pame_source&.language, + source_year: evaluation.pame_source&.year + } + end + end + + def self.sources_to_json + sources = PameSource.all.order(id: :asc) + sources.to_a.map! do |source| + { + id: source.id, + data_title: source.data_title, + resp_party: source.resp_party, + year: source.year, + language: source.language + } + end.to_json + end + + def self.filters_to_json + unique_methodologies = PameEvaluation.pluck(:methodology).uniq.sort + unique_iso3 = Country.pluck(:iso_3).compact.uniq.sort + unique_year = PameEvaluation.pluck(:year).uniq.map(&:to_s).sort + + [ + { + name: 'methodology', + title: 'Methodology', + options: unique_methodologies, + type: 'multiple' + }, + { + name: "iso3", + title: "Country", + options: unique_iso3, + type: 'multiple' + }, + { + name: "year", + title: "Year of assessment", + options: unique_year, + type: 'multiple' + } + ].to_json + end + + def self.generate_csv(where_statement, restricted_where_statement) + where_statement = where_statement.empty? ? '' : "WHERE #{where_statement}" + restricted_where_statement = restricted_where_statement.empty? ? '' : "WHERE #{restricted_where_statement}" + query = <<-SQL + SELECT pame_evaluations.id AS id, + pame_evaluations.metadata_id AS metadata_id, + pame_evaluations.url AS url, + pame_evaluations.year AS evaluation_year, + pame_evaluations.methodology AS methodology, + protected_areas.wdpa_id AS wdpa_id, + ARRAY_TO_STRING(ARRAY_AGG(countries.iso_3),';') AS countries, + protected_areas.name AS site_name, + designations.name AS designation, + pame_sources.data_title AS data_title, + pame_sources.resp_party AS resp_party, + pame_sources.year AS source_year, + pame_sources.language AS language + FROM pame_evaluations + INNER JOIN protected_areas ON pame_evaluations.protected_area_id = protected_areas.id + LEFT JOIN pame_sources ON pame_evaluations.pame_source_id = pame_sources.id + INNER JOIN countries_protected_areas ON protected_areas.id = countries_protected_areas.protected_area_id + INNER JOIN countries ON countries_protected_areas.country_id = countries.id + INNER JOIN designations ON protected_areas.designation_id = designations.id + #{where_statement} + GROUP BY pame_evaluations.id, protected_areas.wdpa_id, protected_areas.name, designation, pame_sources.data_title, + pame_sources.resp_party, pame_sources.year, pame_sources.language + + UNION + + SELECT pame_evaluations.id AS id, + pame_evaluations.metadata_id AS metadata_id, + pame_evaluations.url AS url, + pame_evaluations.year AS evaluation_year, + pame_evaluations.methodology AS methodology, + pame_evaluations.wdpa_id AS wdpa_id, + ARRAY_TO_STRING(ARRAY_AGG(countries.iso_3),';') AS countries, + pame_evaluations.name AS site_name, + NULL AS designation, + pame_sources.data_title AS data_title, + pame_sources.resp_party AS resp_party, + pame_sources.year AS source_year, + pame_sources.language AS language + FROM pame_evaluations + INNER JOIN pame_sources ON pame_evaluations.pame_source_id = pame_sources.id + INNER JOIN countries_pame_evaluations ON pame_evaluations.id = countries_pame_evaluations.pame_evaluation_id + INNER JOIN countries ON countries_pame_evaluations.country_id = countries.id + #{restricted_where_statement} + GROUP BY pame_evaluations.id, wdpa_id, pame_evaluations.name, designation, pame_sources.data_title, + pame_sources.resp_party, pame_sources.year, pame_sources.language; + SQL + evaluations = ActiveRecord::Base.connection.execute(query) + + csv_string = CSV.generate(encoding: 'UTF-8') do |csv_line| + + evaluation_columns = PameEvaluation.new.attributes.keys + evaluation_columns << "evaluation_id" + + excluded_attributes = ["assessment_is_public", "restricted", "protected_area_id", "pame_source_id", "created_at", "updated_at", "id", "site_id", "source_id"] + + evaluation_columns.delete_if { |k, v| excluded_attributes.include? k } + + additional_columns = ["iso3", "designation", "source_data_title", "source_resp_party", "source_year", "source_language"] + evaluation_columns << additional_columns.map{ |e| "#{e}" } + + csv_line << evaluation_columns.flatten + + evaluations.each do |evaluation| + evaluation_attributes = PameEvaluation.new.attributes + + evaluation_attributes.delete_if { |k, v| excluded_attributes.include? k } + + evaluation_attributes["evaluation_id"] = evaluation["id"] + evaluation_attributes["metadata_id"] = evaluation["metadata_id"] + evaluation_attributes["url"] = evaluation["url"] || "N/A" + evaluation_attributes["year"] = evaluation["evaluation_year"] + evaluation_attributes["methodology"] = evaluation["methodology"] + evaluation_attributes["wdpa_id"] = evaluation["wdpa_id"] + evaluation_attributes["iso_3"] = evaluation['countries'] + evaluation_attributes["name"] = evaluation["site_name"] + evaluation_attributes["designation"] = evaluation["designation"] || "N/A" + evaluation_attributes["source_data_title"] = evaluation["data_title"] + evaluation_attributes["source_resp_party"] = evaluation["resp_party"] + evaluation_attributes["source_year"] = evaluation["source_year"] + evaluation_attributes["source_language"] = evaluation["language"] + + evaluation_attributes = evaluation_attributes.values.map{ |e| "#{e}" } + csv_line << evaluation_attributes + end + end + csv_string + end + + def self.to_csv(json = nil) + json_params = json.nil? ? nil : JSON.parse(json) + filter_params = json_params["_json"].nil? ? nil : json_params["_json"] + + where_statement = [] + restricted_where_statement = [] + where_params = parse_filters(filter_params) + where_params.map do |k, v| + where_statement << v unless v.nil? + restricted_where_statement << v if !v.nil? && k != :sites + end + + where_statement << '(pame_evaluations.protected_area_id IS NOT NULL AND restricted = false)' + where_statement = where_statement.join(' AND ') + + restricted_where_statement << '(pame_evaluations.protected_area_id IS NULL AND restricted = false)' + restricted_where_statement = restricted_where_statement.join(' AND ') + + generate_csv(where_statement, restricted_where_statement) + end end + + diff --git a/app/models/protected_area.rb b/app/models/protected_area.rb index c0391aaec..dc08b096c 100644 --- a/app/models/protected_area.rb +++ b/app/models/protected_area.rb @@ -19,6 +19,7 @@ class ProtectedArea < ApplicationRecord belongs_to :designation delegate :jurisdiction, to: :designation, allow_nil: true belongs_to :wikipedia_article + belongs_to :green_list_status after_create :create_slug @@ -30,7 +31,7 @@ class ProtectedArea < ApplicationRecord } scope :green_list_areas, -> { - where(is_green_list: true) + where.not(green_list_status_id: nil) } scope :most_protected_marine_areas, -> (limit) { @@ -76,6 +77,10 @@ def self.green_list_total_km green_list_areas.inject(0) { |_sum, pa| _sum + pa.gis_area } end + def is_green_list + green_list_status_id.present? + end + def wdpa_ids wdpa_id end @@ -152,8 +157,21 @@ def self.sum_of_most_protected_marine_areas reported_areas.inject(0){ |sum, area| sum + area.to_i } end + def extent_url + layer_number = is_point? ? 0 : 1 + + { + url: "#{WDPA_FEATURE_SERVER_URL}/#{layer_number}/query?where=wdpaid+%3D+%27#{wdpa_id}%27&returnGeometry=false&returnExtentOnly=true&outSR=4326&f=pjson", + padding: 1 + } + end + private + def is_point? + the_geom.geometry_type.type_name.match('Point').present? + end + def bounding_box_query dirty_query = """ SELECT ST_XMax(extent) AS max_x, diff --git a/app/models/region.rb b/app/models/region.rb index 751a2becd..5e3ae4083 100644 --- a/app/models/region.rb +++ b/app/models/region.rb @@ -1,5 +1,6 @@ class Region < ApplicationRecord include GeometryConcern + include MapHelper has_many :countries has_many :protected_areas, through: :countries @@ -80,6 +81,8 @@ def protected_areas_per_iucn_category processed_data end + + def sources_per_jurisdiction ActiveRecord::Base.connection.execute(""" SELECT jurisdictions.name, COUNT(DISTINCT protected_areas_sources.source_id) @@ -106,6 +109,10 @@ def as_indexed_json options={} ) end + def extent_url + region_extent_url(name) + end + def protected_areas_per_designation(jurisdiction=nil) ActiveRecord::Base.connection.execute(""" SELECT designations.id AS designation_id, designations.name AS designation_name, pas_per_designations.count diff --git a/app/presenters/protected_area_presenter.rb b/app/presenters/protected_area_presenter.rb index 14cef4209..0d402181b 100644 --- a/app/presenters/protected_area_presenter.rb +++ b/app/presenters/protected_area_presenter.rb @@ -111,23 +111,42 @@ def attributes def external_links [ - { - title: 'View more', - image_url: ActionController::Base.helpers.image_url('logos/green-list.png'), - link_title: "View the Green List page for #{protected_area.name}", - url: '' ##TODO links needed from CSV provided by IUCN. - }, - { - title: 'View more', - image_url: ActionController::Base.helpers.image_url('logos/parcc.png'), - link_title: "View the climate change vulnerability assessments for #{protected_area.name}", - link_url: url_for_related_source('parcc_info', protected_area) - } - ] + green_list_status_info, + parcc_info + ].compact end private + def green_list_status_info + return unless protected_area.green_list_status_id + + gls = protected_area.green_list_status + { + affiliation: 'greenlist', + date: gls.expiry_date, + image_url: green_list_logo(gls.status), + link_title: "View the Green List page for #{protected_area.name}", + type: gls.status, + url: '' ##TODO links needed from CSV provided by IUCN. + } + end + + def green_list_logo(status) + logo = status.downcase == 'candidate' ? 'green-list-black' : 'green-list' + ActionController::Base.helpers.image_url("logos/#{logo}.png") + end + + def parcc_info + return unless protected_area.has_parcc_info + + { + image_url: ActionController::Base.helpers.image_url('logos/parcc.png'), + link_title: "View the climate change vulnerability assessments for #{protected_area.name}", + link_url: url_for_related_source('parcc_info', protected_area) + } + end + def protected_area @protected_area end @@ -161,7 +180,7 @@ def num_fields_with_data def parse_management_plan management_plan if (management_plan.is_a? String) && (management_plan.starts_with?("http")) - link_to("View Management Plan", management_plan) + ActionController::Base.helpers.link_to("View Management Plan", management_plan) else management_plan end diff --git a/app/presenters/thematical_areas_presenter.rb b/app/presenters/thematical_areas_presenter.rb index cb090af0a..9cca0ffa4 100644 --- a/app/presenters/thematical_areas_presenter.rb +++ b/app/presenters/thematical_areas_presenter.rb @@ -41,7 +41,7 @@ def pas_figure(label) when theme(:marine) pas.where(marine: true) when theme(:green_list) - pas.where(is_green_list: true) + pas.where.not(green_list_status_id: nil) when theme(:wdpa) pas.wdpas when theme(:oecm) diff --git a/app/serializers/map_overlays_serializer.rb b/app/serializers/map_overlays_serializer.rb new file mode 100644 index 000000000..3e6e58668 --- /dev/null +++ b/app/serializers/map_overlays_serializer.rb @@ -0,0 +1,31 @@ +class MapOverlaysSerializer + + def initialize(overlays, yml) + @overlays = overlays + @yml = yml + end + + def serialize + @overlays.map do |overlay| + overlay.merge({ + title: @yml[:overlays][overlay[:id].to_sym][:title], + layers: get_layers(overlay) + }) + end + end + + private + + def get_layers(overlay) + max_index = overlay[:layers].count-1 + + (0..max_index).map{ |i| get_layer(overlay, i) } + end + + def get_layer(overlay, index) + { + id: "#{overlay[:id]}_#{index}", + url: overlay[:layers][index] + } + end +end diff --git a/app/views/country/show.html.erb b/app/views/country/show.html.erb index 328f94135..9fd323311 100644 --- a/app/views/country/show.html.erb +++ b/app/views/country/show.html.erb @@ -1,7 +1,7 @@
-
+
<%= render partial: "partials/stats/stats-overview", locals: { flag: @flag_path[0], title: @country.name, @@ -12,9 +12,10 @@ total_wdpa: @total_wdpa, } %> -
- MAP -
+ <%= render partial: "partials/maps/header", locals: { + map: @map, + map_options: @map_options + } %>
@@ -56,15 +57,9 @@ <%= render partial: "partials/cards/sites", locals: { cards: @sites } %> <%= render partial: "partials/messages/message-citation", locals: { title: @country.name } %> - - -Download this dataset -<%= download_dropdown(@country.iso_3, 'general', (types rescue [:csv, :shp])) %> - - -
- +
+
+

related countries

<% if @country.children.any? || @country.parent.present? %> @@ -126,40 +121,3 @@ data-control-position="bottomleft">
<% end %>
<% end %> - - - - - - - diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb index 7931ec566..4b3ba9680 100644 --- a/app/views/home/index.html.erb +++ b/app/views/home/index.html.erb @@ -19,8 +19,10 @@ <%= render partial: "partials/cards/squares", locals: { cards: pas_categories } %> -
- <%= render partial: "partials/maps/main" %> +
+ <%= render partial: "partials/maps/main", locals: { + map: @main_map + } %>
diff --git a/app/views/layouts/cms/_thematic-areas-basic.html.erb b/app/views/layouts/cms/_thematic-areas-basic.html.erb index fc1480ddb..a1151d5f9 100644 --- a/app/views/layouts/cms/_thematic-areas-basic.html.erb +++ b/app/views/layouts/cms/_thematic-areas-basic.html.erb @@ -1,13 +1,13 @@ -<%= render partial: "./layouts/partials/hero-small", locals: { +<%= render partial: "./layouts/partials/hero-basic", locals: { classes: 'thematical-area' , hero_image: cms_fragment_render(:theme_image, @cms_page), + summary: cms_fragment_render("summary"), title: @cms_page.label } %> -
+
<%= cms_fragment_render("content") %>
-<%#= render "partials/cards/themes" %> -<%#= render "partials/cards/resources" %> +<%= render "partials/carousels/themes" %> <%= render "partials/ctas/live-report" %> diff --git a/app/views/layouts/partials/_head.html.erb b/app/views/layouts/partials/_head.html.erb index a992334ea..e1d220cf8 100644 --- a/app/views/layouts/partials/_head.html.erb +++ b/app/views/layouts/partials/_head.html.erb @@ -1,23 +1,9 @@ -<%= page_title('Protected Planet') %> - - - - - - - - - - - - - - - - - - +<%= page_title %> + + + +<%== opengraph %> @@ -39,7 +25,7 @@ <%= stylesheet_link_tag 'pdf', media: 'all' %> <% end %> -<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> +<%= stylesheet_link_tag 'application', media: 'all' %> <%= stylesheet_pack_tag 'application' %> diff --git a/app/views/marine/index.html.erb b/app/views/marine/index.html.erb index c289ee2f8..14d10c022 100644 --- a/app/views/marine/index.html.erb +++ b/app/views/marine/index.html.erb @@ -1,9 +1,3 @@ -<%= content_for :twitter_card, "summary_large_image" %> -<%= content_for :social_title, "Explore the World's Marine Protected Areas" %> -<%= content_for :social_description, "Over 70% of the surface of Earth is ocean, comprised of highly diverse ecosystems, and providing a wide range of marine ecosystem services that support human society, health and the economy. This website presents the most recent official coverage statistics for marine protected areas, updated monthly from the World Database on Protected Areas." %> -<%= content_for :social_image, image_url('social-marine.png') %> -<%= content_for :social_image_alt, "A map of the world with highlighted areas that represent protected areas. The following statistics are shown: there are #{@marine_statistics['total_marine_protected_areas']} marine protected areas, #{@marine_statistics['total_ocean_pa_coverage_percentage']} of the ocean is covered by protected areas and the total area protected is 23,036,078km² These statistics are for 2017." %> - <%= render partial: "./partials/bars/topbar-secondary" %> <%= render partial: "./layouts/partials/hero-thematic", locals: { @@ -57,7 +51,11 @@ regionCoverage: @regionCoverage, } %> -<%= render partial: "marine/sections/map" %> +
+ <%= render partial: "partials/maps/main", locals: { + map: @map + } %> +
<%= render partial: "partials/cards/sites", locals: { diff --git a/app/views/marine/sections/_map.html.erb b/app/views/marine/sections/_map.html.erb deleted file mode 100644 index c19492dbf..000000000 --- a/app/views/marine/sections/_map.html.erb +++ /dev/null @@ -1,22 +0,0 @@ -
-

MAP

- -
-
-
-
- -
-

- The designations employed and the presentation of material on this map do not imply the expression of any opinion whatsoever on the part of the Secretariat of the United Nations concerning the legal status of any country, territory, city or area or of its authorities, or concerning the delimitation of its frontiers or boundaries. -

-
-
\ No newline at end of file diff --git a/app/views/oecm/index.html.erb b/app/views/oecm/index.html.erb index 7d3fb4e52..7dfbe5ed8 100644 --- a/app/views/oecm/index.html.erb +++ b/app/views/oecm/index.html.erb @@ -12,12 +12,11 @@ } %> -
- <%= render partial: "partials/tabs/tabs-thematic-area-database", locals: { - config_search: @config_search_areas, - tabs: @tabs - } %> -
+<%= render partial: "partials/tabs/tabs-thematic-area-database", locals: { + config_search: @config_search_areas, + map: @map, + tabs: @tabs +} %>
<%= render "partials/cards/resources" %> diff --git a/app/views/pame/index.html.erb b/app/views/pame/index.html.erb index 33178dc16..d4b8b9711 100644 --- a/app/views/pame/index.html.erb +++ b/app/views/pame/index.html.erb @@ -1,3 +1,34 @@ <%= render partial: "./layouts/partials/hero-basic", locals: { title: @cms_page.label, classes: "#{@cms_page.slug} bg-image-overlay--white" } %> -<%= render "partials/ctas/live-report" %> \ No newline at end of file +
+ + + +
+ +<%= render "partials/ctas/live-report" %> \ No newline at end of file diff --git a/app/views/partials/cards/_circles.html.erb b/app/views/partials/cards/_circles.html.erb index 5e56b01a6..5ad954694 100644 --- a/app/views/partials/cards/_circles.html.erb +++ b/app/views/partials/cards/_circles.html.erb @@ -1,6 +1,6 @@

- +
<% cards.each do |card| %> <%= link_to card[:url], title: "Explore Protected Areas by #{card[:title]}", class: 'card' do %> diff --git a/app/views/partials/ctas/_api.html.erb b/app/views/partials/ctas/_api.html.erb index 249ff8080..5e0e66655 100644 --- a/app/views/partials/ctas/_api.html.erb +++ b/app/views/partials/ctas/_api.html.erb @@ -1,13 +1,13 @@ <% if cta_api %> -
-
- <%= image_tag 'icons/key.svg', alt: 'key icon', class: 'cta__bg' %> -
-

<%= cta_api.title %>

-

<%= cta_api.summary %>

+
+
+ <%= image_tag 'icons/key.svg', alt: 'key icon', class: 'cta__bg' %> +
+

<%= cta_api.title %>

+

<%= cta_api.summary %>

- <%= link_to t('ctas.api.button'), cta_api.url, target: '_blank', class: 'button--outline-white' %> + <%= link_to t('ctas.api.button'), cta_api.url, target: '_blank', class: 'button--outline-white' %> +
-
-
+
<% end %> \ No newline at end of file diff --git a/app/views/partials/maps/_header.html.erb b/app/views/partials/maps/_header.html.erb new file mode 100644 index 000000000..815459c59 --- /dev/null +++ b/app/views/partials/maps/_header.html.erb @@ -0,0 +1,12 @@ +
+ + + +
\ No newline at end of file diff --git a/app/views/partials/maps/_main.html.erb b/app/views/partials/maps/_main.html.erb index 2e6b48242..5f633c9ce 100644 --- a/app/views/partials/maps/_main.html.erb +++ b/app/views/partials/maps/_main.html.erb @@ -1 +1,24 @@ - \ No newline at end of file +
+ + + + + + +
diff --git a/app/views/partials/messages/_message-warning-stats.html.erb b/app/views/partials/messages/_message-warning-stats.html.erb index 891c2414a..0fbec0b53 100644 --- a/app/views/partials/messages/_message-warning-stats.html.erb +++ b/app/views/partials/messages/_message-warning-stats.html.erb @@ -1,3 +1,14 @@

<%= t('stats.warning').html_safe %>

+ + <% if is_malaysia? %> + + <% end %>
\ No newline at end of file diff --git a/app/views/partials/social/_sharing.html.erb b/app/views/partials/social/_sharing.html.erb index b99f8e7b4..013eef187 100644 --- a/app/views/partials/social/_sharing.html.erb +++ b/app/views/partials/social/_sharing.html.erb @@ -1,3 +1,8 @@ -<%= create_sharing_twitter_link %> -<%= create_sharing_facebook_link %> -<%= create_sharing_linkedin_link %> \ No newline at end of file +<% %w[twitter facebook linkedin].each do |social_medium| %> + <% translate_path = format('social.share.%s', { key: social_medium }) %> + <%= link_to '', t("#{translate_path}.url", url: request.original_url), + title: t("#{translate_path}.title"), + class: "social__icon--#{social_medium}", + target: '_blank' + %> +<% end %> \ No newline at end of file diff --git a/app/views/partials/stats/_stats-attributes.html.erb b/app/views/partials/stats/_stats-attributes.html.erb index 8c745cd77..06ccbf195 100644 --- a/app/views/partials/stats/_stats-attributes.html.erb +++ b/app/views/partials/stats/_stats-attributes.html.erb @@ -1,5 +1,5 @@ -
-

<%= t('stats.attributes.title') %>

+
+

<%= t('stats.attributes.title') %>

    <% attributes.each do |attribute| %> diff --git a/app/views/partials/stats/_stats-coverage.html.erb b/app/views/partials/stats/_stats-coverage.html.erb index 9b2e4d520..aa64049ec 100644 --- a/app/views/partials/stats/_stats-coverage.html.erb +++ b/app/views/partials/stats/_stats-coverage.html.erb @@ -1,5 +1,5 @@ -
    -

    <%= t("stats.#{type}-title") %>

    +
    +

    <%= t("stats.#{type}-title") %>

    diff --git a/app/views/partials/stats/_stats-designations.html.erb b/app/views/partials/stats/_stats-designations.html.erb index ffbefbed2..6c4dcbc0c 100644 --- a/app/views/partials/stats/_stats-designations.html.erb +++ b/app/views/partials/stats/_stats-designations.html.erb @@ -1,5 +1,5 @@ -
    -

    <%= t('stats.designations.title') %>

    +
    +

    <%= t('stats.designations.title') %>

    -

    Lorem Ipsum

    +
    +

    <%= t('stats.external.title') %>

    +
      <% external_links.each do |link| %> <% end %>
    diff --git a/app/views/partials/stats/_stats-governance.html.erb b/app/views/partials/stats/_stats-governance.html.erb index 8ad6549ab..ff09755c2 100644 --- a/app/views/partials/stats/_stats-governance.html.erb +++ b/app/views/partials/stats/_stats-governance.html.erb @@ -1,5 +1,5 @@ -
    -

    <%= t('stats.governance.title') %>

    +
    +

    <%= t('stats.governance.title') %>

    Chart diff --git a/app/views/partials/stats/_stats-iucn-categories.html.erb b/app/views/partials/stats/_stats-iucn-categories.html.erb index 65a6ddbaf..b3c0d740a 100644 --- a/app/views/partials/stats/_stats-iucn-categories.html.erb +++ b/app/views/partials/stats/_stats-iucn-categories.html.erb @@ -1,5 +1,5 @@ -
    -

    <%= t('stats.iucn-categories.title') %>

    +
    +

    <%= t('stats.iucn-categories.title') %>

      <% iucn_categories.each do |category| %> diff --git a/app/views/partials/stats/_stats-overlap.html.erb b/app/views/partials/stats/_stats-overlap.html.erb new file mode 100644 index 000000000..12319b577 --- /dev/null +++ b/app/views/partials/stats/_stats-overlap.html.erb @@ -0,0 +1,3 @@ +
      +

      Chart - overlaps

      +
      \ No newline at end of file diff --git a/app/views/partials/stats/_stats-overview.html.erb b/app/views/partials/stats/_stats-overview.html.erb index b9da6d617..c58b22660 100644 --- a/app/views/partials/stats/_stats-overview.html.erb +++ b/app/views/partials/stats/_stats-overview.html.erb @@ -82,4 +82,11 @@ Points <%= total_points_percentage %>%
    <% end %> + + <% if local_assigns[:total_countries] %> +
    + <%= total_countries %> + Number of Countries +
    + <% end %>
    \ No newline at end of file diff --git a/app/views/partials/stats/_stats-pame.html.erb b/app/views/partials/stats/_stats-pame.html.erb index 6391e9b0f..872a4fd5b 100644 --- a/app/views/partials/stats/_stats-pame.html.erb +++ b/app/views/partials/stats/_stats-pame.html.erb @@ -1,5 +1,6 @@ -
    -

    <%= t('stats.pame.title') %>

    +
    +

    <%= t('stats.pame.title') %>

    + <% if pame_evaluations_summary.any? %>
      diff --git a/app/views/partials/stats/_stats-sources.html.erb b/app/views/partials/stats/_stats-sources.html.erb index 206a67c1b..f3f09195c 100644 --- a/app/views/partials/stats/_stats-sources.html.erb +++ b/app/views/partials/stats/_stats-sources.html.erb @@ -1,5 +1,5 @@ -
      -

      <%= t('stats.sources.title') %>

      +
      +

      <%= t('stats.sources.title') %>

      diff --git a/app/views/partials/svgs/_info.svg b/app/views/partials/svgs/_info.svg new file mode 100644 index 000000000..ed60cdd41 --- /dev/null +++ b/app/views/partials/svgs/_info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/views/partials/tabs/_tabs-thematic-area-database.html.erb b/app/views/partials/tabs/_tabs-thematic-area-database.html.erb index 0368187d0..e0abf92cf 100644 --- a/app/views/partials/tabs/_tabs-thematic-area-database.html.erb +++ b/app/views/partials/tabs/_tabs-thematic-area-database.html.erb @@ -4,29 +4,37 @@ > \ No newline at end of file diff --git a/app/views/protected_areas/show.html.erb b/app/views/protected_areas/show.html.erb index 7ef2dbf0b..442641f56 100644 --- a/app/views/protected_areas/show.html.erb +++ b/app/views/protected_areas/show.html.erb @@ -1,7 +1,7 @@ -
      +
      -
      +
      <%= render partial: "partials/stats/stats-overview", locals: { area: reported_area, location: @locations.html_safe, @@ -11,25 +11,24 @@ story_map_links: @protected_area.story_map_links } %> -
      - MAP -
      + <%= render partial: "partials/maps/header", locals: { + map: @map, + map_options: @map_options + } %>
      -
      +
      <%= render partial: "partials/stats/stats-attributes", locals: { attributes: @presenter.attributes } %>
      -
      +
      <%= render "partials/stats/stats-pame" %> <%= render partial: "partials/stats/stats-external", locals: { external_links: @presenter.external_links } %>
      -
      -

      Chart - overlaps

      -
      + <%= render partial: "partials/stats/stats-overlap" %> <%= render partial: "partials/stats/stats-sources", locals: { sources: @sources } %> @@ -37,71 +36,21 @@
      - - - - <%= content_for :seo, "Learn about and visualise the protected area #{@protected_area.name} (#{@protected_area.designation.try(:name)}), situated in #{@countries.map(&:name).join("; ") rescue "Areas Beyond National Jurisdiction"}" %> <%= content_for :seo_image, tiles_url(type: "protected_area", id: @protected_area.wdpa_id, version: 1) %> <%= content_for :page_title, @protected_area.name %> <% has_designations = @protected_area.networks.detect(&:designation).present? %> -
      -

      - - -

      - - <% if has_designations %> -
      -

      - This site has multiple designations. <%= link_to raw('(See all details ↓)'), '#anchor-designations', class: 'link--page-anchor' %> -

      -
      - <% end %> - - - -<%= link_to url_for(action: :show, id: @protected_area.wdpa_id, format: :pdf), class: 'link link--hero-download' do %> -Download site -<% end %> - - -
      - - - -
      -
      - -
      - The designations employed and the presentation of material on this map do not - imply the expression of any opinion whatsoever on the part of the Secretariat - of the United Nations concerning the legal status of any country, territory, - city or area or of its authorities, or concerning the delimitation of its - frontiers or boundaries. +<% if has_designations %> +
      +

      + This site has multiple designations. <%= link_to raw('(See all details ↓)'), '#anchor-designations', class: 'link--page-anchor' %> +

      - -
      - -<% if @protected_area.story_map_links.any? %> -

      Link to story map

      - <% @protected_area.story_map_links.each do |story_map_link| %> -

      <%= link_to "Story Map(#{story_map_link.link_type})", story_map_link.link, class: 'link is-external', title: 'view the Story map', target: '_blank'%>

      - <% end %> <% end %> + <% if has_designations %>

      Site multiple designations

      @@ -136,65 +85,6 @@
      <% end %> - <% if @networks.any? %> - <% unless @for_pdf %> - <%= render partial: "map_connections" %> - <% end %> - -
      -

      Site network breakdown

      - -
      - <% unless @for_pdf %> -
        - <% @networks.each_with_index do |network, i| %> -
      • "><%= network.name %>
      • - <% end %> -
      - <% end %> - - <% @networks.each_with_index do |network, i| %> -
      "> - - <% if @for_pdf %> - <%= network.name %> - <% end %> - -

      - -
      - -
      - <% network.protected_areas.each do |protected_area| %> - <% next if protected_area.id == @protected_area.id %> - <%= render partial: "pa_card", locals: { protected_area: protected_area, comparison_protected_area: @protected_area } %> - <% end %> -
      - - -
      -
      - <% end %> -
      - -
      - <% end %> - -
      -

      Downloads

      -
      -
      - - <%= link_to url_for(action: :show, id: @protected_area.wdpa_id, format: :pdf), class: 'info-box__link' do %> - Download site - <% end %> -
      -
      -
      - <% if management_plan_document %>

      Management Plan

      @@ -204,4 +94,5 @@
      <% end %> +
      diff --git a/app/views/region/show.html.erb b/app/views/region/show.html.erb index 380f8facf..68150d6d6 100644 --- a/app/views/region/show.html.erb +++ b/app/views/region/show.html.erb @@ -1,35 +1,25 @@
      -
      - <%= render partial: "partials/stats/stats-overview", locals: { - title: @region.name, - total_oecm: @total_oecm, - total_points_percentage: @presenter.total_points_percentage, - total_polygons_percentage: @presenter.total_polygons_percentage, - total_wdpa: @total_wdpa, - } %> - -

      Number of Countries

      -

      <%= spaceify(@region.countries.count) %>

      +
      - <%#= render "partials/stats/facts/number_of_pas", {location: @region} %> - <%#= render "partials/stats/facts/polygons_points_ratio" %> +
      + <%= render partial: "partials/stats/stats-overview", locals: { + title: @region.name, + total_oecm: @total_oecm, + total_points_percentage: @presenter.total_points_percentage, + total_polygons_percentage: @presenter.total_polygons_percentage, + total_wdpa: @total_wdpa, + total_countries: spaceify(@region.countries.count) + } %> -
      - <% unless @for_pdf %> -

      map

      -
      + <%#= render "partials/stats/facts/number_of_pas", {location: @region} %> + <%#= render "partials/stats/facts/polygons_points_ratio" %> -
      - The designations employed and the presentation of material on this map do not - imply the expression of any opinion whatsoever on the part of the Secretariat - of the United Nations concerning the legal status of any country, territory, - city or area or of its authorities, or concerning the delimitation of its - frontiers or boundaries. -
      - <% end %> -
      -
      + <%= render partial: "partials/maps/header", locals: { + map: @map, + map_options: @map_options + } unless @for_pdf %> +
      +
      diff --git a/app/views/resources/index.html.erb b/app/views/resources/index.html.erb index 02b4a47d3..45027e7f1 100644 --- a/app/views/resources/index.html.erb +++ b/app/views/resources/index.html.erb @@ -3,6 +3,8 @@

      filters

      + + <%= load_categories %>
      diff --git a/app/views/search_areas/index.html.erb b/app/views/search_areas/index.html.erb index 4a998d331..0446b7c07 100644 --- a/app/views/search_areas/index.html.erb +++ b/app/views/search_areas/index.html.erb @@ -16,5 +16,11 @@ text-filters="<%= t('search.filters') %>" text-map="<%= t('search.map') %>" :results="<%= @results.to_json %>" - > + > + +
      \ No newline at end of file diff --git a/app/views/target_dashboard/index.html.erb b/app/views/target_dashboard/index.html.erb index a1092f121..59d4a208a 100644 --- a/app/views/target_dashboard/index.html.erb +++ b/app/views/target_dashboard/index.html.erb @@ -1,11 +1,12 @@ -<%= render partial: "./layouts/partials/hero-small", locals: { +<%= render partial: "./layouts/partials/hero-basic", locals: { classes: 'thematical-area' , hero_image: cms_fragment_render(:theme_image, @cms_page), + summary: cms_fragment_render("summary"), title: @cms_page.label } %>
      -
      +
      @@ -17,7 +18,9 @@ text="<%= getTooltipText(stat[:id]) %>" class="carousel-cell__tooltip" > - + + <%= render '/partials/svgs/info.svg' %> + @@ -39,7 +42,7 @@
      -
      +
      " - > + > + + + <%= render '/partials/svgs/info.svg' %> + +
      diff --git a/app/views/wdpa/index.html.erb b/app/views/wdpa/index.html.erb index b636752d4..985ad6f46 100644 --- a/app/views/wdpa/index.html.erb +++ b/app/views/wdpa/index.html.erb @@ -12,12 +12,11 @@ } %> -
      - <%= render partial: "partials/tabs/tabs-thematic-area-database", locals: { - config_search: @config_search_areas, - tabs: @tabs - } %> -
      +<%= render partial: "partials/tabs/tabs-thematic-area-database", locals: { + config_search: @config_search_areas, + map: @map, + tabs: @tabs +} %>
      <%= render "partials/cards/resources" %> diff --git a/config/database.yml b/config/database.yml index 730e9f3af..f7f856f3a 100644 --- a/config/database.yml +++ b/config/database.yml @@ -14,7 +14,7 @@ default: &default development: <<: *default - database: protectedplanet-db + database: <%= ENV['POSTGRES_DBNAME'] %> # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". diff --git a/config/initializers/comfortable_mexican_sofa.rb b/config/initializers/comfortable_mexican_sofa.rb index e4739abeb..7c33b3bb7 100644 --- a/config/initializers/comfortable_mexican_sofa.rb +++ b/config/initializers/comfortable_mexican_sofa.rb @@ -2,6 +2,7 @@ require 'cms_tags/date_not_null' require 'cms_tags/text_custom' +require 'cms_tags/categories' # encoding: utf-8 ComfortableMexicanSofa.configure do |config| diff --git a/config/initializers/comfy_patching.rb b/config/initializers/comfy_patching.rb new file mode 100644 index 000000000..94c21f394 --- /dev/null +++ b/config/initializers/comfy_patching.rb @@ -0,0 +1,60 @@ +Rails.configuration.to_prepare do + Comfy::Cms::Page.class_eval do + has_many :pages_categories, foreign_key: 'page_id' + has_many :page_categories, through: :pages_categories, foreign_key: 'page_id' + + has_many :topics, -> { joins(:layout_category).where("comfy_cms_layout_categories.label = 'topics'") }, + through: :pages_categories, class_name: 'Comfy::Cms::PageCategory', source: :page_category + + has_many :page_types, -> { joins(:layout_category).where("comfy_cms_layout_categories.label = 'types'") }, + through: :pages_categories, class_name: 'Comfy::Cms::PageCategory', source: :page_category + + accepts_nested_attributes_for :pages_categories + end + + Comfy::Cms::Layout.class_eval do + has_many :layouts_categories, foreign_key: 'layout_id' + has_many :layout_categories, through: :layouts_categories, foreign_key: 'layout_id' + + accepts_nested_attributes_for :layouts_categories + + after_save :assign_layout_categories + + def assign_layout_categories + _categories = self.content_tokens.select do |t| + unless t.is_a?(Hash) + false + else + t[:tag_class] == 'categories' + end + end + + delete_orphan_categories && return if _categories.blank? + + _categories.each do |cat| + tag_name = cat[:tag_params].split(',').first + _layout_category = Comfy::Cms::LayoutCategory.find_by(label: tag_name) + Comfy::Cms::LayoutsCategory.find_or_create_by( + layout_id: self.id, + layout_category_id: _layout_category.id + ) + end + end + + def delete_orphan_categories + layouts_categories = Comfy::Cms::LayoutsCategory.where(layout_id: self.id) + + self.pages.each do |page| + layouts_categories.each do |lc| + pcs_ids = page.page_categories.where(layout_category_id: lc.id).map(&:id) + Comfy::Cms::PagesCategory.where( + page_id: page.id, + page_category_id: pcs_ids + ).destroy_all + end + end + + layout_categories.destroy_all + end + end +end \ No newline at end of file diff --git a/config/locales/global/en.yml b/config/locales/global/en.yml index 8e1477363..9fe5c871e 100644 --- a/config/locales/global/en.yml +++ b/config/locales/global/en.yml @@ -8,6 +8,8 @@ en: download-pdf: PDF Download download: Download link: Link + more: More info + pdf: PDF footer: copyright: "ProtectedPlanet 2014-%{date}. All rights reserved" title1: Explore diff --git a/config/locales/home/en.yml b/config/locales/home/en.yml index db475275d..65dd6e7ad 100644 --- a/config/locales/home/en.yml +++ b/config/locales/home/en.yml @@ -29,7 +29,7 @@ en: geo_type: country icon: country - - title: Indiviual Area + title: Individual Area geo_type: site icon: area categories: diff --git a/config/locales/main_map/en.yml b/config/locales/main_map/en.yml new file mode 100644 index 000000000..9e2f1e625 --- /dev/null +++ b/config/locales/main_map/en.yml @@ -0,0 +1,28 @@ +en: + map: + overlays: + terrestrial_wdpa: + title: Terrestrial Protected Areas + marine_wdpa: + title: Coastal and Marine Protected Areas + oecm: + title: Other effective area-based conservation measures + title: Discover Protected Areas + disclaimer: + heading: Map Disclaimer + body: The designations employed and the presentation of material on this map do not imply the expression of any opinion whatsoever on the part of the Secretariat of the United Nations concerning the legal status of any country, territory, city or area or of its authorities, or concerning the delimitation of its frontiers or boundaries. + dropdown_label: Search for a + search_types: + country: + title: Country + placeholder: Search... + region: + title: Region + placeholder: Search... + site: + title: Protected Area or OECM + placeholder: Search... + autocomplete_error_messages: + no_results: The search term has no results. + invalid_search_string: The search term has too few characters. + diff --git a/config/locales/meta/en.yml b/config/locales/meta/en.yml new file mode 100644 index 000000000..bea06796d --- /dev/null +++ b/config/locales/meta/en.yml @@ -0,0 +1,18 @@ +en: + meta: + site: + name: Protected Planet + name_with_suffix: Protected Planet | %{suffix} + title: Explore the World's Protected Areas + title_with_suffix: Explore the World's Protected Areas in %{suffix} + description: >- + Protected Planet is the online interface for the + World Database on Protected Areas (WDPA), and the most comprehensive + global database on terrestrial and marine protected areas. + image: social.png + image_alt: >- + Screenshot of the Protected Planet website which shows the menu bar + and a map of the world that has protected areas highlighted in green. + twitter: + site: "@protectedplanet" + creator: "@protectedplanet" \ No newline at end of file diff --git a/config/locales/search/en.yml b/config/locales/search/en.yml index cf0aa9fc7..52d5b44be 100644 --- a/config/locales/search/en.yml +++ b/config/locales/search/en.yml @@ -32,7 +32,7 @@ en: region: Regions country: Countries site: Individual Areas - map: Map + map: Map View protected-areas: Protected Areas results: results no-results: No results. Please search again. @@ -40,4 +40,18 @@ en: - all - news-and-stories - resources - - areas \ No newline at end of file + - areas + custom_categories: + topics: + name: 'topics' + items: + wdpa: WDPA + oecm: OECM + pame: PAME + types: + name: 'types' + items: + publications: 'Publications' + statistics: 'Statistics' + technical: 'Technical' + training: 'Training' \ No newline at end of file diff --git a/config/locales/social/share/en.yml b/config/locales/social/share/en.yml new file mode 100644 index 000000000..f6425c2a6 --- /dev/null +++ b/config/locales/social/share/en.yml @@ -0,0 +1,12 @@ +en: + social: + share: + twitter: + title: Share on Twitter + url: https://twitter.com/intent/tweet/?url=%{url} + facebook: + title: Share on Facebook + url: https://facebook.com/sharer/sharer.php?u=%{url} + linkedin: + title: Share on LinkedIn + url: https://www.linkedin.com/shareArticle?url=%{url} \ No newline at end of file diff --git a/config/locales/stats/en.yml b/config/locales/stats/en.yml index fcbecc27d..9ec2a5502 100644 --- a/config/locales/stats/en.yml +++ b/config/locales/stats/en.yml @@ -7,13 +7,22 @@ en: coverage-chart-smallprint: The graph excludes status years with no information reported (STAT_YR = 0). designations: title: Designations + external: + title: Affiliations growth: title: Growth in protected area coverage governance: title: Governance types + green-list: + intro: This site is part of the IUCN Green List Community + type: Green List Status + date: Certification Expiry Date iucn-categories: title: IUCN Management Categories location: Location + management-plan: + button: View Management Plan + button-title: Download the Management Plan for %{site} marine-protected: Marine and Coastal Area Protected marine-title: Marine Protected Areas Coverage marine-total: Marine Area Protected diff --git a/config/locales/thematic_area/marine/en.yml b/config/locales/thematic_area/marine/en.yml index 6582ec5e3..6bea5c966 100644 --- a/config/locales/thematic_area/marine/en.yml +++ b/config/locales/thematic_area/marine/en.yml @@ -14,4 +14,4 @@ en: sub_title: Top 6 countries chart_text: "%{country} and its %{overseas} overseas countries and territories have a combined area of %{km}km2" chart_stat_text: of their national waters are protected - chart_stat_text_overseas: of their overseas territories waters are protected + chart_stat_text_overseas: of their overseas territories waters are protected \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 9e38ba6fa..00d7e0fe4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -41,7 +41,7 @@ get '/search/by_point', to: 'search#by_point' end end - + # resources :projects, only: [:create, :index, :update, :destroy] get '/marine/download_designations', to: 'marine#download_designations' @@ -69,6 +69,10 @@ # routes worked on so far as part of the refresh + + post '/pame/download', to: 'pame#download' + post '/pame/list', to: 'pame#list' + get '/resources', to: 'resources#index' get '/search', to: 'search#index' diff --git a/config/search.yml b/config/search.yml index 711dae922..ce0dfea44 100644 --- a/config/search.yml +++ b/config/search.yml @@ -116,6 +116,16 @@ filters: path: 'categories' field: 'categories.id' required: true + topic: + type: 'nested' + path: 'topics' + field: 'topics.id' + required: true + page_type: + type: 'nested' + path: 'page_types' + field: 'page_types.id' + required: true ancestor: type: 'nested' path: 'ancestors' diff --git a/config/webpack/environment.js b/config/webpack/environment.js index 6ef014b01..c251afb53 100644 --- a/config/webpack/environment.js +++ b/config/webpack/environment.js @@ -1,7 +1,9 @@ const { environment } = require('@rails/webpacker') const { VueLoaderPlugin } = require('vue-loader') const vue = require('./loaders/vue') +const DotenvPlugin = require('dotenv-webpack') +environment.plugins.prepend('DotenvPlugin', new DotenvPlugin()) environment.plugins.prepend('VueLoaderPlugin', new VueLoaderPlugin()) environment.loaders.prepend('vue', vue) module.exports = environment diff --git a/db b/db index 1d1133155..7f970b02d 160000 --- a/db +++ b/db @@ -1 +1 @@ -Subproject commit 1d1133155f6000eb45b0cd75c6915dd7c74a9ef4 +Subproject commit 7f970b02d2eb785833ad9f9be2f31b7925f851ad diff --git a/docs/search.md b/docs/search.md index 16fde6a07..7af913d62 100644 --- a/docs/search.md +++ b/docs/search.md @@ -86,3 +86,29 @@ Search.search 'manbone', filters: {type: 'country', country: 123} # Search with pagination Search.search 'manbone', page: 3 ``` + + +### Troubleshooting + +* You may have to rebuild the search indices on staging/production on occasion: + +Run this in the console: + +``` + > Search::Index.delete + > reload! + > Search::Index.create + > Search::Index.create_cms_fragments +``` +This procedure is not error-proof either, as `reload!` might not work properly itself. So you may have to re-index manually: + +`Search::Index.new(Search::_INDEX).create` + +before running: + +``` + > Search::Index.delete + > Search::Index.create + > Search::Index.create_cms_fragments +``` +again. diff --git a/lib/cms_tags/categories.rb b/lib/cms_tags/categories.rb new file mode 100644 index 000000000..9af437eaf --- /dev/null +++ b/lib/cms_tags/categories.rb @@ -0,0 +1,23 @@ +class Categories < ComfortableMexicanSofa::Content::Tag::Fragment + def initialize(context:, params: [], source: nil) + super + @group_name = params.first + @categories = Comfy::Cms::LayoutCategory.find_by(label: @group_name).page_categories + end + + def form_field(object_name, view, index) + input = view.collection_check_boxes(:page, :page_category_ids, @categories, :id, :label) do |b| + view.content_tag(:div, class: "form-check form-check-inline") do + view.concat b.check_box(class: "form-check-input") + view.concat b.label(class: "form-check-label") + end + end + + yield input + end +end + +ComfortableMexicanSofa::Content::Renderer.register_tag( + :categories, Categories +) + \ No newline at end of file diff --git a/lib/data/seeds/green_list_sites.csv b/lib/data/seeds/green_list_sites.csv index 1663afec1..01cbea3b0 100644 --- a/lib/data/seeds/green_list_sites.csv +++ b/lib/data/seeds/green_list_sites.csv @@ -1,59 +1,114 @@ -site -17231 -902497 -555558374 -303317 -309970 -3215 -555526032 -345942 -555539507 -354414 -4044 -62735 -7523 -555526045 -555539517 -303038 -303320 -555547502 -20299 -168201 -555624257 -365025 -659 -17744 -12223 -10754 -147297 -330608 -15089 -388659 -819 -555526036 -555555490 -303552 -83037 -555542446 -108125 -555544085 -63136 -9739 -13966 -555555517 -555555499 -26625 -83268 -68137 -555526606 -61595 -662 -9782 -345888 -306808 -721 -4114 -303045 -6307 -555624365 -902487 +wdpaid,status,expiry_date +306808,Green Listed,19-03-2024 +108125,Green Listed,27-01-2023 +83037,relisted,31-12-2023 +388659,relisted,19-03-2024 +662,relisted,31-12-2023 +4044,relisted,31-12-2023 +147297,relisted,31-12-2023 +555555490,Green Listed,27-10-2023 +659,Green Listed,19-03-2023 +330608,Green Listed,19-03-2024 +345942,Green Listed,31-12-2023 +345888,Green Listed,31-12-2023 +555547502,Green Listed,31-12-2023 +83268,Green Listed,31-12-2023 +6307,Green Listed,31-12-2023 +555555517,Green Listed,27-10-2023 +62735,Green Listed,19-03-2024 +303320,Green Listed,27-10-2023 +256,Candidate, +555544085,Candidate, +68137,Candidate, +902497,Green Listed,31-12-2023 +555526767,Candidate, +555542446,Candidate, +17360,Candidate, +555558374,Green Listed,31-12-2023 +365025,Candidate, +17231,Green Listed,27-10-2023 +17709,Green Listed,31-12-2023 +168201,Candidate, +555624365,Candidate, +555539517,Candidate, +303317,Green Listed,27-10-2023 +819,Candidate, +20299,Candidate, +26625,Candidate, +63136,relisted,31-12-2024 +309970,relisted,31-12-2024 +354414,relisted,01-01-2024 +17744,Candidate, +555624257,Candidate, +555526032,Candidate, +555526045,Candidate, +555526036,Candidate, +555526606,Candidate, +15089,Candidate, +555599909,Candidate, +4114,Candidate, +9782,Green Listed,31-12-2023 +555555499,relisted,27-10-2023 +311888,Candidate, +303038,Candidate, +303045,Candidate, +61595,Candidate, +303072,Candidate, +10754,Green Listed,31-12-2019 +12223,Green Listed,31-12-2019 +13966,Green Listed,31-12-2019 +303552,Green Listed,31-12-2025 +902487,Green Listed,27-10-2023 +147283,Candidate, +718,Green Listed,31-12-2019 +769,Green Listed,31-12-2019 +768,Green Listed,31-12-2019 +555525592,Candidate, +143,Candidate, +7873,Candidate, +32674,Candidate, +555697546,Candidate, +7523,Candidate, +721,Candidate, +555555513,Candidate, +62990,Candidate, +10279,Candidate, +306793_A,Candidate, +306793_B,Candidate, +306844_A,Candidate, +06844_B,Candidate, +1850_A,Candidate, +1850_B,Candidate, +306777,Candidate, +306779,Candidate, +555548807,Candidate, +306780,Candidate, +767,Candidate, +555514399,Candidate, +555525849,Candidate, +555577562,Candidate, +663,Candidate, +95349,Candidate, +6271,Candidate, +106711,Candidate, +18998,Candidate, +62716,Candidate, +3981,Candidate, +15260,Candidate, +147302,Candidate, +555577562,Candidate, +36534,Candidate, +26654,Candidate, +67860,Candidate, +95996,Candidate, +3014,Candidate, +63642,Candidate, +555637437,Candidate, +64511,Candidate, +13165,Candidate, +18805,Candidate, +30697,Candidate, +313091,Candidate, +555582988,Candidate, +18807,Candidate, +915,Candidate, \ No newline at end of file diff --git a/lib/data/seeds/pame_data-2019-08-30.csv b/lib/data/seeds/pame_data-2019-08-30.csv index 33bc33ebb..62f1f4dfb 100644 --- a/lib/data/seeds/pame_data-2019-08-30.csv +++ b/lib/data/seeds/pame_data-2019-08-30.csv @@ -1,4 +1,4 @@ -evaluation_id,wdpa_id,ISO3,methodology,year,url,metadata_id,name,designation,source_data_title,source_resp_party,source_year,source_language,restricted,assessment_is_public +id,protected_area,countries,methodology,year,url,metadata_id,name,designation,pame_source_data_title,pame_source_resp_party,pame_source_year,pame_source_language,restricted,assessment_is_public 1,555622076,ARE,METT,2016,Not Reported,1,Al Aqqa,Protected Area,UAE Management Effectiveness Evaluation,Ministry of Climate Change and Environment,2018,Arabic,FALSE,FALSE 2,555622075,ARE,METT,2016,Not Reported,1,Al Bidiya,Protected Area,UAE Management Effectiveness Evaluation,Ministry of Climate Change and Environment,2018,Arabic,FALSE,FALSE 3,555622070,ARE,METT,2016,Not Reported,1,Al Ghaf of Nazwa,Protected Area,UAE Management Effectiveness Evaluation,Ministry of Climate Change and Environment,2018,Arabic,FALSE,FALSE diff --git a/lib/data/seeds/test_pame_data.csv b/lib/data/seeds/test_pame_data.csv index 7ce7290cc..a5773dddd 100644 --- a/lib/data/seeds/test_pame_data.csv +++ b/lib/data/seeds/test_pame_data.csv @@ -1,4 +1,4 @@ -evaluation_id,wdpa_id,iso3,methodology,year,url,metadata_id,name,designation,source_data_title,source_resp_party,source_year,source_language,restricted +id,protected_area,countries,methodology,year,url,metadata_id,name,designation,pame_source_data_title,pame_source_resp_party,pame_source_year,pame_source_language,restricted 64,1,ARG,Valdiviana,2001,For storage only,3,Lanín,National Park,Áreas Protegidas enfocadas a la Efectividad del Manejo,"Jorge Fabricant – Técnico Dirección de Ordenamiento Territorial, Suelos y Lucha contra la Desertificación, del Ministerio de Ambiente y Desarrollo Sustentable",2018,Spanish,FALSE 66,2,ARG,Parks profiles,2006,For storage only,3,Lanín,National Park,Áreas Protegidas enfocadas a la Efectividad del Manejo,"Jorge Fabricant – Técnico Dirección de Ordenamiento Territorial, Suelos y Lucha contra la Desertificación, del Ministerio de Ambiente y Desarrollo Sustentable",2018,Spanish,FALSE 70,3,ARG,Parks profiles,2006,For storage only,3,Los Alerces,National Park,Áreas Protegidas enfocadas a la Efectividad del Manejo,"Jorge Fabricant – Técnico Dirección de Ordenamiento Territorial, Suelos y Lucha contra la Desertificación, del Ministerio de Ambiente y Desarrollo Sustentable",2018,Spanish,FALSE diff --git a/lib/data/seeds/test_pame_data_hidden.csv b/lib/data/seeds/test_pame_data_hidden.csv index 734d7dc36..d23e0e64b 100644 --- a/lib/data/seeds/test_pame_data_hidden.csv +++ b/lib/data/seeds/test_pame_data_hidden.csv @@ -1,4 +1,4 @@ -evaluation_id,wdpa_id,iso3,methodology,year,url,metadata_id,name,designation,source_data_title,source_resp_party,source_year,source_language,restricted +id,protected_area,countries,methodology,year,url,metadata_id,name,designation,pame_source_data_title,pame_source_resp_party,pame_source_year,pame_source_language,restricted 64,,ARG,Valdiviana,2001,For storage only,3,Lanín,National Park,Áreas Protegidas enfocadas a la Efectividad del Manejo,"Jorge Fabricant – Técnico Dirección de Ordenamiento Territorial, Suelos y Lucha contra la Desertificación, del Ministerio de Ambiente y Desarrollo Sustentable",2018,Spanish,FALSE 66,2,ARG,Parks profiles,2006,For storage only,3,Lanín,National Park,Áreas Protegidas enfocadas a la Efectividad del Manejo,"Jorge Fabricant – Técnico Dirección de Ordenamiento Territorial, Suelos y Lucha contra la Desertificación, del Ministerio de Ambiente y Desarrollo Sustentable",2018,Spanish,TRUE 70,,ARG,Parks profiles,2006,For storage only,3,Los Alerces,National Park,Áreas Protegidas enfocadas a la Efectividad del Manejo,"Jorge Fabricant – Técnico Dirección de Ordenamiento Territorial, Suelos y Lucha contra la Desertificación, del Ministerio de Ambiente y Desarrollo Sustentable",2018,Spanish,TRUE diff --git a/lib/modules/autocompletion.rb b/lib/modules/autocompletion.rb index 2009eaa41..9736501d6 100644 --- a/lib/modules/autocompletion.rb +++ b/lib/modules/autocompletion.rb @@ -2,29 +2,76 @@ module Autocompletion AUTOCOMPLETION_KEY = "autocompletion".freeze IDENTIFIER_FIELDS = { 'protected_area' => :wdpa_id, - 'country' => :iso_3 + 'country' => :iso_3, + 'region' => :iso }.freeze def self.lookup(term, db_type='wdpa', search_index=Search::PA_INDEX) - filters = { filters: { is_oecm: db_type == 'oecm' } } - search = Search.search(term.downcase, filters, search_index) + search = Search.search(term.downcase, get_filters(db_type), search_index) results = search.results.objects.values.compact.flatten + # Count the number of items with the same name + # and store it in an hash + names_counts = {} + results.map do |r| + names_counts[r.name] ||= 0 + names_counts[r.name] += 1 + end + results.map do |result| - name = result.name + name = computed_name(result, names_counts) type = result.class.name.underscore identifier = result.send(identifier_field(type)) - url = type == 'country' ? "/country/#{identifier}" : "/#{identifier}" + geom_type = result.is_a?(ProtectedArea) ? result.the_geom.geometry_type.to_s : 'N/A' + url = get_type(type, identifier) + extent_url = result.respond_to?(:extent_url) ? result.extent_url : 'N/A' - { title: name, url: url } + { + id: identifier, + is_pa: result.is_a?(ProtectedArea), + extent_url: extent_url, + title: name, + url: url + } end end private + def self.get_filters(type) + case type + when 'wdpa' + { filters: { is_oecm: false } } + when 'oecm' + { filters: { is_oecm: true } } + else + {} + end + end + + def self.get_type(type, identifier) + if type == 'country' || type == 'region' + "/#{type}/#{identifier}" + else + "/#{identifier}" + end + end + def self.identifier_field(type) IDENTIFIER_FIELDS[type] || :id end + + # If more than one item with the same name has been counted + # and it's a PA, add the designation name in brackets + def self.computed_name(item, names_counts) + return item.name unless item.is_a?(ProtectedArea) + + if names_counts[item.name] > 1 + "#{item.name} (#{item.designation.name})" + else + item.name + end + end end diff --git a/lib/modules/comfy_opengraph.rb b/lib/modules/comfy_opengraph.rb new file mode 100644 index 000000000..6fc388739 --- /dev/null +++ b/lib/modules/comfy_opengraph.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +# Helper class to set Opengraph meta-tags for use with Comfy CMS @cms_page's +class ComfyOpengraph + include Rails.application.routes.url_helpers + + attr_accessor :mappings + + # Set the fragment to Opengraph key mappings for example: + # comfyOpengraph = ComfyOpengraph.new({ + # 'social-title': 'title', + # 'social-description': 'description', + # 'theme_image': 'image' + # }) + def initialize(mappings) + throw 'Mappings must be in hash format.' unless mappings.is_a?(Hash) + @mappings = mappings.deep_stringify_keys + end + + # Iterate over the fragments of a CMS page and set their Opengraph values + def parse(opengraph: nil, page: nil) + page.fragments.to_a.each do |fragment| + id = fragment.identifier.to_s + next unless @mappings.key?(id) + + value = get_fragment_value(fragment) + next if value.blank? # don't use the value if it isn't set + + # set opengraph meta-tag via a new hash + payload = {} + payload[@mappings[id]] = value + opengraph.content('og', payload) + end + end + + private + + def get_fragment_value(fragment) + if fragment.tag =~ /file/ # get path when fragment is file + URI.join(root_url, rails_blob_path(fragment.attachments.first.blob, only_path: true)) + else # expect a string by default + fragment.content&.squish + end + end +end diff --git a/lib/modules/opengraph_builder.rb b/lib/modules/opengraph_builder.rb new file mode 100644 index 000000000..6fe9dd48b --- /dev/null +++ b/lib/modules/opengraph_builder.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +# This class is meant to be used as a wrapper for +# Opengraph meta-tags within HTML markup. +# +class OpengraphBuilder + attr_accessor :data + + # The initializer accepts a hash with the starting structure for example... + # + # og = OpengraphBuilder.new( + # { og: { title: 'my title' }, twitter: { card: 'my_card' } } + # ) + def initialize(content = {}) + @data = Hash.new({}).merge(content.deep_stringify_keys) + end + + # Use og.content with a prefix and hash to store, for example... + # + # og 'twitter', card: 'content', + # title: 'Example title!', + # description: 'Example description', + # site: '@example', + # image: 'https://placehold.it/100', + # creator: '@example' %> + # + # To load this content into a layout view, you would then simply call... + # + # <%== og.content %> to get all of the content in the store, or... + # <%== og %> as it has a to_s method, or... + # + # <%== og.content('twitter') %> to get only the content for a specific prefix. + # + def content(prefix = nil, options = nil) + if options.nil? + html(prefix) + else + save_options_at_prefix(prefix ? prefix.to_s : 'og', options) + end + end + + def to_s + html + end + + private + + def html(prefix = nil) + return meta_tags(@data[prefix], prefix) if prefix + + arr = [] + @data.each { |p, data| arr << meta_tags(data, p) } + arr.join("\n") + end + + def save_options_at_prefix(prefix, options) + @data[prefix.to_s].merge!(options.deep_stringify_keys) + end + + def meta_tags(data, prefix) + arr = [] + data.each do |key, value| + arr << format( + '', + { prefix: prefix, key: key, value: value.to_s } + ) + end + arr.join("\n") + end +end diff --git a/lib/modules/search/matcher.rb b/lib/modules/search/matcher.rb index c25d07442..8d93ac56a 100644 --- a/lib/modules/search/matcher.rb +++ b/lib/modules/search/matcher.rb @@ -47,6 +47,7 @@ class Search::Matcher ) }, { type: 'nested', path: 'categories', fields: ['categories.label'] }, + { type: 'nested', path: 'topics', fields: ['topics.label'] }, { type: 'nested', path: 'ancestors', fields: ['ancestors.label'] } ] } diff --git a/lib/modules/search/templates/mappings.json b/lib/modules/search/templates/mappings.json index 582c11344..7606fad9b 100644 --- a/lib/modules/search/templates/mappings.json +++ b/lib/modules/search/templates/mappings.json @@ -232,6 +232,28 @@ } } }, + "topics": { + "type": "nested", + "properties": { + "id": { + "type": "integer" + }, + "label": { + "type": "keyword" + } + } + }, + "page_types": { + "type": "nested", + "properties": { + "id": { + "type": "integer" + }, + "label": { + "type": "keyword" + } + } + }, "fragments_for_index": { "type": "nested", "properties": { diff --git a/lib/modules/wdpa/green_list_importer.rb b/lib/modules/wdpa/green_list_importer.rb index f88d72e4a..d06a0398a 100644 --- a/lib/modules/wdpa/green_list_importer.rb +++ b/lib/modules/wdpa/green_list_importer.rb @@ -1,30 +1,43 @@ module Wdpa::GreenListImporter + # Make sure headers are: wdpaid,status,expiry_date GREEN_LIST_SITES_CSV = "#{Rails.root}/lib/data/seeds/green_list_sites.csv" extend self def import ActiveRecord::Base.transaction do - old_green_list = ProtectedArea.where(is_green_list: true) - old_green_list.update_all(is_green_list: false) - csv = CSV.read(GREEN_LIST_SITES_CSV) - csv.shift # remove headers + ProtectedArea.where.not(green_list_status_id: nil). + update_all(green_list_status_id: nil) + GreenListStatus.destroy_all - sites = csv.map { |row| row }.flatten.uniq + invalid = [] + not_found = [] + duplicates = [] - sites.each do |site| - wdpa_id = Integer(site) rescue false - if wdpa_id - pa = ProtectedArea.find_by_wdpa_id(wdpa_id) - else - pa = ProtectedArea.find_by_slug(site) + CSV.foreach(GREEN_LIST_SITES_CSV, headers: true) do |row| + wdpa_id = Integer(row['wdpaid']) rescue false + unless wdpa_id + invalid << row['wdpaid'] + next end - unless pa.blank? - pa.is_green_list = true + pa = ProtectedArea.find_by_wdpa_id(wdpa_id) + + if pa.blank? + not_found << wdpa_id + else + if pa.green_list_status_id + duplicates << wdpa_id + next + end + gls = GreenListStatus.create(row.to_h.slice('status', 'expiry_date')) + pa.green_list_status_id = gls.id pa.save - puts "Marked #{site} as a green list site" end end + + puts "Invalid WDPAIDs found: #{invalid.join(',')}" + puts "PA with WDPAID not found: #{not_found.join(',')}" + puts "Statuses rows for same WDPAID found: #{duplicates.join(',')}" end end end diff --git a/lib/modules/wdpa/pame_importer.rb b/lib/modules/wdpa/pame_importer.rb index 204f866af..e367c40c8 100644 --- a/lib/modules/wdpa/pame_importer.rb +++ b/lib/modules/wdpa/pame_importer.rb @@ -3,82 +3,9 @@ module Wdpa::PameImporter PAME_EVALUATIONS = "#{Rails.root}/lib/data/seeds/pame_data-2019-08-30.csv".freeze - def self.import(csv_file=nil) - puts "Deleting old PAME evaluations..." - PameEvaluation.delete_all - puts "Importing PAME evaluations..." - hidden_evaluations = [] - - csv_file = csv_file || PAME_EVALUATIONS - - CSV.foreach(csv_file, headers: true) do |row| - id = row[0].to_i - wdpa_id = row[1].to_i - methodology = row[3] - year = row[4].to_i - protected_area = ProtectedArea.find_by_wdpa_id(wdpa_id) || nil - metadata_id = row[6].to_i - name = row[7] - url = row[5] - restricted = row[13] == "FALSE" ? false : true - assessment_is_public = row[14] == "FALSE" ? false : true - - if assessment_is_public - url = url.blank? ? "Not currently public" : url - else - url = "Not reported" - end - - iso3s = row[2] - pame_source = PameSource.where({ - data_title: row[9], - resp_party: row[10], - year: row[11].to_i, - language: row[12] - }).first_or_create do |ps| - # if the record doesn't exist, create it... - ps.data_title = row[9] - ps.resp_party = row[10] - ps.year = row[11].to_i - ps.language = row[12] - end - - pame_evaluation = PameEvaluation.where({ - id: id, - protected_area: protected_area, - methodology: methodology, - year: year, - metadata_id: metadata_id, - url: url, - pame_source: pame_source, - restricted: restricted - }).first_or_create do |pe| - # If the record doesn't exist, create it... - pe.id = id - pe.protected_area = protected_area - pe.methodology = methodology - pe.year = year - pe.metadata_id = metadata_id - pe.url = url - pe.pame_source = pame_source - pe.restricted = restricted - pe.wdpa_id = wdpa_id - pe.name = name - pe.assessment_is_public = assessment_is_public - end - if protected_area.nil? - hidden_evaluations << wdpa_id unless restricted - iso3s.split(",").each do |iso3| - country = Country.find_by(iso_3: iso3) - if country.present? - pame_evaluation.countries << country unless pame_evaluation.countries.include? country - end - end - end - end - - puts "Import finished!" - puts "The following are hidden: #{hidden_evaluations.count}" - puts hidden_evaluations.join(",") + def self.import (csv_file=nil) + csv_file ||= PAME_EVALUATIONS + PameEvaluation.import csv_file, "UTF-8" end + end diff --git a/lib/tasks/cms_categories.rake b/lib/tasks/cms_categories.rake new file mode 100644 index 000000000..5b0e7f469 --- /dev/null +++ b/lib/tasks/cms_categories.rake @@ -0,0 +1,15 @@ +namespace :cms_categories do + desc 'Seed database tables with custom categories for the CMS' + task import: :environment do + groups = I18n.t('search')[:custom_categories] + + groups.each do |group_name, categories| + layout_category = Comfy::Cms::LayoutCategory.find_or_create_by(label: group_name.to_s) + categories_names = categories[:items].keys + + categories_names.each do |cat_name| + Comfy::Cms::PageCategory.find_or_create_by(layout_category_id: layout_category.id, label: cat_name) + end + end + end +end \ No newline at end of file diff --git a/maps/README.md b/maps/README.md deleted file mode 100644 index ecfec6772..000000000 --- a/maps/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# We moved! - -Once hosted here, the mapbox basemaps for Protected Planet can now be found in the [unepwcmc/basemaps](https://github.com/unepwcmc/basemaps) repository. diff --git a/package.json b/package.json index 2e8e33604..5be220e07 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "scripts": { - "start": "webpack-dev-server --host 0.0.0.0 --inline --content-base ." + "start": "./bin/webpack-dev-server --host 0.0.0.0 --inline --content-base .", + "lint": "eslint --ext .js,.vue" }, "dependencies": { "@amcharts/amcharts4": "^4.9.28", @@ -22,5 +23,9 @@ "vue2-touch-events": "^2.0.0", "vuex": "^3.1.0", "webpack-dev-server": "^3.8.2" + }, + "devDependencies": { + "eslint": "~5.16.0", + "eslint-plugin-vue": "^5.2.3" } } diff --git a/test/models/pame_evaluation_test.rb b/test/models/pame_evaluation_test.rb index 3f5d2444e..2dcac6549 100644 --- a/test/models/pame_evaluation_test.rb +++ b/test/models/pame_evaluation_test.rb @@ -1,7 +1,61 @@ +# coding: utf-8 require 'test_helper' class PameEvaluationTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end + + + test "basic to_csv with default where clause" do + region = FactoryGirl.create(:region, id: 987, name: 'North Manmerica') + country = FactoryGirl.create(:country, id: 123, name: 'Manboneland', region: region) + sub_location = FactoryGirl.create(:sub_location, english_name: 'Manboneland City') + + iucn_category = FactoryGirl.create(:iucn_category, id: 456, name: 'IA') + designation = FactoryGirl.create(:designation, id: 654, name: 'National') + governance = FactoryGirl.create(:governance, id: 654, name: 'Regional') + pa = FactoryGirl.create(:protected_area, + name: 'Manbone', countries: [country], sub_locations: [sub_location], + original_name: 'Manboné', iucn_category: iucn_category, + designation: designation, marine: true, wdpa_id: 555999, + governance: governance, + the_geom_latitude: 1, the_geom_longitude: 2, + has_irreplaceability_info: true, has_parcc_info: false + ) + pe = FactoryGirl.create(:pame_evaluation, + name: 'Evaluate Manbone', protected_area: pa, countries: [country]) + # evaluation with no pa and restricted + pe = FactoryGirl.create(:pame_evaluation, + name: 'Evaluate Thingamy', wdpa_id: 42, + countries: [country]) + csv_string = PameEvaluation.to_csv("{\"_json\":[{\"name\":\"methodology\",\"options\":[],\"type\":\"multiple\"},{\"name\":\"iso3\",\"options\":[],\"type\":\"multiple\"},{\"name\":\"year\",\"options\":[],\"type\":\"multiple\"}],\"controller\":\"pame\",\"action\":\"download\",\"pame\":{\"_json\":[{\"name\":\"methodology\",\"options\":[],\"type\":\"multiple\"},{\"name\":\"iso3\",\"options\":[],\"type\":\"multiple\"},{\"name\":\"year\",\"options\":[],\"type\":\"multiple\"}]}}") + assert_equal 3, csv_string.lines.count + end + + + test "restricted excluded in to_csv with default where clause" do + region = FactoryGirl.create(:region, id: 987, name: 'North Manmerica') + country = FactoryGirl.create(:country, id: 123, name: 'Manboneland', region: region) + sub_location = FactoryGirl.create(:sub_location, english_name: 'Manboneland City') + + iucn_category = FactoryGirl.create(:iucn_category, id: 456, name: 'IA') + designation = FactoryGirl.create(:designation, id: 654, name: 'National') + governance = FactoryGirl.create(:governance, id: 654, name: 'Regional') + pa = FactoryGirl.create(:protected_area, + name: 'Manbone', countries: [country], sub_locations: [sub_location], + original_name: 'Manboné', iucn_category: iucn_category, + designation: designation, marine: true, wdpa_id: 555999, + governance: governance, + the_geom_latitude: 1, the_geom_longitude: 2, + has_irreplaceability_info: true, has_parcc_info: false + ) + pe = FactoryGirl.create(:pame_evaluation, + name: 'Evaluate Manbone', protected_area: pa, countries: [country], restricted: true) + # evaluation with no pa and restricted + pe = FactoryGirl.create(:pame_evaluation, + name: 'Evaluate Thingamy', wdpa_id: 42, restricted: true, + countries: [country]) + csv_string = PameEvaluation.to_csv("{\"_json\":[{\"name\":\"methodology\",\"options\":[],\"type\":\"multiple\"},{\"name\":\"iso3\",\"options\":[],\"type\":\"multiple\"},{\"name\":\"year\",\"options\":[],\"type\":\"multiple\"}],\"controller\":\"pame\",\"action\":\"download\",\"pame\":{\"_json\":[{\"name\":\"methodology\",\"options\":[],\"type\":\"multiple\"},{\"name\":\"iso3\",\"options\":[],\"type\":\"multiple\"},{\"name\":\"year\",\"options\":[],\"type\":\"multiple\"}]}}") + assert_equal 1, csv_string.lines.count + end + + end diff --git a/test/unit/wdpa/green_list_importer_test.rb b/test/unit/wdpa/green_list_importer_test.rb index 4dfd4a3f3..8dd7ab6f1 100644 --- a/test/unit/wdpa/green_list_importer_test.rb +++ b/test/unit/wdpa/green_list_importer_test.rb @@ -1,19 +1,61 @@ require 'test_helper' class TestWdpaMarineStatsImporter < ActiveSupport::TestCase - test "#import update sites to be green list" do + def before_setup + super + wdpa_ids = [1,2,3] wdpa_ids.each do |wdpa_id| FactoryGirl.create(:protected_area, wdpa_id: wdpa_id) end - green_list_sites = wdpa_ids.slice(1,2).each_slice(1).to_a - csv_content = [['site'], green_list_sites] - CSV.stubs(:read).returns(csv_content) + #multiple_yields from mocha expects multiple arrays, + #an array for each row + csv_content = [ + [{ + 'wdpaid' => 1, + 'status' => 'Green Listed', + 'expiry_date' => Date.today + }], + [{ + 'wdpaid' => 2, + 'status' => 'Candidate', + 'expiry_date' => Date.today + }] + ] + + CSV.stubs(:foreach).with("#{Rails.root}/lib/data/seeds/green_list_sites.csv", headers: true). + multiple_yields(*csv_content) + end + test "#import updates sites to be green list" do Wdpa::GreenListImporter.import - green_list_pas = ProtectedArea.where(is_green_list: true).count + green_list_pas = ProtectedArea.where.not(green_list_status_id: nil).count assert_equal green_list_pas, 2 end + + test "#import creates correct number of GreenListStatus records" do + Wdpa::GreenListImporter.import + + assert_equal GreenListStatus.count, 2 + end + + test "#import creates GreenListStatus records with correct status" do + Wdpa::GreenListImporter.import + gls_1 = ProtectedArea.find_by(wdpa_id: 1).green_list_status + gls_2 = ProtectedArea.find_by(wdpa_id: 2).green_list_status + + assert_equal gls_1.status, 'Green Listed' + assert_equal gls_2.status, 'Candidate' + end + + test "#import creates GreenListStatus records with correct expiry date" do + Wdpa::GreenListImporter.import + gls_1 = ProtectedArea.find_by(wdpa_id: 1).green_list_status + gls_2 = ProtectedArea.find_by(wdpa_id: 2).green_list_status + + assert_equal gls_1.expiry_date, Date.today + assert_equal gls_2.expiry_date, Date.today + end end diff --git a/test/unit/wdpa/pame_importer_test.rb b/test/unit/wdpa/pame_importer_test.rb index af30879f0..4e9e1f222 100644 --- a/test/unit/wdpa/pame_importer_test.rb +++ b/test/unit/wdpa/pame_importer_test.rb @@ -10,14 +10,22 @@ class TestPameImporter < ActiveSupport::TestCase FactoryGirl.create(:protected_area, wdpa_id: wdpa_id) end + arg = FactoryGirl.create(:country, iso_3: 'ARG', name: 'Argentina') + Wdpa::PameImporter.import(PAME_EVALUATIONS) pame_evaluations = PameEvaluation.all assert_equal 9, pame_evaluations.count + # make sure we don't create any new countries and that evaluation ends up attached to existing country + assert_equal 1, Country.all.count + assert_equal arg.id, pame_evaluations.first.countries[0].id + # same for PAs don't create any more and should all be attached up + assert_equal 3, ProtectedArea.all.count + assert_equal 1, pame_evaluations.find(64).protected_area.wdpa_id + end test "#import pame evaluations with hidden evaluation" do - PAME_EVALUATIONS = "#{Rails.root}/lib/data/seeds/test_pame_data_hidden.csv".freeze # this csv tests the three cases # nil pa not restricted, with pa with restricted and nil pa restricted. @@ -31,5 +39,14 @@ class TestPameImporter < ActiveSupport::TestCase pame_evaluations = PameEvaluation.all assert_equal 9, pame_evaluations.count + # check nil pa + assert pame_evaluations.find(64).protected_area.nil? + assert !pame_evaluations.find(64).restricted + # pa and restricted + assert_equal 2, pame_evaluations.find(66).protected_area.wdpa_id + assert pame_evaluations.find(66).restricted + # nil pa and restricted + assert pame_evaluations.find(70).protected_area.nil? + assert pame_evaluations.find(70).restricted end -end \ No newline at end of file +end diff --git a/yarn.lock b/yarn.lock index cb54533ff..6f5e5babf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1038,6 +1038,11 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" +acorn-jsx@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" + integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== + acorn-node@^1.3.0: version "1.8.2" resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" @@ -1052,6 +1057,11 @@ acorn-walk@^7.0.0: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== +acorn@^6.0.2, acorn@^6.0.7: + version "6.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" + integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== + acorn@^6.2.1: version "6.4.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.0.tgz#b659d2ffbafa24baf5db1cdbb2c94a983ecd2784" @@ -1098,6 +1108,16 @@ ajv@^6.1.0, ajv@^6.10.2, ajv@^6.5.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^6.9.1: + version "6.12.3" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.3.tgz#18c5af38a111ddeb4f2697bd78d68abc1cabd706" + integrity sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + align-text@^0.1.1, align-text@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" @@ -1122,6 +1142,11 @@ ansi-colors@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== +ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + ansi-html@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" @@ -1282,6 +1307,11 @@ ast-types@^0.7.0: resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.7.8.tgz#902d2e0d60d071bdcd46dc115e1809ed11c138a9" integrity sha1-kC0uDWDQcb3NRtwRXhgJ7RHBOKk= +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + async-each@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" @@ -1814,7 +1844,7 @@ cfb@^1.1.4: crc-32 "~1.2.0" printj "~1.1.2" -chalk@2.4.2, chalk@^2.0, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: +chalk@2.4.2, chalk@^2.0, chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1834,6 +1864,11 @@ chalk@^1.1.1: strip-ansi "^3.0.0" supports-color "^2.0.0" +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + chokidar@^2.0.2, chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" @@ -1888,6 +1923,18 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= + dependencies: + restore-cursor "^2.0.0" + +cli-width@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== + cliui@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" @@ -2246,7 +2293,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -cross-spawn@6.0.5, cross-spawn@^6.0.0: +cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -2795,7 +2842,7 @@ debug@^3.0.0, debug@^3.1.1, debug@^3.2.5: dependencies: ms "^2.1.1" -debug@^4.1.0, debug@^4.1.1: +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== @@ -2961,6 +3008,13 @@ dns-txt@^2.0.2: dependencies: buffer-indexof "^1.0.0" +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + dom-serializer@0: version "0.2.2" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" @@ -3263,7 +3317,14 @@ escodegen@~1.2.0: optionalDependencies: source-map "~0.1.30" -eslint-scope@^4.0.3: +eslint-plugin-vue@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-5.2.3.tgz#3ee7597d823b5478804b2feba9863b1b74273961" + integrity sha512-mGwMqbbJf0+VvpGR5Lllq0PMxvTdrZ/ZPjmhkacrCHbubJeJOt+T6E3HUzAifa2Mxi7RSdJfC9HFpOeSYVMMIw== + dependencies: + vue-eslint-parser "^5.0.0" + +eslint-scope@^4.0.0, eslint-scope@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== @@ -3271,6 +3332,78 @@ eslint-scope@^4.0.3: esrecurse "^4.1.0" estraverse "^4.1.1" +eslint-utils@^1.3.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint@~5.16.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" + integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.9.1" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^4.0.3" + eslint-utils "^1.3.1" + eslint-visitor-keys "^1.0.0" + espree "^5.0.1" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.7.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^6.2.2" + js-yaml "^3.13.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.11" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^5.5.1" + strip-ansi "^4.0.0" + strip-json-comments "^2.0.1" + table "^5.2.3" + text-table "^0.2.0" + +espree@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-4.1.0.tgz#728d5451e0fd156c04384a7ad89ed51ff54eb25f" + integrity sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w== + dependencies: + acorn "^6.0.2" + acorn-jsx "^5.0.0" + eslint-visitor-keys "^1.0.0" + +espree@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" + integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== + dependencies: + acorn "^6.0.7" + acorn-jsx "^5.0.0" + eslint-visitor-keys "^1.0.0" + esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -3281,6 +3414,13 @@ esprima@~1.0.4: resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.0.4.tgz#9f557e08fc3b4d26ece9dd34f8fbf476b62585ad" integrity sha1-n1V+CPw7TSbs6d00+Pv0drYlha0= +esquery@^1.0.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== + dependencies: + estraverse "^5.1.0" + esrecurse@^4.1.0: version "4.2.1" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" @@ -3293,6 +3433,11 @@ estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== +estraverse@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642" + integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw== + estraverse@~1.5.0: version "1.5.1" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.5.1.tgz#867a3e8e58a9f84618afb6c2ddbcd916b7cbaf71" @@ -3470,6 +3615,15 @@ extend@~3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + extglob@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" @@ -3528,6 +3682,20 @@ figgy-pudding@^3.5.1: resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + file-loader@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-4.3.0.tgz#780f040f729b3d18019f20605f723e844b8a58af" @@ -3622,6 +3790,20 @@ fizzy-ui-utils@^2.0.7: dependencies: desandro-matches-selector "^2.0.0" +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + flatted@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" @@ -3800,6 +3982,11 @@ function-bind@^1.1.1, function-bind@~1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -3926,7 +4113,7 @@ global-prefix@^3.0.0: kind-of "^6.0.2" which "^1.3.1" -globals@^11.1.0: +globals@^11.1.0, globals@^11.7.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== @@ -4196,7 +4383,7 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= -iconv-lite@0.4, iconv-lite@0.4.24: +iconv-lite@0.4, iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -4227,6 +4414,11 @@ iferr@^0.1.5: resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + import-cwd@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" @@ -4242,7 +4434,7 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" -import-fresh@^3.1.0: +import-fresh@^3.0.0, import-fresh@^3.1.0: version "3.2.1" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== @@ -4325,6 +4517,25 @@ ini@^1.3.4, ini@^1.3.5: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== +inquirer@^6.2.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" + integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.12" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + internal-ip@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" @@ -4677,6 +4888,14 @@ js-base64@^2.1.8: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== +js-yaml@^3.13.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + js-yaml@^3.13.1: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" @@ -4715,6 +4934,11 @@ json-schema@0.2.3: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -4829,7 +5053,7 @@ levenary@^1.1.1: dependencies: leven "^3.1.0" -levn@~0.3.0: +levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= @@ -4936,7 +5160,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5, lodash@~4.17.12: +lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5, lodash@~4.17.12: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== @@ -5175,6 +5399,11 @@ mime@^2.4.4: resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + mimic-fn@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -5326,6 +5555,11 @@ multicast-dns@^6.0.1: dns-packet "^1.3.1" thunky "^1.0.2" +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= + nan@^2.12.1, nan@^2.13.2: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" @@ -5348,6 +5582,11 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -5638,6 +5877,13 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= + dependencies: + mimic-fn "^1.0.0" + opn@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" @@ -5653,7 +5899,7 @@ optimize-css-assets-webpack-plugin@^5.0.3: cssnano "^4.1.10" last-call-webpack-plugin "^3.0.0" -optionator@^0.8.1: +optionator@^0.8.1, optionator@^0.8.2: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== @@ -5698,7 +5944,7 @@ os-locale@^3.0.0, os-locale@^3.1.0: lcid "^2.0.0" mem "^4.0.0" -os-tmpdir@^1.0.0: +os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= @@ -6712,6 +6958,11 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" @@ -6984,6 +7235,11 @@ regexp.prototype.flags@^1.2.0: define-properties "^1.1.3" es-abstract "^1.17.0-next.1" +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + regexpu-core@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.6.0.tgz#2037c18b327cfce8a6fea2a4ec441f2432afb8b6" @@ -7130,6 +7386,14 @@ resolve@^1.1.7, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.3.2, resolve@^1.8.1 dependencies: path-parse "^1.0.6" +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + restructure@^0.5.3: version "0.5.4" resolved "https://registry.yarnpkg.com/restructure/-/restructure-0.5.4.tgz#f54e7dd563590fb34fd6bf55876109aeccb28de8" @@ -7183,6 +7447,13 @@ rimraf@2, rimraf@^2.5.4, rimraf@^2.6.3, rimraf@^2.7.1: dependencies: glob "^7.1.3" +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -7200,6 +7471,11 @@ rollup@^0.25.8: minimist "^1.2.0" source-map-support "^0.3.2" +run-async@^2.2.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" @@ -7212,6 +7488,13 @@ rw@1, rw@^1.3.2: resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q= +rxjs@^6.4.0: + version "6.6.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.0.tgz#af2901eedf02e3a83ffa7f886240ff9018bbec84" + integrity sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg== + dependencies: + tslib "^1.9.0" + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -7459,6 +7742,11 @@ signal-exit@^3.0.0: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= +signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + simple-swizzle@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" @@ -7466,6 +7754,15 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -7811,7 +8108,7 @@ string-width@^1.0.1, string-width@^1.0.2: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1: +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -7907,6 +8204,11 @@ strip-indent@^1.0.1: dependencies: get-stdin "^4.0.1" +strip-json-comments@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + style-loader@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.1.3.tgz#9e826e69c683c4d9bf9db924f85e9abb30d5e200" @@ -7976,6 +8278,16 @@ svgo@^1.0.0: unquote "~1.1.1" util.promisify "~1.0.0" +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" @@ -8050,6 +8362,11 @@ terser@^4.1.2, terser@^4.4.3: source-map "~0.6.1" source-map-support "~0.5.12" +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + through2@^2.0.0, through2@~2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -8058,7 +8375,7 @@ through2@^2.0.0, through2@~2.0.3: readable-stream "~2.3.6" xtend "~4.0.1" -through@2, through@^2.3.8, through@~2.3, through@~2.3.4, through@~2.3.8: +through@2, through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.4, through@~2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -8090,6 +8407,13 @@ tinyqueue@^2.0.3: resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-2.0.3.tgz#64d8492ebf39e7801d7bd34062e29b45b2035f08" integrity sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA== +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" @@ -8476,6 +8800,18 @@ vue-analytics@^5.17.2: resolved "https://registry.yarnpkg.com/vue-analytics/-/vue-analytics-5.22.1.tgz#9d6b32da56daee1b9dfb23a267b50349a03f710f" integrity sha512-HPKQMN7gfcUqS5SxoO0VxqLRRSPkG1H1FqglsHccz6BatBatNtm/Vyy8brApktZxNCfnAkrSVDpxg3/FNDeOgQ== +vue-eslint-parser@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-5.0.0.tgz#00f4e4da94ec974b821a26ff0ed0f7a78402b8a1" + integrity sha512-JlHVZwBBTNVvzmifwjpZYn0oPWH2SgWv5dojlZBsrhablDu95VFD+hriB1rQGwbD+bms6g+rAFhQHk6+NyiS6g== + dependencies: + debug "^4.1.0" + eslint-scope "^4.0.0" + eslint-visitor-keys "^1.0.0" + espree "^4.1.0" + esquery "^1.0.1" + lodash "^4.17.11" + vue-flickity@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/vue-flickity/-/vue-flickity-1.2.1.tgz#c6195a47732957a7c49a23e904a05fa4995edabf" @@ -8768,6 +9104,13 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + ws@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"