Merge branch 'master' into chore/add_coffeelint_config
This commit is contained in:
211
.scss-lint.yml
Normal file
211
.scss-lint.yml
Normal file
@@ -0,0 +1,211 @@
|
||||
linters:
|
||||
|
||||
BangFormat:
|
||||
enabled: true
|
||||
space_before_bang: true
|
||||
space_after_bang: false
|
||||
|
||||
BorderZero:
|
||||
enabled: true
|
||||
convention: zero
|
||||
|
||||
ChainedClasses:
|
||||
enabled: false
|
||||
|
||||
ColorKeyword:
|
||||
enabled: true
|
||||
|
||||
ColorVariable:
|
||||
enabled: false
|
||||
|
||||
Comment:
|
||||
enabled: false
|
||||
|
||||
DebugStatement:
|
||||
enabled: true
|
||||
|
||||
DeclarationOrder:
|
||||
enabled: true
|
||||
|
||||
DisableLinterReason:
|
||||
enabled: true
|
||||
|
||||
DuplicateProperty:
|
||||
enabled: false
|
||||
|
||||
ElsePlacement:
|
||||
enabled: true
|
||||
style: same_line
|
||||
|
||||
EmptyLineBetweenBlocks:
|
||||
enabled: true
|
||||
ignore_single_line_blocks: true
|
||||
|
||||
EmptyRule:
|
||||
enabled: true
|
||||
|
||||
ExtendDirective:
|
||||
enabled: false
|
||||
|
||||
FinalNewline:
|
||||
enabled: true
|
||||
present: true
|
||||
|
||||
HexLength:
|
||||
enabled: true
|
||||
style: short
|
||||
|
||||
HexNotation:
|
||||
enabled: true
|
||||
style: lowercase
|
||||
|
||||
HexValidation:
|
||||
enabled: true
|
||||
|
||||
IdSelector:
|
||||
enabled: true
|
||||
|
||||
ImportantRule:
|
||||
enabled: false
|
||||
|
||||
ImportPath:
|
||||
enabled: true
|
||||
leading_underscore: false
|
||||
filename_extension: false
|
||||
|
||||
Indentation:
|
||||
enabled: true
|
||||
allow_non_nested_indentation: true
|
||||
character: space
|
||||
width: 2
|
||||
|
||||
LeadingZero:
|
||||
enabled: true
|
||||
style: include_zero
|
||||
|
||||
MergeableSelector:
|
||||
enabled: false
|
||||
force_nesting: false
|
||||
|
||||
NameFormat:
|
||||
enabled: true
|
||||
convention: hyphenated_lowercase
|
||||
allow_leading_underscore: true
|
||||
|
||||
NestingDepth:
|
||||
enabled: true
|
||||
max_depth: 4
|
||||
|
||||
PlaceholderInExtend:
|
||||
enabled: true
|
||||
|
||||
PrivateNamingConvention:
|
||||
enabled: true
|
||||
prefix: _
|
||||
|
||||
PropertyCount:
|
||||
enabled: false
|
||||
|
||||
PropertySortOrder:
|
||||
enabled: false
|
||||
|
||||
PropertySpelling:
|
||||
enabled: true
|
||||
extra_properties: []
|
||||
|
||||
PropertyUnits:
|
||||
enabled: false
|
||||
|
||||
PseudoElement:
|
||||
enabled: true
|
||||
|
||||
QualifyingElement:
|
||||
enabled: true
|
||||
allow_element_with_attribute: false
|
||||
allow_element_with_class: false
|
||||
allow_element_with_id: false
|
||||
|
||||
SelectorDepth:
|
||||
enabled: true
|
||||
max_depth: 5
|
||||
|
||||
SelectorFormat:
|
||||
enabled: true
|
||||
convention: hyphenated_lowercase
|
||||
|
||||
Shorthand:
|
||||
enabled: true
|
||||
|
||||
SingleLinePerProperty:
|
||||
enabled: true
|
||||
allow_single_line_rule_sets: false
|
||||
|
||||
SingleLinePerSelector:
|
||||
enabled: true
|
||||
|
||||
SpaceAfterComma:
|
||||
enabled: true
|
||||
|
||||
SpaceAfterPropertyColon:
|
||||
enabled: true
|
||||
style: one_space
|
||||
|
||||
SpaceAfterPropertyName:
|
||||
enabled: true
|
||||
|
||||
SpaceAfterVariableColon:
|
||||
enabled: true
|
||||
style: at_least_one_space
|
||||
|
||||
SpaceAfterVariableName:
|
||||
enabled: true
|
||||
|
||||
SpaceAroundOperator:
|
||||
enabled: true
|
||||
style: one_space
|
||||
|
||||
SpaceBeforeBrace:
|
||||
enabled: true
|
||||
style: space
|
||||
allow_single_line_padding: true
|
||||
|
||||
SpaceBetweenParens:
|
||||
enabled: true
|
||||
spaces: 0
|
||||
|
||||
StringQuotes:
|
||||
enabled: true
|
||||
style: single_quotes
|
||||
|
||||
TrailingSemicolon:
|
||||
enabled: true
|
||||
|
||||
TrailingZero:
|
||||
enabled: true
|
||||
|
||||
TransitionAll:
|
||||
enabled: false
|
||||
|
||||
UnnecessaryMantissa:
|
||||
enabled: true
|
||||
|
||||
UnnecessaryParentReference:
|
||||
enabled: true
|
||||
|
||||
UrlFormat:
|
||||
enabled: false
|
||||
|
||||
UrlQuotes:
|
||||
enabled: true
|
||||
|
||||
VariableForProperty:
|
||||
enabled: false
|
||||
|
||||
VendorPrefixes:
|
||||
enabled: true
|
||||
identifier_list: base
|
||||
include: []
|
||||
exclude: []
|
||||
|
||||
ZeroUnit:
|
||||
enabled: true
|
||||
5
Gemfile
5
Gemfile
@@ -71,6 +71,9 @@ gem 'rails-assets-markdown-it', source: 'https://rails-assets.org'
|
||||
|
||||
gem 'cocoon'
|
||||
|
||||
gem 'graphql', '~> 1.6.3'
|
||||
gem 'graphiql-rails', '~> 1.4.1'
|
||||
|
||||
group :development, :test do
|
||||
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
|
||||
gem 'byebug'
|
||||
@@ -105,7 +108,9 @@ end
|
||||
|
||||
group :development do
|
||||
# Access an IRB console on exception pages or by using <%= console %> in views
|
||||
gem 'scss_lint', require: false
|
||||
gem 'web-console', '3.3.0'
|
||||
|
||||
end
|
||||
|
||||
eval_gemfile './Gemfile_custom'
|
||||
|
||||
10
Gemfile.lock
10
Gemfile.lock
@@ -176,8 +176,10 @@ GEM
|
||||
geocoder (1.4.3)
|
||||
globalid (0.3.7)
|
||||
activesupport (>= 4.1.0)
|
||||
graphiql-rails (1.4.1)
|
||||
rails
|
||||
graphql (1.6.3)
|
||||
groupdate (3.2.0)
|
||||
activesupport (>= 3)
|
||||
gyoku (1.3.1)
|
||||
builder (>= 2.1.2)
|
||||
hashie (3.5.5)
|
||||
@@ -395,6 +397,9 @@ GEM
|
||||
nokogiri (>= 1.4.0)
|
||||
nori (~> 2.4)
|
||||
wasabi (~> 3.4)
|
||||
scss_lint (0.53.0)
|
||||
rake (>= 0.9, < 13)
|
||||
sass (~> 3.4.20)
|
||||
simplecov (0.14.1)
|
||||
docile (~> 1.1.0)
|
||||
json (>= 1.8, < 3)
|
||||
@@ -504,6 +509,8 @@ DEPENDENCIES
|
||||
foundation-rails (~> 6.2.4.0)
|
||||
foundation_rails_helper (~> 2.0.0)
|
||||
fuubar
|
||||
graphiql-rails (~> 1.4.1)
|
||||
graphql (~> 1.6.3)
|
||||
groupdate (~> 3.2.0)
|
||||
i18n-tasks (~> 0.9.15)
|
||||
initialjs-rails (= 0.2.0.4)
|
||||
@@ -537,6 +544,7 @@ DEPENDENCIES
|
||||
rvm1-capistrano3
|
||||
sass-rails (~> 5.0, >= 5.0.4)
|
||||
savon
|
||||
scss_lint
|
||||
sitemap_generator (~> 5.3.1)
|
||||
social-share-button (~> 0.10)
|
||||
spring
|
||||
|
||||
@@ -47,7 +47,6 @@ RAILS_ENV=test rake db:setup
|
||||
Run the app locally:
|
||||
```
|
||||
bin/rails s
|
||||
|
||||
```
|
||||
|
||||
Prerequisites for testing: install PhantomJS >= 1.9.8
|
||||
@@ -58,7 +57,13 @@ Run the tests with:
|
||||
bin/rspec
|
||||
```
|
||||
|
||||
Run the [coffeelint](http://www.coffeelint.org/) linter for coffeescript (install with `npm install -g coffeelint`) :
|
||||
If you add SCSS code you can check it with:
|
||||
|
||||
```
|
||||
scss-lint
|
||||
```
|
||||
|
||||
If you work on Coffeescript code you can check it with [coffeelint](http://www.coffeelint.org/) (install with `npm install -g coffeelint`) :
|
||||
|
||||
```
|
||||
coffeelint .
|
||||
|
||||
@@ -58,7 +58,13 @@ Para ejecutar los tests:
|
||||
bin/rspec
|
||||
```
|
||||
|
||||
Usa el linter coffeescript [coffeelint](http://www.coffeelint.org/) (puedes instalaro con `npm install -g coffeelint`) :
|
||||
Si añades código SCSS puedes revisarlo con:
|
||||
|
||||
```
|
||||
scss-lint
|
||||
```
|
||||
|
||||
Si trabajas en código coffeescript puedes revisarlo con [coffeelint](http://www.coffeelint.org/) (instalalo con `npm install -g coffeelint`) :
|
||||
|
||||
```
|
||||
coffeelint .
|
||||
|
||||
@@ -53,53 +53,53 @@ $small-font-size: rem-calc(14);
|
||||
$line-height: rem-calc(24);
|
||||
$tiny-font-size: rem-calc(12);
|
||||
|
||||
$brand: #004A83;
|
||||
$brand: #004a83;
|
||||
$dark: darken($brand, 10%);
|
||||
|
||||
$text: #222222;
|
||||
$text: #222;
|
||||
$text-medium: #515151;
|
||||
$text-light: #BFBFBF;
|
||||
$text-light: #bfbfbf;
|
||||
|
||||
$border: #DEE0E3;
|
||||
$border: #dee0e3;
|
||||
|
||||
$link: $brand;
|
||||
$link-hover: darken($link, 20%);
|
||||
|
||||
$debates: $brand;
|
||||
|
||||
$like: #7BD2A8;
|
||||
$unlike: #EF8585;
|
||||
$like: #7bd2a8;
|
||||
$unlike: #ef8585;
|
||||
|
||||
$delete: #F04124;
|
||||
$check: #46DB91;
|
||||
$delete: #f04124;
|
||||
$check: #46db91;
|
||||
|
||||
$proposals: #FFA42D;
|
||||
$proposals: #ffa42d;
|
||||
$proposals-dark: #794500;
|
||||
|
||||
$budget: #7E328A;
|
||||
$budget-hover: #7571BF;
|
||||
$budget: #7e328a;
|
||||
$budget-hover: #7571bf;
|
||||
|
||||
$highlight: #E7F2FC;
|
||||
$light: #F5F7FA;
|
||||
$featured: #FFDC5C;
|
||||
$highlight: #e7f2fc;
|
||||
$light: #f5f7fa;
|
||||
$featured: #ffdc5c;
|
||||
|
||||
$footer-border: #BFC1C3;
|
||||
$footer-border: #bfc1c3;
|
||||
|
||||
$success-bg: #DFF0D8;
|
||||
$success-border: #D6E9C6;
|
||||
$color-success: #3C763D;
|
||||
$success-bg: #dff0d8;
|
||||
$success-border: #d6e9c6;
|
||||
$color-success: #3c763d;
|
||||
|
||||
$info-bg: #D9EDF7;
|
||||
$info-border: #BCE8F1;
|
||||
$color-info: #31708F;
|
||||
$info-bg: #d9edf7;
|
||||
$info-border: #bce8f1;
|
||||
$color-info: #31708f;
|
||||
|
||||
$warning-bg: #FCF8E3;
|
||||
$warning-border: #FAEBCC;
|
||||
$color-warning: #8A6D3B;
|
||||
$warning-bg: #fcf8e3;
|
||||
$warning-border: #faebcc;
|
||||
$color-warning: #8a6d3b;
|
||||
|
||||
$alert-bg: #F2DEDE;
|
||||
$alert-border: #EBCCD1;
|
||||
$color-alert: #A94442;
|
||||
$alert-bg: #f2dede;
|
||||
$alert-border: #ebccd1;
|
||||
$color-alert: #a94442;
|
||||
|
||||
|
||||
// 1. Global
|
||||
@@ -118,8 +118,8 @@ $foundation-palette: (
|
||||
$light-gray: #e6e6e6;
|
||||
$medium-gray: #cacaca;
|
||||
$dark-gray: #8a8a8a;
|
||||
$black: #222222;
|
||||
$white: #ffffff;
|
||||
$black: #222;
|
||||
$white: #fff;
|
||||
$body-background: $white;
|
||||
$body-font-color: $black;
|
||||
$body-font-family: 'Source Sans Pro', 'Helvetica', 'Arial', sans-serif !important;
|
||||
@@ -573,7 +573,7 @@ $tab-background: $white;
|
||||
$tab-background-active: $white;
|
||||
$tab-item-font-size: $base-font-size;
|
||||
$tab-item-background-hover: $white;
|
||||
$tab-item-padding: $line-height/2 0;
|
||||
$tab-item-padding: $line-height / 2 0;
|
||||
$tab-expand-max: 6;
|
||||
$tab-content-background: $white;
|
||||
$tab-content-border: $border;
|
||||
|
||||
@@ -13,22 +13,22 @@
|
||||
// 01. Global styles
|
||||
// -----------------
|
||||
|
||||
$admin-color: #CF3638;
|
||||
$admin-color: #cf3638;
|
||||
|
||||
body.admin {
|
||||
.admin {
|
||||
|
||||
header {
|
||||
.header {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.top-links {
|
||||
background: darken($admin-color, 15%);
|
||||
}
|
||||
|
||||
.back-web {
|
||||
padding-top: $line-height/4;
|
||||
padding-top: $line-height / 4;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.top-bar {
|
||||
background: $admin-color !important;
|
||||
@@ -42,34 +42,17 @@ body.admin {
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
|
||||
.button {
|
||||
margin-top: 0;
|
||||
|
||||
&.margin-top {
|
||||
margin-top: $line-height;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="text"], textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.fieldset {
|
||||
|
||||
select {
|
||||
height: $line-height*2;
|
||||
height: $line-height * 2;
|
||||
}
|
||||
|
||||
.input-group input[type="text"] {
|
||||
[type="text"] {
|
||||
border-radius: 0;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
@@ -84,12 +67,12 @@ body.admin {
|
||||
}
|
||||
|
||||
&.with-button {
|
||||
line-height: $line-height*2;
|
||||
line-height: $line-height * 2;
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
background: white;
|
||||
background: #fff;
|
||||
border: 1px solid $border;
|
||||
|
||||
&:hover {
|
||||
@@ -97,7 +80,8 @@ body.admin {
|
||||
}
|
||||
}
|
||||
|
||||
td.break {
|
||||
table {
|
||||
.break {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
@@ -105,14 +89,15 @@ body.admin {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
input[type="submit"] ~ a, a ~ a {
|
||||
[type="submit"] ~ a,
|
||||
a ~ a {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
margin-top: $line-height/2;
|
||||
margin-top: $line-height / 2;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
margin-left: $line-height/2;
|
||||
margin-right: $line-height/2;
|
||||
margin-left: $line-height / 2;
|
||||
margin-right: $line-height / 2;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
@@ -122,7 +107,7 @@ body.admin {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.menu.simple li.active {
|
||||
.menu.simple .active {
|
||||
border-bottom: 2px solid $admin-color;
|
||||
color: $admin-color;
|
||||
}
|
||||
@@ -132,10 +117,6 @@ body.admin {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
#proposals {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.accordion-title {
|
||||
font-size: $base-font-size;
|
||||
}
|
||||
@@ -183,7 +164,7 @@ body.admin {
|
||||
&:hover .on-hover-block {
|
||||
display: block;
|
||||
margin: 0;
|
||||
margin-top: $line-height/2;
|
||||
margin-top: $line-height / 2;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -231,20 +212,21 @@ body.admin {
|
||||
display: inline-block;
|
||||
font-size: rem-calc(24);
|
||||
line-height: $line-height;
|
||||
padding: $line-height/2 $line-height/4;
|
||||
padding: $line-height / 2 $line-height / 4;
|
||||
padding-left: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
background: white;
|
||||
background: #fff;
|
||||
margin: 0;
|
||||
outline: 0;
|
||||
|
||||
ul {
|
||||
margin-left: $line-height/1.5;
|
||||
margin-left: $line-height / 1.5;
|
||||
border-left: 1px solid $border;
|
||||
padding-left: $line-height/2;
|
||||
padding-left: $line-height / 2;
|
||||
}
|
||||
|
||||
&.section-title {
|
||||
@@ -254,8 +236,6 @@ body.admin {
|
||||
&.active a {
|
||||
background: #f3f6f7;
|
||||
border-radius: rem-calc(6);
|
||||
-moz-border-radius: rem-calc(6);
|
||||
-webkit-border-radius: rem-calc(6);
|
||||
color: $admin-color;
|
||||
font-weight: bold;
|
||||
}
|
||||
@@ -271,17 +251,14 @@ body.admin {
|
||||
&:hover {
|
||||
background: #f3f6f7;
|
||||
border-radius: rem-calc(6);
|
||||
-moz-border-radius: rem-calc(6);
|
||||
-webkit-border-radius: rem-calc(6);
|
||||
color: $admin-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.is-accordion-submenu-parent {
|
||||
|
||||
& > a::after {
|
||||
> a::after {
|
||||
border-color: $admin-color transparent transparent;
|
||||
}
|
||||
}
|
||||
@@ -291,11 +268,11 @@ body.admin {
|
||||
margin-left: $line-height;
|
||||
|
||||
li:first-child {
|
||||
padding-top: $line-height/2;
|
||||
padding-top: $line-height / 2;
|
||||
}
|
||||
|
||||
li:last-child {
|
||||
padding-bottom: $line-height/2;
|
||||
padding-bottom: $line-height / 2;
|
||||
}
|
||||
|
||||
a {
|
||||
@@ -308,12 +285,14 @@ body.admin {
|
||||
// -----------------
|
||||
|
||||
.delete {
|
||||
border-bottom: 1px dotted #CF2A0E;
|
||||
border-bottom: 1px dotted #cf2a0e;
|
||||
color: $delete;
|
||||
font-size: $small-font-size;
|
||||
|
||||
&:hover, &:active, &:focus {
|
||||
border-bottom: 1px dotted white;
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
border-bottom: 1px dotted #fff;
|
||||
color: #cf2a0e;
|
||||
}
|
||||
}
|
||||
@@ -376,8 +355,6 @@ body.admin {
|
||||
&:hover {
|
||||
max-height: rem-calc(1000);
|
||||
transition: max-height 0.9s;
|
||||
-moz-transition: max-height 0.9s;
|
||||
-webkit-transition: max-height 0.9s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,7 +362,7 @@ body.admin {
|
||||
// ---------
|
||||
|
||||
.stats {
|
||||
background: white;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.stats-numbers {
|
||||
@@ -417,6 +394,7 @@ body.admin {
|
||||
ul {
|
||||
list-style-type: none;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
font-size: rem-calc(14);
|
||||
@@ -435,16 +413,17 @@ body.admin {
|
||||
color: $delete;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.account-info, .login-as, .spending-proposal-info {
|
||||
.account-info,
|
||||
.login-as,
|
||||
.spending-proposal-info {
|
||||
background-color: #e7e7e7;
|
||||
border-radius: rem-calc(3);
|
||||
font-size: rem-calc(16);
|
||||
font-weight: normal;
|
||||
margin: $line-height;
|
||||
padding: $line-height/2;
|
||||
padding: $line-height / 2;
|
||||
|
||||
strong {
|
||||
font-size: rem-calc(18);
|
||||
@@ -455,15 +434,17 @@ body.admin {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
body.admin {
|
||||
.admin {
|
||||
|
||||
.investment-projects-list.medium-9 {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.investment-projects-summary {
|
||||
.investment-projects-summary {
|
||||
|
||||
th, td {
|
||||
th,
|
||||
td {
|
||||
text-align: center;
|
||||
|
||||
&:first-child {
|
||||
@@ -485,10 +466,10 @@ body.admin {
|
||||
background: $success-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.admin-content .select-geozone, .admin-content .select-heading {
|
||||
.admin-content .select-geozone,
|
||||
.admin-content .select-heading {
|
||||
|
||||
a {
|
||||
display: block;
|
||||
@@ -501,14 +482,14 @@ body.admin {
|
||||
}
|
||||
}
|
||||
|
||||
table.investment-projects-summary {
|
||||
.investment-projects-summary {
|
||||
|
||||
td.total-price {
|
||||
.total-price {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
body.admin {
|
||||
.admin {
|
||||
|
||||
.geozone {
|
||||
background: #ececec;
|
||||
@@ -516,8 +497,8 @@ body.admin {
|
||||
color: $text;
|
||||
display: inline-block;
|
||||
font-size: $small-font-size;
|
||||
margin-bottom: $line-height/3;
|
||||
padding: $line-height/4 $line-height/3;
|
||||
margin-bottom: $line-height / 3;
|
||||
padding: $line-height / 4 $line-height / 3;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
@@ -538,9 +519,9 @@ body.admin {
|
||||
table {
|
||||
|
||||
.callout {
|
||||
height: $line-height*2;
|
||||
line-height: $line-height*2;
|
||||
padding: 0 $line-height/2;
|
||||
height: $line-height * 2;
|
||||
line-height: $line-height * 2;
|
||||
padding: 0 $line-height / 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -551,15 +532,15 @@ table {
|
||||
// ---------------
|
||||
|
||||
.markdown-editor {
|
||||
background-color: white;
|
||||
background-color: #fff;
|
||||
|
||||
.markdown-area,
|
||||
#markdown-preview {
|
||||
.markdown-preview {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-editor #markdown-preview {
|
||||
.markdown-editor .markdown-preview {
|
||||
overflow-y: auto;
|
||||
height: 15em;
|
||||
}
|
||||
@@ -577,11 +558,11 @@ table {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.markdown-editor.fullscreen #markdown-preview {
|
||||
.markdown-editor.fullscreen .markdown-preview {
|
||||
height: 99%;
|
||||
}
|
||||
|
||||
.edit_legislation_draft_version .row {
|
||||
.edit-legislation-draft-version .row {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
@@ -614,7 +595,7 @@ table {
|
||||
|
||||
// 08. CMS
|
||||
// --------------
|
||||
.cms_page_list {
|
||||
.cms-page-list {
|
||||
|
||||
[class^="icon-"] {
|
||||
padding-right: $menu-icon-spacing;
|
||||
@@ -624,17 +605,18 @@ table {
|
||||
|
||||
.legislation-process-edit {
|
||||
|
||||
.edit_legislation_process {
|
||||
.edit-legislation-process {
|
||||
|
||||
small {
|
||||
color: $text-medium;
|
||||
}
|
||||
|
||||
input[type]:not([type="submit"]):not([type="file"]):not([type="checkbox"]):not([type="radio"]) {
|
||||
[type]:not([type="submit"]):not([type="file"]):not([type="checkbox"]):not([type="radio"]) {
|
||||
background: $white;
|
||||
}
|
||||
|
||||
.legislation-process-start, .legislation-process-end {
|
||||
.legislation-process-start,
|
||||
.legislation-process-end {
|
||||
@include breakpoint(medium) {
|
||||
line-height: 3rem;
|
||||
}
|
||||
@@ -664,7 +646,7 @@ table {
|
||||
|
||||
.legislation-questions-form {
|
||||
|
||||
input[type]:not([type="submit"]):not([type="file"]):not([type="checkbox"]):not([type="radio"]) {
|
||||
[type]:not([type="submit"]):not([type="file"]):not([type="checkbox"]):not([type="radio"]) {
|
||||
background: $white;
|
||||
margin-bottom: 0;
|
||||
|
||||
@@ -673,27 +655,16 @@ table {
|
||||
}
|
||||
}
|
||||
|
||||
input::-webkit-input-placeholder {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
input:-moz-placeholder { /* Firefox 18- */
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
input::-moz-placeholder { /* Firefox 19+ */
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
input:-ms-input-placeholder {
|
||||
input::placeholder {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.legislation-questions-answers {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.field {
|
||||
.field {
|
||||
margin-bottom: 1rem;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
@@ -720,115 +691,24 @@ table {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.legislation-draft-versions-form {
|
||||
|
||||
.legislation-process-version {
|
||||
@include breakpoint(medium) {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
input[type]:not([type="submit"]):not([type="file"]):not([type="checkbox"]):not([type="radio"]) {
|
||||
background: $white;
|
||||
}
|
||||
|
||||
.control {
|
||||
cursor: pointer;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
small {
|
||||
display: block;
|
||||
margin-top: -1rem;
|
||||
color: $text-medium;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
margin-left: 0.25rem;
|
||||
display: inline-block;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fullscreen-container {
|
||||
text-align: center;
|
||||
background: #ccdbe6;
|
||||
|
||||
.markdown-editor-header,
|
||||
.markdown-editor-buttons {
|
||||
display: none;
|
||||
}
|
||||
.fullscreen-container {
|
||||
|
||||
a {
|
||||
line-height: 8rem;
|
||||
|
||||
span {
|
||||
text-decoration: none;
|
||||
font-size: $small-font-size;
|
||||
}
|
||||
|
||||
.icon-expand {
|
||||
margin-left: 0.25rem;
|
||||
vertical-align: sub;
|
||||
text-decoration: none;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:focus,
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#legislation_draft_version_body {
|
||||
font-family: $font-family-serif;
|
||||
background: #f5f5f5;
|
||||
height: 16em;
|
||||
|
||||
&:focus {
|
||||
border: 1px solid #cacaca;
|
||||
box-shadow: inset 0 1px 2px rgba(34, 34, 34, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
#markdown-preview {
|
||||
font-family: $font-family-serif;
|
||||
border: 1px solid #cacaca;
|
||||
margin-bottom: 2rem;
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: $font-family-serif !important;
|
||||
font-size: 1rem;
|
||||
line-height: 1.625rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1rem;
|
||||
line-height: 1.625rem;
|
||||
}
|
||||
}
|
||||
|
||||
.fullscreen {
|
||||
|
||||
.markdown-area,
|
||||
#markdown-preview {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.column {
|
||||
padding: 0;
|
||||
}
|
||||
.fullscreen {
|
||||
|
||||
.fullscreen-container {
|
||||
text-align: left;
|
||||
background: $admin-color;
|
||||
padding: 0.5rem 1rem;
|
||||
margin-bottom: 0;
|
||||
|
||||
a {
|
||||
line-height: 3rem;
|
||||
@@ -865,6 +745,7 @@ table {
|
||||
float: right;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
font-size: $small-font-size;
|
||||
@@ -877,30 +758,129 @@ table {
|
||||
margin: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#legislation_draft_version_body {
|
||||
.legislation-draft-versions-form {
|
||||
|
||||
.legislation-process-version {
|
||||
@include breakpoint(medium) {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
[type]:not([type="submit"]):not([type="file"]):not([type="checkbox"]):not([type="radio"]) {
|
||||
background: $white;
|
||||
}
|
||||
|
||||
.control {
|
||||
cursor: pointer;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
small {
|
||||
display: block;
|
||||
margin-top: -1rem;
|
||||
color: $text-medium;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
margin-left: 0.25rem;
|
||||
display: inline-block;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fullscreen-container {
|
||||
text-align: center;
|
||||
background: #ccdbe6;
|
||||
|
||||
.markdown-editor-header,
|
||||
.markdown-editor-buttons {
|
||||
display: none;
|
||||
}
|
||||
|
||||
span {
|
||||
text-decoration: none;
|
||||
font-size: $small-font-size;
|
||||
}
|
||||
|
||||
.icon-expand {
|
||||
margin-left: 0.25rem;
|
||||
vertical-align: sub;
|
||||
text-decoration: none;
|
||||
line-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.legislation-draft-version-body {
|
||||
font-family: $font-family-serif;
|
||||
background: #f5f5f5;
|
||||
height: 16em;
|
||||
|
||||
&:focus {
|
||||
border: 1px solid #cacaca;
|
||||
box-shadow: inset 0 1px 2px rgba(34, 34, 34, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-preview {
|
||||
font-family: $font-family-serif;
|
||||
border: 1px solid #cacaca;
|
||||
margin-bottom: 2rem;
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: $font-family-serif !important;
|
||||
font-size: 1rem;
|
||||
line-height: 1.625rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1rem;
|
||||
line-height: 1.625rem;
|
||||
}
|
||||
}
|
||||
|
||||
.fullscreen {
|
||||
|
||||
.markdown-area,
|
||||
.markdown-preview {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.column {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.fullscreen-container {
|
||||
text-align: left;
|
||||
background: $admin-color;
|
||||
padding: 0.5rem 1rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.legislation-draft-version-body {
|
||||
border-radius: 0;
|
||||
padding: 1rem;
|
||||
border: none;
|
||||
border: 0;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
padding: 1rem 2rem;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
#markdown-preview {
|
||||
.markdown-preview {
|
||||
padding: 1rem;
|
||||
border: none;
|
||||
border: 0;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
padding: 1rem 2rem;
|
||||
@@ -908,3 +888,9 @@ table {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.legislation-draft-version-body {
|
||||
&:focus {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,59 +2,59 @@
|
||||
//
|
||||
|
||||
.annotator-editor .annotator-controls,
|
||||
.annotator-filter, .annotator-filter
|
||||
.annotator-filter-navigation button {
|
||||
background: #F3F3F3;
|
||||
.annotator-filter,
|
||||
.annotator-filter .annotator-filter-navigation button {
|
||||
background: #f3f3f3;
|
||||
background-image: none;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
border-top: 1px solid $border;
|
||||
box-shadow: none;
|
||||
padding: $line-height/2 $line-height/4;
|
||||
padding: $line-height / 2 $line-height / 4;
|
||||
}
|
||||
|
||||
.annotator-adder {
|
||||
background-image: image-url("annotator_adder.png");
|
||||
background-image: image-url('annotator_adder.png');
|
||||
margin-top: -52px;
|
||||
}
|
||||
|
||||
.annotator-widget {
|
||||
background: white;
|
||||
background: #fff;
|
||||
border: 1px solid $border;
|
||||
border-radius: 0;
|
||||
bottom: $line-height;
|
||||
box-shadow: 0 0 5px rgba(0,0,0,0.05);
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.05);
|
||||
font-family: $body-font-family;
|
||||
font-size: $base-font-size;
|
||||
line-height: $line-height;
|
||||
min-width: $line-height*13;
|
||||
min-width: $line-height * 13;
|
||||
|
||||
p {
|
||||
color: $text;
|
||||
padding: $line-height/2;
|
||||
padding: $line-height / 2;
|
||||
}
|
||||
}
|
||||
|
||||
.annotator-item,
|
||||
.annotator-editor .annotator-item input:focus,
|
||||
.annotator-editor .annotator-item textarea:focus {
|
||||
background: white;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.annotator-widget:after,
|
||||
.annotator-editor.annotator-invert-y .annotator-widget:after {
|
||||
background-image: image-url("annotator_items.png");
|
||||
.annotator-widget::after,
|
||||
.annotator-editor.annotator-invert-y .annotator-widget::after {
|
||||
background-image: image-url('annotator_items.png');
|
||||
}
|
||||
|
||||
.annotator-editor a,
|
||||
.annotator-filter .annotator-filter-property label {
|
||||
padding: 0 $line-height/4;
|
||||
padding: 0 $line-height / 4;
|
||||
}
|
||||
|
||||
.annotator-editor a {
|
||||
background: none;
|
||||
background-image: none;
|
||||
border: none;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
color: $link;
|
||||
font-family: $body-font-family;
|
||||
@@ -63,7 +63,9 @@
|
||||
text-shadow: none;
|
||||
padding: 0;
|
||||
|
||||
&:hover, &:active, &:focus {
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
background: none;
|
||||
background-image: none;
|
||||
color: $link-hover;
|
||||
@@ -71,28 +73,28 @@
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
&:after {
|
||||
&::after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
&.annotator-cancel {
|
||||
background: #F04124;
|
||||
color: white;
|
||||
padding: $line-height/4 $line-height/2;
|
||||
background: #f04124;
|
||||
color: #fff;
|
||||
padding: $line-height / 4 $line-height / 2;
|
||||
|
||||
&:hover {
|
||||
background: darken(#F04124, 20);
|
||||
background: darken(#f04124, 20);
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.annotator-save {
|
||||
background: #43AC6A;
|
||||
color: white;
|
||||
padding: $line-height/4 $line-height/2;
|
||||
background: #43ac6a;
|
||||
color: #fff;
|
||||
padding: $line-height / 4 $line-height / 2;
|
||||
|
||||
&:hover {
|
||||
background: darken(#43AC6A, 20);
|
||||
background: darken(#43ac6a, 20);
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
@@ -104,19 +106,23 @@
|
||||
}
|
||||
|
||||
.annotator-hl.weight-1 {
|
||||
background: #FFF9DA;
|
||||
background: #fff9da;
|
||||
}
|
||||
|
||||
.annotator-hl.weight-2 {
|
||||
background: #FFF5BC;
|
||||
background: #fff5bc;
|
||||
}
|
||||
|
||||
.annotator-hl.weight-3 {
|
||||
background: #FFF1A2;
|
||||
background: #fff1a2;
|
||||
}
|
||||
|
||||
.annotator-hl.weight-4 {
|
||||
background: #FFEF8C;
|
||||
background: #ffef8c;
|
||||
}
|
||||
|
||||
.annotator-hl.weight-5 {
|
||||
background: #FFE95F;
|
||||
background: #ffe95f;
|
||||
}
|
||||
|
||||
.current-annotation {
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
@import "social-share-button";
|
||||
@import "foundation_and_overrides";
|
||||
@import "fonts";
|
||||
@import "icons";
|
||||
@import "mixins";
|
||||
@import "admin";
|
||||
@import "layout";
|
||||
@import "participation";
|
||||
@import "pages";
|
||||
@import "legislation";
|
||||
@import "legislation_process";
|
||||
@import "custom";
|
||||
@import "c3";
|
||||
@import "annotator.min";
|
||||
@import "annotator_overrides";
|
||||
@import "jquery-ui/datepicker";
|
||||
@import "datepicker_overrides";
|
||||
@import 'social-share-button';
|
||||
@import 'foundation_and_overrides';
|
||||
@import 'fonts';
|
||||
@import 'icons';
|
||||
@import 'mixins';
|
||||
@import 'admin';
|
||||
@import 'layout';
|
||||
@import 'participation';
|
||||
@import 'pages';
|
||||
@import 'legislation';
|
||||
@import 'legislation_process';
|
||||
@import 'custom';
|
||||
@import 'c3';
|
||||
@import 'annotator.min';
|
||||
@import 'annotator_overrides';
|
||||
@import 'jquery-ui/datepicker';
|
||||
@import 'datepicker_overrides';
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
.ui-datepicker-header {
|
||||
background: $brand;
|
||||
color: white;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@@ -14,9 +14,17 @@
|
||||
color: $text;
|
||||
}
|
||||
|
||||
.ui-state-hover, .ui-state-active {
|
||||
.ui-state-hover,
|
||||
.ui-state-active {
|
||||
background: $brand;
|
||||
color: white;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
thead {
|
||||
|
||||
tr th {
|
||||
color: $dark;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +40,9 @@
|
||||
right: 12px;
|
||||
}
|
||||
|
||||
.ui-datepicker-prev, .ui-datepicker-next {
|
||||
color: white;
|
||||
.ui-datepicker-prev,
|
||||
.ui-datepicker-next {
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
font-weight: normal;
|
||||
font-size: $small-font-size;
|
||||
@@ -44,22 +53,13 @@
|
||||
table {
|
||||
border: 1px solid $border;
|
||||
border-top: 0;
|
||||
|
||||
thead {
|
||||
background: $dark;
|
||||
border-left: 1px solid $dark;
|
||||
border-right: 1px solid $dark;
|
||||
|
||||
tr th {
|
||||
color: $dark;
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
border-bottom: 1px solid $border;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0px;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
&:nth-child(odd) {
|
||||
@@ -72,23 +72,23 @@
|
||||
border-right: 1px solid $border;
|
||||
|
||||
&:last-child {
|
||||
border-right: 0px;
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
span, a {
|
||||
span,
|
||||
a {
|
||||
text-align: center;
|
||||
line-height: $line-height;
|
||||
color: $text;
|
||||
}
|
||||
|
||||
&.ui-datepicker-unselectable.ui-state-disabled {
|
||||
background: white;
|
||||
|
||||
.ui-state-default {
|
||||
background: #F8F8F8;
|
||||
color: $text-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-datepicker-unselectable.ui-state-disabled {
|
||||
background: #fff;
|
||||
|
||||
.ui-state-default {
|
||||
background: #f8f8f8;
|
||||
color: $text-medium;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
font-url('sourcesanspro-light-webfont.woff2') format('woff2'),
|
||||
font-url('sourcesanspro-light-webfont.woff') format('woff'),
|
||||
font-url('sourcesanspro-light-webfont.ttf') format('truetype'),
|
||||
font-url('sourcesanspro-light-webfont.svg#source_sans_prolight') format('svg')
|
||||
font-url('sourcesanspro-light-webfont.svg#source_sans_prolight') format('svg');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -67,7 +67,7 @@
|
||||
font-url('lato-light.ttf') format('truetype'),
|
||||
font-url('lato-light.svg#latolight') format('svg');
|
||||
font-weight: lighter;
|
||||
font-style: normal
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -79,7 +79,7 @@
|
||||
font-url('lato-regular.ttf') format('truetype'),
|
||||
font-url('lato-regular.svg#latoregular') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -91,5 +91,5 @@
|
||||
font-url('lato-bold.ttf') format('truetype'),
|
||||
font-url('lato-bold.svg#latobold') format('svg');
|
||||
font-weight: bold;
|
||||
font-style: normal
|
||||
font-style: normal;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
@font-face {
|
||||
font-family: 'icons';
|
||||
src: font-url('icons.eot');
|
||||
@@ -9,7 +10,8 @@
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
[data-icon]:before {
|
||||
|
||||
[data-icon]::before {
|
||||
font-family: "icons" !important;
|
||||
content: attr(data-icon);
|
||||
font-style: normal !important;
|
||||
@@ -21,8 +23,9 @@
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
[class^="icon-"]:before,
|
||||
[class*=" icon-"]:before {
|
||||
|
||||
[class^="icon-"]::before,
|
||||
[class*=" icon-"]::before {
|
||||
font-family: "icons" !important;
|
||||
font-style: normal !important;
|
||||
font-weight: normal !important;
|
||||
@@ -33,168 +36,223 @@
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
.icon-angle-down:before {
|
||||
content: "\61";
|
||||
|
||||
.icon-angle-down::before {
|
||||
content: '\61';
|
||||
}
|
||||
.icon-angle-left:before {
|
||||
content: "\62";
|
||||
|
||||
.icon-angle-left::before {
|
||||
content: '\62';
|
||||
}
|
||||
.icon-angle-right:before {
|
||||
content: "\63";
|
||||
|
||||
.icon-angle-right::before {
|
||||
content: '\63';
|
||||
}
|
||||
.icon-angle-up:before {
|
||||
content: "\64";
|
||||
|
||||
.icon-angle-up::before {
|
||||
content: '\64';
|
||||
}
|
||||
.icon-comments:before {
|
||||
content: "\65";
|
||||
|
||||
.icon-comments::before {
|
||||
content: '\65';
|
||||
}
|
||||
.icon-twitter:before {
|
||||
content: "\66";
|
||||
|
||||
.icon-twitter::before {
|
||||
content: '\66';
|
||||
}
|
||||
.icon-calendar:before {
|
||||
content: "\67";
|
||||
|
||||
.icon-calendar::before {
|
||||
content: '\67';
|
||||
}
|
||||
.icon-debates:before {
|
||||
content: "\69";
|
||||
|
||||
.icon-debates::before {
|
||||
content: '\69';
|
||||
}
|
||||
.icon-unlike:before {
|
||||
content: "\6a";
|
||||
|
||||
.icon-unlike::before {
|
||||
content: '\6a';
|
||||
}
|
||||
.icon-like:before {
|
||||
content: "\6b";
|
||||
|
||||
.icon-like::before {
|
||||
content: '\6b';
|
||||
}
|
||||
.icon-check:before {
|
||||
content: "\6c";
|
||||
|
||||
.icon-check::before {
|
||||
content: '\6c';
|
||||
}
|
||||
.icon-edit:before {
|
||||
content: "\6d";
|
||||
|
||||
.icon-edit::before {
|
||||
content: '\6d';
|
||||
}
|
||||
.icon-user:before {
|
||||
content: "\6f";
|
||||
|
||||
.icon-user::before {
|
||||
content: '\6f';
|
||||
}
|
||||
.icon-settings:before {
|
||||
content: "\71";
|
||||
|
||||
.icon-settings::before {
|
||||
content: '\71';
|
||||
}
|
||||
.icon-stats:before {
|
||||
content: "\72";
|
||||
|
||||
.icon-stats::before {
|
||||
content: '\72';
|
||||
}
|
||||
.icon-proposals:before {
|
||||
content: "\68";
|
||||
|
||||
.icon-proposals::before {
|
||||
content: '\68';
|
||||
}
|
||||
.icon-organizations:before {
|
||||
content: "\73";
|
||||
|
||||
.icon-organizations::before {
|
||||
content: '\73';
|
||||
}
|
||||
.icon-deleted:before {
|
||||
content: "\74";
|
||||
|
||||
.icon-deleted::before {
|
||||
content: '\74';
|
||||
}
|
||||
.icon-tag:before {
|
||||
content: "\75";
|
||||
|
||||
.icon-tag::before {
|
||||
content: '\75';
|
||||
}
|
||||
.icon-eye:before {
|
||||
content: "\70";
|
||||
|
||||
.icon-eye::before {
|
||||
content: '\70';
|
||||
}
|
||||
.icon-x:before {
|
||||
content: "\76";
|
||||
|
||||
.icon-x::before {
|
||||
content: '\76';
|
||||
}
|
||||
.icon-flag:before {
|
||||
content: "\77";
|
||||
|
||||
.icon-flag::before {
|
||||
content: '\77';
|
||||
}
|
||||
.icon-comment:before {
|
||||
content: "\79";
|
||||
|
||||
.icon-comment::before {
|
||||
content: '\79';
|
||||
}
|
||||
.icon-reply:before {
|
||||
content: "\7a";
|
||||
|
||||
.icon-reply::before {
|
||||
content: '\7a';
|
||||
}
|
||||
.icon-facebook:before {
|
||||
content: "\41";
|
||||
|
||||
.icon-facebook::before {
|
||||
content: '\41';
|
||||
}
|
||||
.icon-google-plus:before {
|
||||
content: "\42";
|
||||
|
||||
.icon-google-plus::before {
|
||||
content: '\42';
|
||||
}
|
||||
.icon-search:before {
|
||||
content: "\45";
|
||||
|
||||
.icon-search::before {
|
||||
content: '\45';
|
||||
}
|
||||
.icon-external:before {
|
||||
content: "\46";
|
||||
|
||||
.icon-external::before {
|
||||
content: '\46';
|
||||
}
|
||||
.icon-video:before {
|
||||
content: "\44";
|
||||
|
||||
.icon-video::before {
|
||||
content: '\44';
|
||||
}
|
||||
.icon-document:before {
|
||||
content: "\47";
|
||||
|
||||
.icon-document::before {
|
||||
content: '\47';
|
||||
}
|
||||
.icon-print:before {
|
||||
content: "\48";
|
||||
|
||||
.icon-print::before {
|
||||
content: '\48';
|
||||
}
|
||||
.icon-blog:before {
|
||||
content: "\4a";
|
||||
|
||||
.icon-blog::before {
|
||||
content: '\4a';
|
||||
}
|
||||
.icon-box:before {
|
||||
content: "\49";
|
||||
|
||||
.icon-box::before {
|
||||
content: '\49';
|
||||
}
|
||||
.icon-youtube:before {
|
||||
content: "\4b";
|
||||
|
||||
.icon-youtube::before {
|
||||
content: '\4b';
|
||||
}
|
||||
.icon-letter:before {
|
||||
content: "\4c";
|
||||
|
||||
.icon-letter::before {
|
||||
content: '\4c';
|
||||
}
|
||||
.icon-circle:before {
|
||||
content: "\43";
|
||||
|
||||
.icon-circle::before {
|
||||
content: '\43';
|
||||
}
|
||||
.icon-circle-o:before {
|
||||
content: "\4d";
|
||||
|
||||
.icon-circle-o::before {
|
||||
content: '\4d';
|
||||
}
|
||||
.icon-help:before {
|
||||
content: "\4e";
|
||||
|
||||
.icon-help::before {
|
||||
content: '\4e';
|
||||
}
|
||||
.icon-budget:before {
|
||||
content: "\53";
|
||||
|
||||
.icon-budget::before {
|
||||
content: '\53';
|
||||
}
|
||||
.icon-notification:before {
|
||||
content: "\6e";
|
||||
|
||||
.icon-notification::before {
|
||||
content: '\6e';
|
||||
}
|
||||
.icon-no-notification:before {
|
||||
content: "\78";
|
||||
|
||||
.icon-no-notification::before {
|
||||
content: '\78';
|
||||
}
|
||||
.icon-whatsapp:before {
|
||||
content: "\50";
|
||||
|
||||
.icon-whatsapp::before {
|
||||
content: '\50';
|
||||
}
|
||||
.icon-zip:before {
|
||||
content: "\4f";
|
||||
|
||||
.icon-zip::before {
|
||||
content: '\4f';
|
||||
}
|
||||
.icon-banner:before {
|
||||
content: "\51";
|
||||
|
||||
.icon-banner::before {
|
||||
content: '\51';
|
||||
}
|
||||
.icon-arrow-down:before {
|
||||
content: "\52";
|
||||
|
||||
.icon-arrow-down::before {
|
||||
content: '\52';
|
||||
}
|
||||
.icon-arrow-left:before {
|
||||
content: "\54";
|
||||
|
||||
.icon-arrow-left::before {
|
||||
content: '\54';
|
||||
}
|
||||
.icon-arrow-right:before {
|
||||
content: "\55";
|
||||
|
||||
.icon-arrow-right::before {
|
||||
content: '\55';
|
||||
}
|
||||
.icon-check-circle:before {
|
||||
content: "\56";
|
||||
|
||||
.icon-check-circle::before {
|
||||
content: '\56';
|
||||
}
|
||||
.icon-arrow-top:before {
|
||||
content: "\57";
|
||||
|
||||
.icon-arrow-top::before {
|
||||
content: '\57';
|
||||
}
|
||||
.icon-checkmark-circle:before {
|
||||
content: "\59";
|
||||
|
||||
.icon-checkmark-circle::before {
|
||||
content: '\59';
|
||||
}
|
||||
.icon-minus-square:before {
|
||||
content: "\58";
|
||||
|
||||
.icon-minus-square::before {
|
||||
content: '\58';
|
||||
}
|
||||
.icon-plus-square:before {
|
||||
content: "\5a";
|
||||
|
||||
.icon-plus-square::before {
|
||||
content: '\5a';
|
||||
}
|
||||
.icon-expand:before {
|
||||
content: "\30";
|
||||
|
||||
.icon-expand::before {
|
||||
content: '\30';
|
||||
}
|
||||
.icon-telegram:before {
|
||||
content: "\31";
|
||||
|
||||
.icon-telegram::before {
|
||||
content: '\31';
|
||||
}
|
||||
.icon-instagram:before {
|
||||
content: "\32";
|
||||
|
||||
.icon-instagram::before {
|
||||
content: '\32';
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
// 01. Global styles
|
||||
// -----------------
|
||||
|
||||
*, *:before, *:after {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
@@ -23,96 +25,344 @@
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.column, .columns {
|
||||
.column,
|
||||
.columns {
|
||||
display: inline-block !important;
|
||||
float: none !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
.small-1, .row .small-1 { width: 7.33333%; }
|
||||
.small-2, .row .small-2 { width: 15.66667%; }
|
||||
.small-3, .row .small-3 { width: 24%; }
|
||||
.small-4, .row .small-4 { width: 32.33333%; }
|
||||
.small-5, .row .small-5 { width: 40.66667%; }
|
||||
.small-6, .row .small-6 { width: 49%; }
|
||||
.small-7, .row .small-7 { width: 57.33333%; }
|
||||
.small-8, .row .small-8 { width: 65.66667%; }
|
||||
.small-9, .row .small-9 { width: 74%; }
|
||||
.small-10, .row .small-10 { width: 82.33333%; }
|
||||
.small-11, .row .small-11 { width: 90.66667%; }
|
||||
.small-12, .row .small-12 { width: 99%; }
|
||||
.small-1,
|
||||
.row .small-1 {
|
||||
width: 7.33333%;
|
||||
}
|
||||
|
||||
.medium-1, .row .medium-1 { width: 7.33333%; }
|
||||
.medium-2, .row .medium-2 { width: 15.66667%; }
|
||||
.medium-3, .row .medium-3 { width: 24%; }
|
||||
.medium-4, .row .medium-4 { width: 32.33333%; }
|
||||
.medium-5, .row .medium-5 { width: 40.66667%; }
|
||||
.medium-6, .row .medium-6 { width: 49%; }
|
||||
.medium-7, .row .medium-7 { width: 57.33333%; }
|
||||
.medium-8, .row .medium-8 { width: 65.66667%; }
|
||||
.medium-9, .row .medium-9 { width: 74%; }
|
||||
.medium-10, .row .medium-10 { width: 82.33333%; }
|
||||
.medium-11, .row .medium-11 { width: 90.66667%; }
|
||||
.medium-12, .row .medium-12 { width: 99%; }
|
||||
.small-2,
|
||||
.row .small-2 {
|
||||
width: 15.66667%;
|
||||
}
|
||||
|
||||
.large-1, .row .large-1 { width: 7.33333%; }
|
||||
.large-2, .row .large-2 { width: 15.66667%; }
|
||||
.large-3, .row .large-3 { width: 24%; }
|
||||
.large-4, .row .large-4 { width: 32.33333%; }
|
||||
.large-5, .row .large-5 { width: 40.66667%; }
|
||||
.large-6, .row .large-6 { width: 49%; }
|
||||
.large-7, .row .large-7 { width: 57.33333%; }
|
||||
.large-8, .row .large-8 { width: 65.66667%; }
|
||||
.large-9, .row .large-9 { width: 74%; }
|
||||
.large-10, .row .large-10 { width: 82.33333%; }
|
||||
.large-11, .row .large-11 { width: 90.66667%; }
|
||||
.large-12, .row .large-12 { width: 99%; }
|
||||
.small-3,
|
||||
.row .small-3 {
|
||||
width: 24%;
|
||||
}
|
||||
|
||||
.row .small-offset-1 { margin-left: 7.33333%; }
|
||||
.row .small-offset-2 { margin-left: 15.66667%; }
|
||||
.row .small-offset-3 { margin-left: 24%; }
|
||||
.row .small-offset-4 { margin-left: 32.33333%; }
|
||||
.row .small-offset-5 { margin-left: 40.66667%; }
|
||||
.row .small-offset-6 { margin-left: 49%; }
|
||||
.row .small-offset-7 { margin-left: 57.33333%; }
|
||||
.row .small-offset-8 { margin-left: 65.66667%; }
|
||||
.row .small-offset-9 { margin-left: 74%; }
|
||||
.row .small-offset-10 { margin-left: 82.33333%; }
|
||||
.row .small-offset-11 { margin-left: 90.66667%; }
|
||||
.row .small-offset-12 { margin-left: 99%; }
|
||||
.small-4,
|
||||
.row .small-4 {
|
||||
width: 32.33333%;
|
||||
}
|
||||
|
||||
.row .medium-offset-1 { margin-left: 7.33333%; }
|
||||
.row .medium-offset-2 { margin-left: 15.66667%; }
|
||||
.row .medium-offset-3 { margin-left: 24%; }
|
||||
.row .medium-offset-4 { margin-left: 32.33333%; }
|
||||
.row .medium-offset-5 { margin-left: 40.66667%; }
|
||||
.row .medium-offset-6 { margin-left: 49%; }
|
||||
.row .medium-offset-7 { margin-left: 57.33333%; }
|
||||
.row .medium-offset-8 { margin-left: 65.66667%; }
|
||||
.row .medium-offset-9 { margin-left: 74%; }
|
||||
.row .medium-offset-10 { margin-left: 82.33333%; }
|
||||
.row .medium-offset-11 { margin-left: 90.66667%; }
|
||||
.row .medium-offset-12 { margin-left: 99%; }
|
||||
.small-5,
|
||||
.row .small-5 {
|
||||
width: 40.66667%;
|
||||
}
|
||||
|
||||
.row .large-offset-1 { margin-left: 7.33333%; }
|
||||
.row .large-offset-2 { margin-left: 15.66667%; }
|
||||
.row .large-offset-3 { margin-left: 24%; }
|
||||
.row .large-offset-4 { margin-left: 32.33333%; }
|
||||
.row .large-offset-5 { margin-left: 40.66667%; }
|
||||
.row .large-offset-6 { margin-left: 49%; }
|
||||
.row .large-offset-7 { margin-left: 57.33333%; }
|
||||
.row .large-offset-8 { margin-left: 65.66667%; }
|
||||
.row .large-offset-9 { margin-left: 74%; }
|
||||
.row .large-offset-10 { margin-left: 82.33333%; }
|
||||
.row .large-offset-11 { margin-left: 90.66667%; }
|
||||
.row .large-offset-12 { margin-left: 99%; }
|
||||
.small-6,
|
||||
.row .small-6 {
|
||||
width: 49%;
|
||||
}
|
||||
|
||||
.small-7,
|
||||
.row .small-7 {
|
||||
width: 57.33333%;
|
||||
}
|
||||
|
||||
.small-8,
|
||||
.row .small-8 {
|
||||
width: 65.66667%;
|
||||
}
|
||||
|
||||
.small-9,
|
||||
.row .small-9 {
|
||||
width: 74%;
|
||||
}
|
||||
|
||||
.small-10,
|
||||
.row .small-10 {
|
||||
width: 82.33333%;
|
||||
}
|
||||
|
||||
.small-11,
|
||||
.row .small-11 {
|
||||
width: 90.66667%;
|
||||
}
|
||||
|
||||
.small-12,
|
||||
.row .small-12 {
|
||||
width: 99%;
|
||||
}
|
||||
|
||||
.medium-1,
|
||||
.row .medium-1 {
|
||||
width: 7.33333%;
|
||||
}
|
||||
|
||||
.medium-2,
|
||||
.row .medium-2 {
|
||||
width: 15.66667%;
|
||||
}
|
||||
|
||||
.medium-3,
|
||||
.row .medium-3 {
|
||||
width: 24%;
|
||||
}
|
||||
|
||||
.medium-4,
|
||||
.row .medium-4 {
|
||||
width: 32.33333%;
|
||||
}
|
||||
|
||||
.medium-5,
|
||||
.row .medium-5 {
|
||||
width: 40.66667%;
|
||||
}
|
||||
|
||||
.medium-6,
|
||||
.row .medium-6 {
|
||||
width: 49%;
|
||||
}
|
||||
|
||||
.medium-7,
|
||||
.row .medium-7 {
|
||||
width: 57.33333%;
|
||||
}
|
||||
|
||||
.medium-8,
|
||||
.row .medium-8 {
|
||||
width: 65.66667%;
|
||||
}
|
||||
|
||||
.medium-9,
|
||||
.row .medium-9 {
|
||||
width: 74%;
|
||||
}
|
||||
|
||||
.medium-10,
|
||||
.row .medium-10 {
|
||||
width: 82.33333%;
|
||||
}
|
||||
|
||||
.medium-11,
|
||||
.row .medium-11 {
|
||||
width: 90.66667%;
|
||||
}
|
||||
|
||||
.medium-12,
|
||||
.row .medium-12 {
|
||||
width: 99%;
|
||||
}
|
||||
|
||||
.large-1,
|
||||
.row .large-1 {
|
||||
width: 7.33333%;
|
||||
}
|
||||
|
||||
.large-2,
|
||||
.row .large-2 {
|
||||
width: 15.66667%;
|
||||
}
|
||||
|
||||
.large-3,
|
||||
.row .large-3 {
|
||||
width: 24%;
|
||||
}
|
||||
|
||||
.large-4,
|
||||
.row .large-4 {
|
||||
width: 32.33333%;
|
||||
}
|
||||
|
||||
.large-5,
|
||||
.row .large-5 {
|
||||
width: 40.66667%;
|
||||
}
|
||||
|
||||
.large-6,
|
||||
.row .large-6 {
|
||||
width: 49%;
|
||||
}
|
||||
|
||||
.large-7,
|
||||
.row .large-7 {
|
||||
width: 57.33333%;
|
||||
}
|
||||
|
||||
.large-8,
|
||||
.row .large-8 {
|
||||
width: 65.66667%;
|
||||
}
|
||||
|
||||
.large-9,
|
||||
.row .large-9 {
|
||||
width: 74%;
|
||||
}
|
||||
|
||||
.large-10,
|
||||
.row .large-10 {
|
||||
width: 82.33333%;
|
||||
}
|
||||
|
||||
.large-11,
|
||||
.row .large-11 {
|
||||
width: 90.66667%;
|
||||
}
|
||||
|
||||
.large-12,
|
||||
.row .large-12 {
|
||||
width: 99%;
|
||||
}
|
||||
|
||||
.row .small-offset-1 {
|
||||
margin-left: 7.33333%;
|
||||
}
|
||||
|
||||
.row .small-offset-2 {
|
||||
margin-left: 15.66667%;
|
||||
}
|
||||
|
||||
.row .small-offset-3 {
|
||||
margin-left: 24%;
|
||||
}
|
||||
|
||||
.row .small-offset-4 {
|
||||
margin-left: 32.33333%;
|
||||
}
|
||||
|
||||
.row .small-offset-5 {
|
||||
margin-left: 40.66667%;
|
||||
}
|
||||
|
||||
.row .small-offset-6 {
|
||||
margin-left: 49%;
|
||||
}
|
||||
|
||||
.row .small-offset-7 {
|
||||
margin-left: 57.33333%;
|
||||
}
|
||||
|
||||
.row .small-offset-8 {
|
||||
margin-left: 65.66667%;
|
||||
}
|
||||
|
||||
.row .small-offset-9 {
|
||||
margin-left: 74%;
|
||||
}
|
||||
|
||||
.row .small-offset-10 {
|
||||
margin-left: 82.33333%;
|
||||
}
|
||||
|
||||
.row .small-offset-11 {
|
||||
margin-left: 90.66667%;
|
||||
}
|
||||
|
||||
.row .small-offset-12 {
|
||||
margin-left: 99%;
|
||||
}
|
||||
|
||||
.row .medium-offset-1 {
|
||||
margin-left: 7.33333%;
|
||||
}
|
||||
|
||||
.row .medium-offset-2 {
|
||||
margin-left: 15.66667%;
|
||||
}
|
||||
|
||||
.row .medium-offset-3 {
|
||||
margin-left: 24%;
|
||||
}
|
||||
|
||||
.row .medium-offset-4 {
|
||||
margin-left: 32.33333%;
|
||||
}
|
||||
|
||||
.row .medium-offset-5 {
|
||||
margin-left: 40.66667%;
|
||||
}
|
||||
|
||||
.row .medium-offset-6 {
|
||||
margin-left: 49%;
|
||||
}
|
||||
|
||||
.row .medium-offset-7 {
|
||||
margin-left: 57.33333%;
|
||||
}
|
||||
|
||||
.row .medium-offset-8 {
|
||||
margin-left: 65.66667%;
|
||||
}
|
||||
|
||||
.row .medium-offset-9 {
|
||||
margin-left: 74%;
|
||||
}
|
||||
|
||||
.row .medium-offset-10 {
|
||||
margin-left: 82.33333%;
|
||||
}
|
||||
|
||||
.row .medium-offset-11 {
|
||||
margin-left: 90.66667%;
|
||||
}
|
||||
|
||||
.row .medium-offset-12 {
|
||||
margin-left: 99%;
|
||||
}
|
||||
|
||||
.row .large-offset-1 {
|
||||
margin-left: 7.33333%;
|
||||
}
|
||||
|
||||
.row .large-offset-2 {
|
||||
margin-left: 15.66667%;
|
||||
}
|
||||
|
||||
.row .large-offset-3 {
|
||||
margin-left: 24%;
|
||||
}
|
||||
|
||||
.row .large-offset-4 {
|
||||
margin-left: 32.33333%;
|
||||
}
|
||||
|
||||
.row .large-offset-5 {
|
||||
margin-left: 40.66667%;
|
||||
}
|
||||
|
||||
.row .large-offset-6 {
|
||||
margin-left: 49%;
|
||||
}
|
||||
|
||||
.row .large-offset-7 {
|
||||
margin-left: 57.33333%;
|
||||
}
|
||||
|
||||
.row .large-offset-8 {
|
||||
margin-left: 65.66667%;
|
||||
}
|
||||
|
||||
.row .large-offset-9 {
|
||||
margin-left: 74%;
|
||||
}
|
||||
|
||||
.row .large-offset-10 {
|
||||
margin-left: 82.33333%;
|
||||
}
|
||||
|
||||
.row .large-offset-11 {
|
||||
margin-left: 90.66667%;
|
||||
}
|
||||
|
||||
.row .large-offset-12 {
|
||||
margin-left: 99%;
|
||||
}
|
||||
|
||||
.top-bar {
|
||||
clear: both !important;
|
||||
height: 100px !important;
|
||||
}
|
||||
|
||||
.locale, .external-links {
|
||||
.locale,
|
||||
.external-links {
|
||||
background: #002d50 !important;
|
||||
}
|
||||
|
||||
@@ -121,11 +371,12 @@
|
||||
}
|
||||
|
||||
.external-links {
|
||||
color: white !important;
|
||||
color: #fff !important;
|
||||
float: right !important;
|
||||
}
|
||||
|
||||
.top-bar-title, .top-bar-title a, .top-bar-title a {
|
||||
.top-bar-title,
|
||||
.top-bar-title a {
|
||||
display: inline-block !important;
|
||||
float: none !important;
|
||||
}
|
||||
@@ -140,28 +391,29 @@
|
||||
margin: 0 !important;
|
||||
position: inherit !important;
|
||||
|
||||
&:after {
|
||||
&::after {
|
||||
content: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
|
||||
input, textarea {
|
||||
input,
|
||||
textarea {
|
||||
height: 48px !important;
|
||||
line-height: 48px !important;
|
||||
margin-bottom: 24px !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
height: auto !important;
|
||||
line-height: inherit !important;
|
||||
width: auto !important;
|
||||
}
|
||||
|
||||
input[type="radio"] {
|
||||
[type="radio"] {
|
||||
width: 18px !important;
|
||||
}
|
||||
}
|
||||
@@ -184,16 +436,17 @@ form {
|
||||
// 02. Admin
|
||||
// ---------
|
||||
|
||||
body.admin form {
|
||||
.admin form {
|
||||
|
||||
input[type="text"], textarea {
|
||||
[type="text"],
|
||||
textarea {
|
||||
height: 48px !important;
|
||||
line-height: 48px !important;
|
||||
margin-bottom: 24px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-sidebar ul [class^="icon-"] {
|
||||
.admin-sidebar [class^="icon-"] {
|
||||
padding-left: 12px !important;
|
||||
padding-right: 12px !important;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,7 @@
|
||||
h4 {
|
||||
font-weight: 400;
|
||||
text-align: center;
|
||||
color: white;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@
|
||||
.legislation-categories {
|
||||
|
||||
.menu.simple {
|
||||
border-bottom: none;
|
||||
border-bottom: 0;
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
margin-left: 0;
|
||||
@@ -37,6 +37,7 @@
|
||||
@include breakpoint(medium) {
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
display: block;
|
||||
@@ -47,9 +48,8 @@
|
||||
margin-bottom: 2rem;
|
||||
max-width: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
li.active {
|
||||
&.active {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
@@ -58,14 +58,14 @@
|
||||
// 03. Legislation cards
|
||||
// -----------------
|
||||
.legislation {
|
||||
margin: 0 0 $line-height 0;
|
||||
background: white;
|
||||
margin: 0 0 $line-height;
|
||||
background: #fff;
|
||||
border: 1px solid;
|
||||
border-color: #e5e6e9 #dfe0e4 #d0d1d5;
|
||||
border-radius: 0;
|
||||
box-shadow: 0px 1px 3px 0 #DEE0E3;
|
||||
box-shadow: 0 1px 3px 0 #dee0e3;
|
||||
min-height: 12rem;
|
||||
padding: 2rem 0 0 0;
|
||||
padding: 2rem 0 0;
|
||||
}
|
||||
|
||||
.legislation-text {
|
||||
@@ -83,13 +83,13 @@
|
||||
}
|
||||
|
||||
.legislation-calendar {
|
||||
background: #E5ECF2;
|
||||
background: #e5ecf2;
|
||||
padding-top: 1rem;
|
||||
|
||||
h5 {
|
||||
margin-left: 0.25rem;
|
||||
margin-bottom: 0;
|
||||
color: #61686E;
|
||||
color: #61686e;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
margin-left: 0;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
// -----------------
|
||||
|
||||
.grey-heading {
|
||||
background: #E6E6E6;
|
||||
background: #e6e6e6;
|
||||
}
|
||||
|
||||
$epigraph-font-size: rem-calc(15);
|
||||
@@ -39,15 +39,15 @@ $epigraph-line-height: rem-calc(22);
|
||||
list-style: none;
|
||||
margin-left: 0;
|
||||
|
||||
li:before {
|
||||
li::before {
|
||||
vertical-align: text-bottom;
|
||||
padding-right: 0.5rem;
|
||||
content: "■";
|
||||
color: #8AA8BE;
|
||||
content: '■';
|
||||
color: #8aa8be;
|
||||
}
|
||||
}
|
||||
|
||||
#debate-show {
|
||||
.legislation-debate-show {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -82,12 +82,9 @@ $epigraph-line-height: rem-calc(22);
|
||||
}
|
||||
|
||||
.half-gradient {
|
||||
/* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#e6e6e6+0,e6e6e6+50,ffffff+50 */
|
||||
background: #e6e6e6; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #e6e6e6 0%, #e6e6e6 50%, #ffffff 50%); /* FF3.6-15 */
|
||||
background: -webkit-linear-gradient(top, #e6e6e6 0%,#e6e6e6 50%,#ffffff 50%); /* Chrome10-25,Safari5.1-6 */
|
||||
background: linear-gradient(to bottom, #e6e6e6 0%,#e6e6e6 50%,#ffffff 50%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#e6e6e6', endColorstr='#ffffff',GradientType=0 ); /* IE6-9 */
|
||||
background: #e6e6e6;
|
||||
background: linear-gradient(to bottom, #e6e6e6 0%, #e6e6e6 50%, #fff 50%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#e6e6e6', endColorstr='#fff', GradientType=0);
|
||||
}
|
||||
|
||||
.text-center .button {
|
||||
@@ -115,6 +112,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
ul {
|
||||
font-size: $epigraph-font-size;
|
||||
line-height: $epigraph-line-height;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 1rem;
|
||||
@@ -124,7 +122,6 @@ $epigraph-line-height: rem-calc(22);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: $base-font-size;
|
||||
@@ -134,6 +131,11 @@ $epigraph-line-height: rem-calc(22);
|
||||
.button-subscribe {
|
||||
margin-top: 1rem;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
padding: 0.5em 1em;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@@ -144,12 +146,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
}
|
||||
|
||||
&:hover h3 {
|
||||
color: white;
|
||||
}
|
||||
|
||||
@include breakpoint(medium) {
|
||||
padding: 0.5em 1em;
|
||||
margin-top: 3rem;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,7 +158,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
|
||||
.legislation-process-list {
|
||||
border-bottom: 1px solid $medium-gray;
|
||||
margin: 0 1rem 1rem 1rem;
|
||||
margin: 0 1rem 1rem;
|
||||
padding-top: 4rem;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
@@ -190,6 +187,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
left: -1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
cursor: pointer;
|
||||
@@ -215,12 +213,13 @@ $epigraph-line-height: rem-calc(22);
|
||||
a,
|
||||
h4 {
|
||||
display: block;
|
||||
color: #6D6D6D;
|
||||
color: #6d6d6d;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
&:hover, &:active {
|
||||
&:hover,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@@ -238,7 +237,6 @@ $epigraph-line-height: rem-calc(22);
|
||||
border-bottom: 2px solid $brand;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 04. Debate list
|
||||
@@ -269,21 +267,21 @@ $epigraph-line-height: rem-calc(22);
|
||||
.debate-title a {
|
||||
color: $brand;
|
||||
}
|
||||
}
|
||||
|
||||
.debate-meta,
|
||||
.debate-meta a {
|
||||
font-size: $small-font-size;
|
||||
color: #6D6D6D;
|
||||
color: #6d6d6d;
|
||||
|
||||
.icon-comments {
|
||||
margin-right: 0.2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.debate-info {
|
||||
padding: 1rem;
|
||||
background: #F4F4F4;
|
||||
background: #f4f4f4;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,13 +295,14 @@ $epigraph-line-height: rem-calc(22);
|
||||
.quiz-header {
|
||||
margin-bottom: 2rem;
|
||||
|
||||
.quiz-title, .quiz-next {
|
||||
.quiz-title,
|
||||
.quiz-next {
|
||||
padding: 1rem;
|
||||
height: 6rem;
|
||||
}
|
||||
|
||||
.quiz-title {
|
||||
background: #E5ECF2;
|
||||
background: #e5ecf2;
|
||||
|
||||
.quiz-header-title {
|
||||
margin-bottom: 0;
|
||||
@@ -324,12 +323,13 @@ $epigraph-line-height: rem-calc(22);
|
||||
.quiz-next-link {
|
||||
display: block;
|
||||
|
||||
&:hover, &:active {
|
||||
&:hover,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.quiz-next {
|
||||
background: #CCDBE5;
|
||||
background: #ccdbe5;
|
||||
font-weight: 700;
|
||||
color: $brand;
|
||||
font-size: $small-font-size;
|
||||
@@ -341,13 +341,14 @@ $epigraph-line-height: rem-calc(22);
|
||||
vertical-align: sub;
|
||||
}
|
||||
|
||||
&:hover, &:active {
|
||||
&:hover,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
background: $brand;
|
||||
color: white;
|
||||
color: #fff;
|
||||
|
||||
.icon-angle-right {
|
||||
color: white;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -380,8 +381,8 @@ $epigraph-line-height: rem-calc(22);
|
||||
}
|
||||
|
||||
.active {
|
||||
background: #CCDBE6;
|
||||
border: none;
|
||||
background: #ccdbe6;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.control input {
|
||||
@@ -392,7 +393,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
|
||||
.control input:checked ~ .control-indicator {
|
||||
background-color: $brand;
|
||||
border: none;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.radio .control-indicator {
|
||||
@@ -409,11 +410,8 @@ $epigraph-line-height: rem-calc(22);
|
||||
line-height: 1rem;
|
||||
font-size: 65%;
|
||||
text-align: center;
|
||||
border: 2px solid #9C9C9C;
|
||||
border: 2px solid #9c9c9c;
|
||||
pointer-events: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
@@ -422,9 +420,9 @@ $epigraph-line-height: rem-calc(22);
|
||||
// 06. Legislation draft
|
||||
// -----------------
|
||||
.debate-draft {
|
||||
padding: 10rem 2rem 15rem 2rem;
|
||||
padding: 10rem 2rem 15rem;
|
||||
display: block;
|
||||
background: #F2F2F2;
|
||||
background: #f2f2f2;
|
||||
|
||||
button {
|
||||
height: 90px;
|
||||
@@ -445,7 +443,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
.legislation-allegation {
|
||||
padding-top: 1rem;
|
||||
|
||||
#debate-show {
|
||||
.legislation-debate-show {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
@@ -459,7 +457,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
|
||||
.button-circle {
|
||||
line-height: 0;
|
||||
padding: 0em;
|
||||
padding: 0;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
@@ -467,7 +465,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
span {
|
||||
padding-left: 1px;
|
||||
|
||||
&:before {
|
||||
&::before {
|
||||
line-height: 1.55;
|
||||
}
|
||||
}
|
||||
@@ -482,8 +480,12 @@ $epigraph-line-height: rem-calc(22);
|
||||
|
||||
.button-subscribed {
|
||||
margin-top: 1rem;
|
||||
border: 1px solid #D1D1D1;
|
||||
background: #F2F2F2;
|
||||
border: 1px solid #d1d1d1;
|
||||
background: #f2f2f2;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
display: inline-block;
|
||||
@@ -499,10 +501,6 @@ $epigraph-line-height: rem-calc(22);
|
||||
&:hover h3 {
|
||||
color: $text;
|
||||
}
|
||||
|
||||
@include breakpoint(medium) {
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,7 +545,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
color: $text-medium
|
||||
color: $text-medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -585,12 +583,12 @@ $epigraph-line-height: rem-calc(22);
|
||||
}
|
||||
|
||||
.calc-text {
|
||||
width: calc(65% - 25px)
|
||||
width: calc(65% - 25px);
|
||||
}
|
||||
|
||||
.calc-comments {
|
||||
cursor: pointer;
|
||||
background: #F2F2F2;
|
||||
background: #f2f2f2;
|
||||
width: 50px;
|
||||
|
||||
.draft-panel {
|
||||
@@ -618,7 +616,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
font-weight: 700;
|
||||
padding: 0.5rem 1rem;
|
||||
color: #696969;
|
||||
background: #F2F2F2;
|
||||
background: #f2f2f2;
|
||||
font-size: $small-font-size;
|
||||
|
||||
.icon-comments {
|
||||
@@ -647,21 +645,23 @@ $epigraph-line-height: rem-calc(22);
|
||||
li {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.open::before {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
margin-left: -1.25rem;
|
||||
font-family: "icons";
|
||||
content: "\58";
|
||||
font-family: 'icons';
|
||||
content: '\58';
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.closed::before {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
margin-left: -1.25rem;
|
||||
font-family: "icons";
|
||||
content: "\5a";
|
||||
font-family: 'icons';
|
||||
content: '\5a';
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
@@ -689,7 +689,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
|
||||
.anchor::before {
|
||||
display: none;
|
||||
content: "#";
|
||||
content: '#';
|
||||
color: $text-medium;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
@@ -732,10 +732,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
font-weight: 700;
|
||||
color: #696969;
|
||||
margin-top: 4rem;
|
||||
-webkit-transform: rotate(-90deg);
|
||||
-moz-transform: rotate(-90deg);
|
||||
-ms-transform: rotate(-90deg);
|
||||
-o-transform: rotate(-90deg);
|
||||
transform: rotate(-90deg);
|
||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
|
||||
}
|
||||
}
|
||||
@@ -746,7 +743,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
.comments-on {
|
||||
.calc-index {
|
||||
width: 50px;
|
||||
background: #F2F2F2;
|
||||
background: #f2f2f2;
|
||||
cursor: pointer;
|
||||
|
||||
.panel-title {
|
||||
@@ -766,10 +763,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
font-weight: 700;
|
||||
color: #696969;
|
||||
margin-top: $line-height;
|
||||
-webkit-transform: rotate(-90deg);
|
||||
-moz-transform: rotate(-90deg);
|
||||
-ms-transform: rotate(-90deg);
|
||||
-o-transform: rotate(-90deg);
|
||||
transform: rotate(-90deg);
|
||||
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
|
||||
|
||||
.panel-title {
|
||||
@@ -781,11 +775,11 @@ $epigraph-line-height: rem-calc(22);
|
||||
|
||||
.calc-text {
|
||||
width: calc(65% - 25px);
|
||||
border-right: none;
|
||||
border-right: 0;
|
||||
|
||||
.show-comments {
|
||||
width: 105%;
|
||||
background: #FAFAFA;
|
||||
background: #fafafa;
|
||||
padding: 0.25rem 2.5rem 0.25rem 0.25rem;
|
||||
border: 1px solid $border;
|
||||
margin-bottom: 1rem;
|
||||
@@ -798,7 +792,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
}
|
||||
|
||||
.calc-comments {
|
||||
background: white;
|
||||
background: #fff;
|
||||
cursor: auto;
|
||||
width: calc(35% - 25px);
|
||||
|
||||
@@ -810,7 +804,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
display: none;
|
||||
}
|
||||
|
||||
#comments-box {
|
||||
.comments-box-container {
|
||||
position: absolute;
|
||||
top: 230px;
|
||||
}
|
||||
@@ -818,7 +812,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
.comment-box {
|
||||
width: 375px;
|
||||
padding: 1rem;
|
||||
background: #F9F9F9;
|
||||
background: #f9f9f9;
|
||||
border: 1px solid $border;
|
||||
display: block;
|
||||
margin-bottom: 2rem;
|
||||
@@ -868,7 +862,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
.participation-not-allowed {
|
||||
font-size: 0.875rem;
|
||||
height: 50px;
|
||||
padding: .85rem 0.75rem;
|
||||
padding: 0.85rem 0.75rem;
|
||||
top: -18px;
|
||||
}
|
||||
}
|
||||
@@ -880,21 +874,21 @@ $epigraph-line-height: rem-calc(22);
|
||||
border-bottom: 1px solid $border;
|
||||
|
||||
.comment-advice {
|
||||
border-top: 1px solid #D0D0D0;
|
||||
border-right: 1px solid #D0D0D0;
|
||||
border-left: 1px solid #D0D0D0;
|
||||
border-top: 1px solid #d0d0d0;
|
||||
border-right: 1px solid #d0d0d0;
|
||||
border-left: 1px solid #d0d0d0;
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
display: inline-block;
|
||||
font-size: $small-font-size;
|
||||
background: #DFDFDF;
|
||||
background: #dfdfdf;
|
||||
|
||||
.icon-proposals {
|
||||
color: #838383;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #87A3B9;
|
||||
color: #87a3b9;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
@@ -902,9 +896,9 @@ $epigraph-line-height: rem-calc(22);
|
||||
textarea {
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
border-bottom: 1px solid #D0D0D0;
|
||||
border-right: 1px solid #D0D0D0;
|
||||
border-left: 1px solid #D0D0D0;
|
||||
border-bottom: 1px solid #d0d0d0;
|
||||
border-right: 1px solid #d0d0d0;
|
||||
border-left: 1px solid #d0d0d0;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
margin-bottom: 0.5rem;
|
||||
@@ -912,7 +906,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
|
||||
.comment-actions {
|
||||
.cancel-comment {
|
||||
color: #87A3B9;
|
||||
color: #87a3b9;
|
||||
text-decoration: underline;
|
||||
font-size: $small-font-size;
|
||||
display: inline-block;
|
||||
@@ -948,10 +942,11 @@ $epigraph-line-height: rem-calc(22);
|
||||
display: inline-block;
|
||||
|
||||
&::after {
|
||||
content: "|";
|
||||
content: '|';
|
||||
color: #838383;
|
||||
}
|
||||
}
|
||||
|
||||
.comment-replies {
|
||||
display: inline-block;
|
||||
}
|
||||
@@ -968,14 +963,14 @@ $epigraph-line-height: rem-calc(22);
|
||||
|
||||
&::after {
|
||||
margin-left: 0.25rem;
|
||||
content: "|";
|
||||
content: '|';
|
||||
}
|
||||
}
|
||||
|
||||
.icon-like,
|
||||
.icon-unlike {
|
||||
cursor: pointer;
|
||||
color: #C7C7C7;
|
||||
color: #c7c7c7;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
@@ -993,8 +988,8 @@ $epigraph-line-height: rem-calc(22);
|
||||
}
|
||||
|
||||
.draft-panel {
|
||||
background: #E5E5E5;
|
||||
border-left: 1px solid #D4D4D4;
|
||||
background: #e5e5e5;
|
||||
border-left: 1px solid #d4d4d4;
|
||||
|
||||
.panel-title {
|
||||
display: inline-block;
|
||||
@@ -1022,7 +1017,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
|
||||
&::before {
|
||||
margin-right: 0.25rem;
|
||||
content: "—"
|
||||
content: '—';
|
||||
}
|
||||
|
||||
.changes-link {
|
||||
@@ -1041,7 +1036,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
|
||||
.icon-external {
|
||||
text-decoration: none;
|
||||
color: #999999;
|
||||
color: #999;
|
||||
line-height: 0;
|
||||
vertical-align: sub;
|
||||
margin-left: 0.5rem;
|
||||
@@ -1067,9 +1062,9 @@ $epigraph-line-height: rem-calc(22);
|
||||
}
|
||||
|
||||
.comment-section {
|
||||
background: #FAFAFA;
|
||||
background: #fafafa;
|
||||
padding: 1rem;
|
||||
border: 1px solid #DEE0E3;
|
||||
border: 1px solid #dee0e3;
|
||||
margin-top: 0.25rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
@@ -1085,7 +1080,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
.icon-expand,
|
||||
.icon-comments {
|
||||
text-decoration: none;
|
||||
color: #999999;
|
||||
color: #999;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
@@ -1119,9 +1114,9 @@ $epigraph-line-height: rem-calc(22);
|
||||
}
|
||||
|
||||
.comment-section {
|
||||
background: #FAFAFA;
|
||||
background: #fafafa;
|
||||
padding: 1rem;
|
||||
border: 1px solid #DEE0E3;
|
||||
border: 1px solid #dee0e3;
|
||||
margin-top: 0.25rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
@@ -1137,7 +1132,7 @@ $epigraph-line-height: rem-calc(22);
|
||||
.icon-expand,
|
||||
.icon-comments {
|
||||
text-decoration: none;
|
||||
color: #999999;
|
||||
color: #999;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
// --------
|
||||
|
||||
@mixin logo {
|
||||
color: white;
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
font-family: 'Lato' !important;
|
||||
font-size: rem-calc(24);
|
||||
font-weight: lighter;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
line-height: $line-height*2;
|
||||
line-height: $line-height * 2;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
@include breakpoint(medium) {
|
||||
height: 80px;
|
||||
margin-right: $line-height/2;
|
||||
margin-right: $line-height / 2;
|
||||
margin-top: 0;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
padding-top: $line-height;
|
||||
|
||||
&.light {
|
||||
background: #ECF0F1;
|
||||
background: #ecf0f1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
@include breakpoint(medium) {
|
||||
display: inline-block;
|
||||
margin-right: $line-height/2;
|
||||
margin-right: $line-height / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@
|
||||
color: $brand;
|
||||
}
|
||||
|
||||
.additional-info {
|
||||
.additional-info {
|
||||
margin-bottom: $line-height;
|
||||
}
|
||||
|
||||
@@ -68,23 +68,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
ul.features {
|
||||
.features {
|
||||
list-style-type: circle;
|
||||
margin-left: $line-height;
|
||||
|
||||
@include breakpoint(medium) {
|
||||
margin: $line-height 0 $line-height $line-height*2.5;
|
||||
margin: $line-height 0 $line-height $line-height * 2.5;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: $line-height
|
||||
margin-bottom: $line-height;
|
||||
}
|
||||
}
|
||||
|
||||
.section-content {
|
||||
border-top: 1px solid $medium-gray;
|
||||
padding-bottom: $line-height*2;
|
||||
padding-top: $line-height*2;
|
||||
padding-bottom: $line-height * 2;
|
||||
padding-top: $line-height * 2;
|
||||
|
||||
&:first-child {
|
||||
border-top: 0;
|
||||
@@ -101,10 +101,10 @@
|
||||
.sidebar-card {
|
||||
border: 1px solid $border;
|
||||
margin-bottom: $line-height;
|
||||
padding: $line-height/2;
|
||||
padding: $line-height / 2;
|
||||
|
||||
&.light {
|
||||
background: #ECF0F1;
|
||||
background: #ecf0f1;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
52
app/controllers/graphql_controller.rb
Normal file
52
app/controllers/graphql_controller.rb
Normal file
@@ -0,0 +1,52 @@
|
||||
class GraphqlController < ApplicationController
|
||||
|
||||
skip_before_action :verify_authenticity_token
|
||||
skip_authorization_check
|
||||
|
||||
class QueryStringError < StandardError; end
|
||||
|
||||
def query
|
||||
begin
|
||||
if query_string.nil? then raise GraphqlController::QueryStringError end
|
||||
response = consul_schema.execute query_string, variables: query_variables
|
||||
render json: response, status: :ok
|
||||
rescue GraphqlController::QueryStringError
|
||||
render json: { message: 'Query string not present' }, status: :bad_request
|
||||
rescue JSON::ParserError
|
||||
render json: { message: 'Error parsing JSON' }, status: :bad_request
|
||||
rescue GraphQL::ParseError
|
||||
render json: { message: 'Query string is not valid JSON' }, status: :bad_request
|
||||
rescue
|
||||
unless Rails.env.production? then raise end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def consul_schema
|
||||
api_types = GraphQL::ApiTypesCreator.create(API_TYPE_DEFINITIONS)
|
||||
query_type = GraphQL::QueryTypeCreator.create(api_types)
|
||||
|
||||
GraphQL::Schema.define do
|
||||
query query_type
|
||||
max_depth 8
|
||||
max_complexity 2500
|
||||
end
|
||||
end
|
||||
|
||||
def query_string
|
||||
if request.headers["CONTENT_TYPE"] == 'application/graphql'
|
||||
request.body.string # request.body.class => StringIO
|
||||
else
|
||||
params[:query]
|
||||
end
|
||||
end
|
||||
|
||||
def query_variables
|
||||
if params[:variables].blank? || params[:variables] == 'null'
|
||||
{}
|
||||
else
|
||||
JSON.parse(params[:variables])
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,5 +1,7 @@
|
||||
class Comment < ActiveRecord::Base
|
||||
include Flaggable
|
||||
include HasPublicAuthor
|
||||
include Graphqlable
|
||||
|
||||
acts_as_paranoid column: :hidden_at
|
||||
include ActsAsParanoidAliases
|
||||
@@ -24,6 +26,12 @@ class Comment < ActiveRecord::Base
|
||||
scope :with_visible_author, -> { joins(:user).where("users.hidden_at IS NULL") }
|
||||
scope :not_as_admin_or_moderator, -> { where("administrator_id IS NULL").where("moderator_id IS NULL")}
|
||||
scope :sort_by_flags, -> { order(flags_count: :desc, updated_at: :desc) }
|
||||
scope :public_for_api, -> do
|
||||
where(%{(comments.commentable_type = 'Debate' and comments.commentable_id in (?)) or
|
||||
(comments.commentable_type = 'Proposal' and comments.commentable_id in (?))},
|
||||
Debate.public_for_api.pluck(:id),
|
||||
Proposal.public_for_api.pluck(:id))
|
||||
end
|
||||
|
||||
scope :sort_by_most_voted, -> { order(confidence_score: :desc, created_at: :desc) }
|
||||
scope :sort_descendants_by_most_voted, -> { order(confidence_score: :desc, created_at: :asc) }
|
||||
|
||||
36
app/models/concerns/graphqlable.rb
Normal file
36
app/models/concerns/graphqlable.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
module Graphqlable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
class_methods do
|
||||
|
||||
def graphql_field_name
|
||||
self.name.gsub('::', '_').underscore.to_sym
|
||||
end
|
||||
|
||||
def graphql_field_description
|
||||
"Find one #{self.model_name.human} by ID"
|
||||
end
|
||||
|
||||
def graphql_pluralized_field_name
|
||||
self.name.gsub('::', '_').underscore.pluralize.to_sym
|
||||
end
|
||||
|
||||
def graphql_pluralized_field_description
|
||||
"Find all #{self.model_name.human.pluralize}"
|
||||
end
|
||||
|
||||
def graphql_type_name
|
||||
self.name.gsub('::', '_')
|
||||
end
|
||||
|
||||
def graphql_type_description
|
||||
"#{self.model_name.human}"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def public_created_at
|
||||
self.created_at.change(min: 0)
|
||||
end
|
||||
|
||||
end
|
||||
5
app/models/concerns/has_public_author.rb
Normal file
5
app/models/concerns/has_public_author.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
module HasPublicAuthor
|
||||
def public_author
|
||||
self.author.public_activity? ? self.author : nil
|
||||
end
|
||||
end
|
||||
@@ -4,11 +4,11 @@ module Measurable
|
||||
class_methods do
|
||||
|
||||
def title_max_length
|
||||
@@title_max_length ||= self.columns.find { |c| c.name == 'title' }.limit || 80
|
||||
@@title_max_length ||= (self.columns.find { |c| c.name == 'title' }.limit rescue nil) || 80
|
||||
end
|
||||
|
||||
def responsible_name_max_length
|
||||
@@responsible_name_max_length ||= self.columns.find { |c| c.name == 'responsible_name' }.limit || 60
|
||||
@@responsible_name_max_length ||= (self.columns.find { |c| c.name == 'responsible_name' }.limit rescue nil) || 60
|
||||
end
|
||||
|
||||
def question_max_length
|
||||
|
||||
@@ -7,6 +7,8 @@ class Debate < ActiveRecord::Base
|
||||
include Sanitizable
|
||||
include Searchable
|
||||
include Filterable
|
||||
include HasPublicAuthor
|
||||
include Graphqlable
|
||||
|
||||
acts_as_votable
|
||||
acts_as_paranoid column: :hidden_at
|
||||
@@ -37,6 +39,7 @@ class Debate < ActiveRecord::Base
|
||||
scope :sort_by_flags, -> { order(flags_count: :desc, updated_at: :desc) }
|
||||
scope :last_week, -> { where("created_at >= ?", 7.days.ago)}
|
||||
scope :featured, -> { where("featured_at is not null")}
|
||||
scope :public_for_api, -> { all }
|
||||
# Ahoy setup
|
||||
visitable # Ahoy will automatically assign visit_id on create
|
||||
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
class Geozone < ActiveRecord::Base
|
||||
|
||||
include Graphqlable
|
||||
|
||||
has_many :proposals
|
||||
has_many :spending_proposals
|
||||
has_many :debates
|
||||
has_many :users
|
||||
validates :name, presence: true
|
||||
|
||||
scope :public_for_api, -> { all }
|
||||
|
||||
def self.names
|
||||
Geozone.pluck(:name)
|
||||
end
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
class Organization < ActiveRecord::Base
|
||||
|
||||
include Graphqlable
|
||||
|
||||
belongs_to :user, touch: true
|
||||
|
||||
validates :name, presence: true
|
||||
|
||||
@@ -6,6 +6,8 @@ class Proposal < ActiveRecord::Base
|
||||
include Sanitizable
|
||||
include Searchable
|
||||
include Filterable
|
||||
include HasPublicAuthor
|
||||
include Graphqlable
|
||||
|
||||
acts_as_votable
|
||||
acts_as_paranoid column: :hidden_at
|
||||
@@ -51,6 +53,7 @@ class Proposal < ActiveRecord::Base
|
||||
scope :retired, -> { where.not(retired_at: nil) }
|
||||
scope :not_retired, -> { where(retired_at: nil) }
|
||||
scope :successful, -> { where("cached_votes_up >= ?", Proposal.votes_needed_for_success) }
|
||||
scope :public_for_api, -> { all }
|
||||
|
||||
def to_param
|
||||
"#{id}-#{title}".parameterize
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
class ProposalNotification < ActiveRecord::Base
|
||||
|
||||
include Graphqlable
|
||||
|
||||
belongs_to :author, class_name: 'User', foreign_key: 'author_id'
|
||||
belongs_to :proposal
|
||||
|
||||
@@ -7,6 +10,8 @@ class ProposalNotification < ActiveRecord::Base
|
||||
validates :proposal, presence: true
|
||||
validate :minimum_interval
|
||||
|
||||
scope :public_for_api, -> { where(proposal_id: Proposal.public_for_api.pluck(:id)) }
|
||||
|
||||
def minimum_interval
|
||||
return true if proposal.try(:notifications).blank?
|
||||
if proposal.notifications.last.created_at > (Time.current - Setting[:proposal_notification_minimum_interval_in_days].to_i.days).to_datetime
|
||||
|
||||
@@ -10,6 +10,8 @@ class User < ActiveRecord::Base
|
||||
acts_as_paranoid column: :hidden_at
|
||||
include ActsAsParanoidAliases
|
||||
|
||||
include Graphqlable
|
||||
|
||||
has_one :administrator
|
||||
has_one :moderator
|
||||
has_one :valuator
|
||||
@@ -61,6 +63,7 @@ class User < ActiveRecord::Base
|
||||
scope :email_digest, -> { where(email_digest: true) }
|
||||
scope :active, -> { where(erased_at: nil) }
|
||||
scope :erased, -> { where.not(erased_at: nil) }
|
||||
scope :public_for_api, -> { all }
|
||||
|
||||
before_validation :clean_document_number
|
||||
|
||||
@@ -288,6 +291,18 @@ class User < ActiveRecord::Base
|
||||
end
|
||||
delegate :can?, :cannot?, to: :ability
|
||||
|
||||
def public_proposals
|
||||
public_activity? ? proposals : User.none
|
||||
end
|
||||
|
||||
def public_debates
|
||||
public_activity? ? debates : User.none
|
||||
end
|
||||
|
||||
def public_comments
|
||||
public_activity? ? comments : User.none
|
||||
end
|
||||
|
||||
# overwritting of Devise method to allow login using email OR username
|
||||
def self.find_for_database_authentication(warden_conditions)
|
||||
conditions = warden_conditions.dup
|
||||
|
||||
@@ -1,2 +1,14 @@
|
||||
class Vote < ActsAsVotable::Vote
|
||||
|
||||
include Graphqlable
|
||||
|
||||
scope :public_for_api, -> do
|
||||
where(%{(votes.votable_type = 'Debate' and votes.votable_id in (?)) or
|
||||
(votes.votable_type = 'Proposal' and votes.votable_id in (?)) or
|
||||
(votes.votable_type = 'Comment' and votes.votable_id in (?))},
|
||||
Debate.public_for_api.pluck(:id),
|
||||
Proposal.public_for_api.pluck(:id),
|
||||
Comment.public_for_api.pluck(:id))
|
||||
end
|
||||
|
||||
end
|
||||
@@ -79,7 +79,7 @@
|
||||
<div class="small-12 medium-6 column markdown-area">
|
||||
<%= f.text_area :body, label: false, placeholder: t('admin.legislation.draft_versions.form.body_placeholder') %>
|
||||
</div>
|
||||
<div id="markdown-preview" class="small-12 medium-6 column">
|
||||
<div id="markdown-preview" class="small-12 medium-6 column markdown-preview">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<h2 class="inline-block"><%= t("admin.site_customization.content_blocks.index.title") %></h2>
|
||||
|
||||
<table class="cms_page_list">
|
||||
<table class="cms-page-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%= t("admin.site_customization.content_blocks.content_block.name") %></th>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<% if @pages.any? %>
|
||||
<h3><%= page_entries_info @pages %></h3>
|
||||
|
||||
<table class="cms_page_list">
|
||||
<table class="cms-page-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%= t("admin.site_customization.pages.page.title") %></th>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<header>
|
||||
<header class="header">
|
||||
<div class="top-links">
|
||||
<div class="expanded row">
|
||||
<%= render 'shared/locale_switcher' %>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<span class="panel-title"><%= t('legislation.draft_versions.show.text_comments') %></span>
|
||||
</div>
|
||||
|
||||
<div id="comments-box" style="display: none;">
|
||||
<div id="comments-box" class="comments-box-container" style="display: none;">
|
||||
<div class="comment-box">
|
||||
<div class="comment-header">
|
||||
<span class="icon-comment" aria-hidden="true"></span>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="debate-show" class="row description">
|
||||
<div id="debate-show" class="row description legislation-debate-show">
|
||||
<div class="small-12 column">
|
||||
<% if process.description.present? %>
|
||||
<h4><%= t('legislation.processes.header_full.description') %></h4>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<% end %>
|
||||
</div>
|
||||
<% if process.additional_info.present? %>
|
||||
<div id="debate-show" class="small-12 column debate-add-info">
|
||||
<div id="debate-show" class="small-12 column debate-add-info legislation-debate-show">
|
||||
<div class="debate-info-wrapper">
|
||||
<%= markdown process.additional_info if process.additional_info %>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<%= render 'admin/shared/proposal_search', url: management_proposals_path %>
|
||||
|
||||
<div class="row">
|
||||
<div id="proposals" class="proposals-list small-12 medium-9 column">
|
||||
<div class="proposals-list small-12 column">
|
||||
|
||||
<div class="filters">
|
||||
<div class="small-12 medium-7 float-left">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<main>
|
||||
<div class="row">
|
||||
<div id="proposals" class="proposals-list small-12 column">
|
||||
<div class="proposals-list small-12 column">
|
||||
<a id="print_link" href="javascript:window.print();" class="button warning float-right">
|
||||
<%= t('management.proposals.print.print_button') %>
|
||||
</a>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<a href="#" class="button float-right margin-right">Crear nueva página</a>
|
||||
<h2 class="inline-block">Editar páginas</h2>
|
||||
|
||||
<table class="cms_page_list">
|
||||
<table class="cms-page-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nombre</th>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="edit_legislation_draft_version" id="edit_legislation_draft_version_1" action="/admin/legislation/processes/1/draft_versions/1" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓" /><input type="hidden" name="_method" value="patch" /><input type="hidden" name="authenticity_token" value="PGlvCYHqh/eXoeE0qz7DEZHsTghiKHY9HSZa2JNj+5pFPxPkF1Zuq3gaAuTmTjjhNi86OYIebJPVjFoaBIRaCA==" />
|
||||
<form class="edit-legislation-draft-version" id="edit_legislation_draft_version_1" action="/admin/legislation/processes/1/draft_versions/1" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓" /><input type="hidden" name="_method" value="patch" /><input type="hidden" name="authenticity_token" value="PGlvCYHqh/eXoeE0qz7DEZHsTghiKHY9HSZa2JNj+5pFPxPkF1Zuq3gaAuTmTjjhNi86OYIebJPVjFoaBIRaCA==" />
|
||||
|
||||
<div class="row">
|
||||
<div class="small-12 medium-4 column">
|
||||
@@ -105,7 +105,7 @@ Esta es la primera versión, no hay cambios.</textarea>
|
||||
<a class="fullscreen-toggle" href="#"><span>Pantalla completa</span> <span class="icon-expand"></span></a>
|
||||
</div>
|
||||
<div class="small-12 medium-6 column">
|
||||
<textarea name="legislation_draft_version[body]" id="legislation_draft_version_body">
|
||||
<textarea name="legislation_draft_version[body]" id="legislation_draft_version_body" class="legislation-draft-version-body">
|
||||
## PREÁMBULO
|
||||
|
||||
### I
|
||||
@@ -579,7 +579,7 @@ e) La afectación legal de los terrenos obtenidos por la Administración en virt
|
||||
3. Antes de la finalización de las obras de urbanización no es posible, con carácter general, la realización de otros actos edificatorios o de implantación de usos que los provisionales previstos en la letra b) del número 1 anterior. Sin embargo, podrá autorizarse la edificación vinculada a la simultánea terminación de las obras de urbanización inmediata a la parcela de que se trate, en las mismas condiciones que en el suelo urbano consolidado.
|
||||
</textarea>
|
||||
</div>
|
||||
<div id="markdown-preview" class="small-12 medium-6 column"></div>
|
||||
<div id="markdown-preview" class="small-12 medium-6 column markdown-preview"></div>
|
||||
<div id="markdown-result">
|
||||
<input label="false" type="hidden" value="<h2>PREÁMBULO</h2>
|
||||
<h3>I</h3>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<form class="edit_legislation_process" id="edit_legislation_process_1" action="/admin/legislation/processes/1" accept-charset="UTF-8" method="post">
|
||||
<form class="edit-legislation-process" id="edit_legislation_process_1" action="/admin/legislation/processes/1" accept-charset="UTF-8" method="post">
|
||||
|
||||
<div class="row">
|
||||
<div class="small-12 medium-4 column">
|
||||
|
||||
89
config/api.yml
Normal file
89
config/api.yml
Normal file
@@ -0,0 +1,89 @@
|
||||
User:
|
||||
fields:
|
||||
id: integer
|
||||
username: string
|
||||
public_debates: [Debate]
|
||||
public_proposals: [Proposal]
|
||||
public_comments: [Comment]
|
||||
# organization: Organization
|
||||
Debate:
|
||||
fields:
|
||||
id: integer
|
||||
title: string
|
||||
description: string
|
||||
public_created_at: string
|
||||
cached_votes_total: integer
|
||||
cached_votes_up: integer
|
||||
cached_votes_down: integer
|
||||
comments_count: integer
|
||||
hot_score: integer
|
||||
confidence_score: integer
|
||||
comments: [Comment]
|
||||
public_author: User
|
||||
votes_for: [Vote]
|
||||
tags: ["ActsAsTaggableOn::Tag"]
|
||||
Proposal:
|
||||
fields:
|
||||
id: integer
|
||||
title: string
|
||||
description: string
|
||||
external_url: string
|
||||
cached_votes_up: integer
|
||||
comments_count: integer
|
||||
hot_score: integer
|
||||
confidence_score: integer
|
||||
public_created_at: string
|
||||
summary: string
|
||||
video_url: string
|
||||
geozone_id: integer
|
||||
retired_at: string
|
||||
retired_reason: string
|
||||
retired_explanation: string
|
||||
geozone: Geozone
|
||||
comments: [Comment]
|
||||
proposal_notifications: [ProposalNotification]
|
||||
public_author: User
|
||||
votes_for: [Vote]
|
||||
tags: ["ActsAsTaggableOn::Tag"]
|
||||
Comment:
|
||||
fields:
|
||||
id: integer
|
||||
commentable_id: integer
|
||||
commentable_type: string
|
||||
body: string
|
||||
public_created_at: string
|
||||
cached_votes_total: integer
|
||||
cached_votes_up: integer
|
||||
cached_votes_down: integer
|
||||
ancestry: string
|
||||
confidence_score: integer
|
||||
public_author: User
|
||||
votes_for: [Vote]
|
||||
Geozone:
|
||||
fields:
|
||||
id: integer
|
||||
name: string
|
||||
ProposalNotification:
|
||||
fields:
|
||||
title: string
|
||||
body: string
|
||||
proposal_id: integer
|
||||
public_created_at: string
|
||||
proposal: Proposal
|
||||
ActsAsTaggableOn::Tag:
|
||||
fields:
|
||||
id: integer
|
||||
name: string
|
||||
taggings_count: integer
|
||||
kind: string
|
||||
Vote:
|
||||
fields:
|
||||
votable_id: integer
|
||||
votable_type: string
|
||||
public_created_at: string
|
||||
vote_flag: boolean
|
||||
# Organization:
|
||||
# fields:
|
||||
# id: integer
|
||||
# user_id: integer
|
||||
# name: string
|
||||
@@ -5,6 +5,15 @@ module ActsAsTaggableOn
|
||||
after_create :increment_tag_custom_counter
|
||||
after_destroy :touch_taggable, :decrement_tag_custom_counter
|
||||
|
||||
scope :public_for_api, -> do
|
||||
where(%{taggings.tag_id in (?) and
|
||||
(taggings.taggable_type = 'Debate' and taggings.taggable_id in (?)) or
|
||||
(taggings.taggable_type = 'Proposal' and taggings.taggable_id in (?))},
|
||||
Tag.where('kind IS NULL or kind = ?', 'category').pluck(:id),
|
||||
Debate.public_for_api.pluck(:id),
|
||||
Proposal.public_for_api.pluck(:id))
|
||||
end
|
||||
|
||||
def touch_taggable
|
||||
taggable.touch if taggable.present?
|
||||
end
|
||||
@@ -20,6 +29,14 @@ module ActsAsTaggableOn
|
||||
|
||||
Tag.class_eval do
|
||||
|
||||
include Graphqlable
|
||||
|
||||
scope :public_for_api, -> do
|
||||
where('(tags.kind IS NULL or tags.kind = ?) and tags.id in (?)',
|
||||
'category',
|
||||
Tagging.public_for_api.pluck('DISTINCT taggings.tag_id'))
|
||||
end
|
||||
|
||||
def increment_custom_counter_for(taggable_type)
|
||||
Tag.increment_counter(custom_counter_field_name_for(taggable_type), id)
|
||||
end
|
||||
@@ -42,6 +59,18 @@ module ActsAsTaggableOn
|
||||
ActsAsTaggableOn::Tag.where('taggings.taggable_type' => 'SpendingProposal').includes(:taggings).order(:name).uniq
|
||||
end
|
||||
|
||||
def self.graphql_field_name
|
||||
:tag
|
||||
end
|
||||
|
||||
def self.graphql_pluralized_field_name
|
||||
:tags
|
||||
end
|
||||
|
||||
def self.graphql_type_name
|
||||
'Tag'
|
||||
end
|
||||
|
||||
private
|
||||
def custom_counter_field_name_for(taggable_type)
|
||||
"#{taggable_type.underscore.pluralize}_count"
|
||||
|
||||
4
config/initializers/graphql.rb
Normal file
4
config/initializers/graphql.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
if ActiveRecord::Base.connection.tables.any?
|
||||
api_config = YAML.load_file('./config/api.yml')
|
||||
API_TYPE_DEFINITIONS = GraphQL::ApiTypesCreator::parse_api_config_file(api_config)
|
||||
end
|
||||
@@ -400,10 +400,15 @@ Rails.application.routes.draw do
|
||||
root to: "dashboard#index"
|
||||
end
|
||||
|
||||
# GraphQL
|
||||
get '/graphql', to: 'graphql#query'
|
||||
post '/graphql', to: 'graphql#query'
|
||||
|
||||
if Rails.env.development?
|
||||
mount LetterOpenerWeb::Engine, at: "/letter_opener"
|
||||
end
|
||||
|
||||
mount GraphiQL::Rails::Engine, at: '/graphiql', graphql_path: '/graphql'
|
||||
mount Tolk::Engine => '/translate', :as => 'tolk'
|
||||
|
||||
# more info pages
|
||||
|
||||
@@ -59,7 +59,17 @@ print "Creating Users"
|
||||
|
||||
def create_user(email, username = Faker::Name.name)
|
||||
pwd = '12345678'
|
||||
User.create!(username: username, email: email, password: pwd, password_confirmation: pwd, confirmed_at: Time.current, terms_of_service: "1")
|
||||
User.create!(
|
||||
username: username,
|
||||
email: email,
|
||||
password: pwd,
|
||||
password_confirmation: pwd,
|
||||
confirmed_at: Time.current,
|
||||
terms_of_service: "1",
|
||||
gender: ['Male', 'Female'].sample,
|
||||
date_of_birth: rand((Time.current - 80.years) .. (Time.current - 16.years)),
|
||||
public_activity: (rand(1..100) > 30)
|
||||
)
|
||||
end
|
||||
|
||||
admin = create_user('admin@consul.dev', 'admin')
|
||||
@@ -103,11 +113,11 @@ end
|
||||
official.update(official_level: i, official_position: "Official position #{i}")
|
||||
end
|
||||
|
||||
(1..40).each do |i|
|
||||
(1..100).each do |i|
|
||||
user = create_user("user#{i}@consul.dev")
|
||||
level = [1, 2, 3].sample
|
||||
if level >= 2
|
||||
user.update(residence_verified_at: Time.current, confirmed_phone: Faker::PhoneNumber.phone_number, document_number: Faker::Number.number(10), document_type: "1")
|
||||
user.update(residence_verified_at: Time.current, confirmed_phone: Faker::PhoneNumber.phone_number, document_number: Faker::Number.number(10), document_type: "1", geozone: Geozone.reorder("RANDOM()").first)
|
||||
end
|
||||
if level == 3
|
||||
user.update(verified_at: Time.current, document_number: Faker::Number.number(10))
|
||||
@@ -285,7 +295,7 @@ puts " ✅"
|
||||
print "Voting Debates, Proposals & Comments"
|
||||
|
||||
100.times do
|
||||
voter = not_org_users.reorder("RANDOM()").first
|
||||
voter = not_org_users.level_two_or_three_verified.reorder("RANDOM()").first
|
||||
vote = [true, false].sample
|
||||
debate = Debate.reorder("RANDOM()").first
|
||||
debate.vote_by(voter: voter, vote: vote)
|
||||
@@ -299,7 +309,7 @@ end
|
||||
end
|
||||
|
||||
100.times do
|
||||
voter = User.level_two_or_three_verified.reorder("RANDOM()").first
|
||||
voter = not_org_users.level_two_or_three_verified.reorder("RANDOM()").first
|
||||
proposal = Proposal.reorder("RANDOM()").first
|
||||
proposal.vote_by(voter: voter, vote: true)
|
||||
end
|
||||
@@ -487,6 +497,16 @@ Proposal.last(3).each do |proposal|
|
||||
created_at: rand((Time.current - 1.week)..Time.current))
|
||||
end
|
||||
|
||||
puts " ✅"
|
||||
puts "Creating proposal notifications"
|
||||
|
||||
100.times do |i|
|
||||
ProposalNotification.create!(title: "Proposal notification title #{i}",
|
||||
body: "Proposal notification body #{i}",
|
||||
author: User.reorder("RANDOM()").first,
|
||||
proposal: Proposal.reorder("RANDOM()").first)
|
||||
end
|
||||
|
||||
puts " ✅"
|
||||
print "Creating polls"
|
||||
|
||||
|
||||
@@ -232,10 +232,10 @@ ActiveRecord::Schema.define(version: 20170613203256) do
|
||||
t.string "visit_id"
|
||||
t.datetime "hidden_at"
|
||||
t.integer "flags_count", default: 0
|
||||
t.datetime "ignored_flag_at"
|
||||
t.integer "cached_votes_total", default: 0
|
||||
t.integer "cached_votes_up", default: 0
|
||||
t.integer "cached_votes_down", default: 0
|
||||
t.datetime "ignored_flag_at"
|
||||
t.integer "comments_count", default: 0
|
||||
t.datetime "confirmed_hide_at"
|
||||
t.integer "cached_anonymous_votes_total", default: 0
|
||||
@@ -254,7 +254,6 @@ ActiveRecord::Schema.define(version: 20170613203256) do
|
||||
add_index "debates", ["cached_votes_total"], name: "index_debates_on_cached_votes_total", using: :btree
|
||||
add_index "debates", ["cached_votes_up"], name: "index_debates_on_cached_votes_up", using: :btree
|
||||
add_index "debates", ["confidence_score"], name: "index_debates_on_confidence_score", using: :btree
|
||||
add_index "debates", ["description"], name: "index_debates_on_description", using: :btree
|
||||
add_index "debates", ["geozone_id"], name: "index_debates_on_geozone_id", using: :btree
|
||||
add_index "debates", ["hidden_at"], name: "index_debates_on_hidden_at", using: :btree
|
||||
add_index "debates", ["hot_score"], name: "index_debates_on_hot_score", using: :btree
|
||||
@@ -718,7 +717,6 @@ ActiveRecord::Schema.define(version: 20170613203256) do
|
||||
add_index "proposals", ["author_id"], name: "index_proposals_on_author_id", using: :btree
|
||||
add_index "proposals", ["cached_votes_up"], name: "index_proposals_on_cached_votes_up", using: :btree
|
||||
add_index "proposals", ["confidence_score"], name: "index_proposals_on_confidence_score", using: :btree
|
||||
add_index "proposals", ["description"], name: "index_proposals_on_description", using: :btree
|
||||
add_index "proposals", ["geozone_id"], name: "index_proposals_on_geozone_id", using: :btree
|
||||
add_index "proposals", ["hidden_at"], name: "index_proposals_on_hidden_at", using: :btree
|
||||
add_index "proposals", ["hot_score"], name: "index_proposals_on_hot_score", using: :btree
|
||||
@@ -926,7 +924,7 @@ ActiveRecord::Schema.define(version: 20170613203256) do
|
||||
t.boolean "email_digest", default: true
|
||||
t.boolean "email_on_direct_message", default: true
|
||||
t.boolean "official_position_badge", default: false
|
||||
t.datetime "password_changed_at", default: '2016-11-23 10:59:20', null: false
|
||||
t.datetime "password_changed_at", default: '2016-12-21 17:55:08', null: false
|
||||
t.boolean "created_from_signature", default: false
|
||||
t.integer "failed_email_digests_count", default: 0
|
||||
t.text "former_users_data_log", default: ""
|
||||
|
||||
84
lib/graph_ql/api_types_creator.rb
Normal file
84
lib/graph_ql/api_types_creator.rb
Normal file
@@ -0,0 +1,84 @@
|
||||
require 'graphql'
|
||||
|
||||
module GraphQL
|
||||
class ApiTypesCreator
|
||||
SCALAR_TYPES = {
|
||||
integer: GraphQL::INT_TYPE,
|
||||
boolean: GraphQL::BOOLEAN_TYPE,
|
||||
float: GraphQL::FLOAT_TYPE,
|
||||
double: GraphQL::FLOAT_TYPE,
|
||||
string: GraphQL::STRING_TYPE
|
||||
}
|
||||
|
||||
def self.create(api_types_definitions)
|
||||
created_types = {}
|
||||
api_types_definitions.each do |model, info|
|
||||
create_type(model, info[:fields], created_types)
|
||||
end
|
||||
created_types
|
||||
end
|
||||
|
||||
def self.type_kind(type)
|
||||
if SCALAR_TYPES[type]
|
||||
:scalar
|
||||
elsif type.class == Class
|
||||
:singular_association
|
||||
elsif type.class == Array
|
||||
:multiple_association
|
||||
end
|
||||
end
|
||||
|
||||
def self.create_type(model, fields, created_types)
|
||||
|
||||
created_types[model] = GraphQL::ObjectType.define do
|
||||
|
||||
name model.graphql_type_name
|
||||
description model.graphql_type_description
|
||||
|
||||
# Make a field for each column, association or method
|
||||
fields.each do |field_name, field_type|
|
||||
case ApiTypesCreator.type_kind(field_type)
|
||||
when :scalar
|
||||
field(field_name, SCALAR_TYPES[field_type], model.human_attribute_name(field_name))
|
||||
when :singular_association
|
||||
field(field_name, -> { created_types[field_type] }) do
|
||||
resolve -> (object, arguments, context) do
|
||||
association_target = object.send(field_name)
|
||||
association_target.present? ? field_type.public_for_api.find_by(id: association_target.id) : nil
|
||||
end
|
||||
end
|
||||
when :multiple_association
|
||||
field_type = field_type.first
|
||||
connection(field_name, -> { created_types[field_type].connection_type }, max_page_size: 50, complexity: 1000) do
|
||||
resolve -> (object, arguments, context) { object.send(field_name).public_for_api }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def self.parse_api_config_file(file)
|
||||
api_type_definitions = {}
|
||||
|
||||
file.each do |api_type_model, api_type_info|
|
||||
model = api_type_model.constantize
|
||||
fields = {}
|
||||
|
||||
api_type_info['fields'].each do |field_name, field_type|
|
||||
if field_type.is_a?(Array) # paginated association
|
||||
fields[field_name.to_sym] = [field_type.first.constantize]
|
||||
elsif SCALAR_TYPES[field_type.to_sym]
|
||||
fields[field_name.to_sym] = field_type.to_sym
|
||||
else # simple association
|
||||
fields[field_name.to_sym] = field_type.constantize
|
||||
end
|
||||
end
|
||||
|
||||
api_type_definitions[model] = { fields: fields }
|
||||
end
|
||||
|
||||
api_type_definitions
|
||||
end
|
||||
end
|
||||
end
|
||||
31
lib/graph_ql/query_type_creator.rb
Normal file
31
lib/graph_ql/query_type_creator.rb
Normal file
@@ -0,0 +1,31 @@
|
||||
require 'graphql'
|
||||
|
||||
module GraphQL
|
||||
class QueryTypeCreator
|
||||
|
||||
def self.create(api_types)
|
||||
GraphQL::ObjectType.define do
|
||||
name 'QueryType'
|
||||
description 'The root query for the schema'
|
||||
|
||||
api_types.each do |model, created_type|
|
||||
if created_type.fields['id']
|
||||
field model.graphql_field_name do
|
||||
type created_type
|
||||
description model.graphql_field_description
|
||||
argument :id, !types.ID
|
||||
resolve -> (object, arguments, context) { model.public_for_api.find_by(id: arguments['id'])}
|
||||
end
|
||||
end
|
||||
|
||||
connection(model.graphql_pluralized_field_name, created_type.connection_type, max_page_size: 50, complexity: 1000) do
|
||||
description model.graphql_pluralized_field_description
|
||||
resolve -> (object, arguments, context) { model.public_for_api }
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
91
spec/controllers/graphql_controller_spec.rb
Normal file
91
spec/controllers/graphql_controller_spec.rb
Normal file
@@ -0,0 +1,91 @@
|
||||
require 'rails_helper'
|
||||
|
||||
# Useful resource: http://graphql.org/learn/serving-over-http/
|
||||
|
||||
def parser_error_raised?(response)
|
||||
data_is_empty = response['data'].nil?
|
||||
error_is_present = (JSON.parse(response.body)['errors'].first['message'] =~ /^Parse error on/)
|
||||
data_is_empty && error_is_present
|
||||
end
|
||||
|
||||
describe GraphqlController, type: :request do
|
||||
let(:proposal) { create(:proposal) }
|
||||
|
||||
describe "handles GET request" do
|
||||
specify "with query string inside query params" do
|
||||
get '/graphql', query: "{ proposal(id: #{proposal.id}) { title } }"
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(JSON.parse(response.body)['data']['proposal']['title']).to eq(proposal.title)
|
||||
end
|
||||
|
||||
specify "with malformed query string" do
|
||||
get '/graphql', query: 'Malformed query string'
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(parser_error_raised?(response)).to be_truthy
|
||||
end
|
||||
|
||||
specify "without query string" do
|
||||
get '/graphql'
|
||||
|
||||
expect(response).to have_http_status(:bad_request)
|
||||
expect(JSON.parse(response.body)['message']).to eq('Query string not present')
|
||||
end
|
||||
end
|
||||
|
||||
describe "handles POST request" do
|
||||
let(:json_headers) { { "CONTENT_TYPE" => "application/json" } }
|
||||
|
||||
specify "with json-encoded query string inside body" do
|
||||
post '/graphql', { query: "{ proposal(id: #{proposal.id}) { title } }" }.to_json, json_headers
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(JSON.parse(response.body)['data']['proposal']['title']).to eq(proposal.title)
|
||||
end
|
||||
|
||||
specify "with raw query string inside body" do
|
||||
graphql_headers = { "CONTENT_TYPE" => "application/graphql" }
|
||||
post '/graphql', "{ proposal(id: #{proposal.id}) { title } }", graphql_headers
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(JSON.parse(response.body)['data']['proposal']['title']).to eq(proposal.title)
|
||||
end
|
||||
|
||||
specify "with malformed query string" do
|
||||
post '/graphql', { query: "Malformed query string" }.to_json, json_headers
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(parser_error_raised?(response)).to be_truthy
|
||||
end
|
||||
|
||||
it "without query string" do
|
||||
post '/graphql', json_headers
|
||||
|
||||
expect(response).to have_http_status(:bad_request)
|
||||
expect(JSON.parse(response.body)['message']).to eq('Query string not present')
|
||||
end
|
||||
end
|
||||
|
||||
describe "correctly parses query variables" do
|
||||
let(:query_string) { "{ proposal(id: #{proposal.id}) { title } }" }
|
||||
|
||||
specify "when absent" do
|
||||
get '/graphql', { query: query_string }
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
end
|
||||
|
||||
specify "when specified as the 'null' string" do
|
||||
get '/graphql', { query: query_string, variables: 'null' }
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
end
|
||||
|
||||
specify "when specified as an empty string" do
|
||||
get '/graphql', { query: query_string, variables: '' }
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -8,6 +8,7 @@ FactoryGirl.define do
|
||||
password 'judgmentday'
|
||||
terms_of_service '1'
|
||||
confirmed_at { Time.current }
|
||||
public_activity true
|
||||
|
||||
trait :incomplete_verification do
|
||||
after :create do |user|
|
||||
|
||||
@@ -86,15 +86,18 @@ feature 'Admin legislation draft versions' do
|
||||
|
||||
click_link 'Version 1'
|
||||
|
||||
fill_in 'legislation_draft_version_title', with: 'Version 1b'
|
||||
|
||||
click_link 'Launch text editor'
|
||||
|
||||
fill_in 'legislation_draft_version_title', with: 'Version 1b'
|
||||
fill_in 'legislation_draft_version_body', with: '# Version 1 body\r\n\r\nParagraph\r\n\r\n>Quote'
|
||||
|
||||
within('.fullscreen') do
|
||||
click_button 'Save changes'
|
||||
click_link 'Close text editor'
|
||||
end
|
||||
|
||||
click_button 'Save changes'
|
||||
|
||||
expect(page).to have_content 'Version 1b'
|
||||
end
|
||||
end
|
||||
|
||||
@@ -97,7 +97,7 @@ feature 'Proposals' do
|
||||
|
||||
expect(current_path).to eq(management_proposals_path)
|
||||
|
||||
within("#proposals") do
|
||||
within(".proposals-list") do
|
||||
expect(page).to have_css('.proposal', count: 1)
|
||||
expect(page).to have_content(proposal1.title)
|
||||
expect(page).to have_content(proposal1.summary)
|
||||
@@ -124,7 +124,7 @@ feature 'Proposals' do
|
||||
expect(page).to have_content "#{user.document_number}"
|
||||
end
|
||||
|
||||
within("#proposals") do
|
||||
within(".proposals-list") do
|
||||
expect(page).to have_css('.proposal', count: 2)
|
||||
expect(page).to have_css("a[href='#{management_proposal_path(proposal1)}']", text: proposal1.title)
|
||||
expect(page).to have_content(proposal1.summary)
|
||||
@@ -143,7 +143,7 @@ feature 'Proposals' do
|
||||
|
||||
click_link "Support proposals"
|
||||
|
||||
within("#proposals") do
|
||||
within(".proposals-list") do
|
||||
find('.in-favor a').click
|
||||
|
||||
expect(page).to have_content "1 support"
|
||||
@@ -160,7 +160,7 @@ feature 'Proposals' do
|
||||
|
||||
click_link "Support proposals"
|
||||
|
||||
within("#proposals") do
|
||||
within(".proposals-list") do
|
||||
click_link proposal.title
|
||||
end
|
||||
|
||||
@@ -205,7 +205,7 @@ feature 'Proposals' do
|
||||
|
||||
expect(page).to have_selector('.js-order-selector[data-order="confidence_score"]')
|
||||
|
||||
within '#proposals' do
|
||||
within(".proposals-list") do
|
||||
expect('Best proposal').to appear_before('Medium proposal')
|
||||
expect('Medium proposal').to appear_before('Worst proposal')
|
||||
end
|
||||
@@ -217,7 +217,7 @@ feature 'Proposals' do
|
||||
expect(current_url).to include('order=created_at')
|
||||
expect(current_url).to include('page=1')
|
||||
|
||||
within '#proposals' do
|
||||
within(".proposals-list") do
|
||||
expect('Medium proposal').to appear_before('Best proposal')
|
||||
expect('Best proposal').to appear_before('Worst proposal')
|
||||
end
|
||||
|
||||
@@ -64,6 +64,75 @@ describe 'ActsAsTaggableOn' do
|
||||
expect(tag.proposals_count).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
describe "public_for_api scope" do
|
||||
|
||||
it "returns tags whose kind is NULL and have at least one tagging whose taggable is not hidden" do
|
||||
tag = create(:tag, kind: nil)
|
||||
proposal = create(:proposal)
|
||||
proposal.tag_list.add(tag)
|
||||
proposal.save
|
||||
|
||||
expect(ActsAsTaggableOn::Tag.public_for_api).to include(tag)
|
||||
end
|
||||
|
||||
it "returns tags whose kind is 'category' and have at least one tagging whose taggable is not hidden" do
|
||||
tag = create(:tag, kind: 'category')
|
||||
proposal = create(:proposal)
|
||||
proposal.tag_list.add(tag)
|
||||
proposal.save
|
||||
|
||||
expect(ActsAsTaggableOn::Tag.public_for_api).to include(tag)
|
||||
end
|
||||
|
||||
it "blocks other kinds of tags" do
|
||||
tag = create(:tag, kind: 'foo')
|
||||
proposal = create(:proposal)
|
||||
proposal.tag_list.add(tag)
|
||||
proposal.save
|
||||
|
||||
expect(ActsAsTaggableOn::Tag.public_for_api).not_to include(tag)
|
||||
end
|
||||
|
||||
it "blocks tags that don't have at least one tagged element" do
|
||||
tag = create(:tag)
|
||||
|
||||
expect(ActsAsTaggableOn::Tag.public_for_api).to_not include(tag)
|
||||
end
|
||||
|
||||
it 'only permits tags on proposals or debates' do
|
||||
tag_1 = create(:tag)
|
||||
tag_2 = create(:tag)
|
||||
tag_3 = create(:tag)
|
||||
|
||||
proposal = create(:proposal)
|
||||
spending_proposal = create(:spending_proposal)
|
||||
debate = create(:debate)
|
||||
|
||||
proposal.tag_list.add(tag_1)
|
||||
spending_proposal.tag_list.add(tag_2)
|
||||
debate.tag_list.add(tag_3)
|
||||
|
||||
proposal.save
|
||||
spending_proposal.save
|
||||
debate.save
|
||||
|
||||
expect(ActsAsTaggableOn::Tag.public_for_api).to match_array([tag_1, tag_3])
|
||||
end
|
||||
|
||||
it 'blocks tags after its taggings became hidden' do
|
||||
tag = create(:tag)
|
||||
proposal = create(:proposal)
|
||||
proposal.tag_list.add(tag)
|
||||
proposal.save
|
||||
|
||||
expect(ActsAsTaggableOn::Tag.public_for_api).to include(tag)
|
||||
|
||||
proposal.delete
|
||||
|
||||
expect(ActsAsTaggableOn::Tag.public_for_api).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
58
spec/lib/graph_ql/api_types_creator_spec.rb
Normal file
58
spec/lib/graph_ql/api_types_creator_spec.rb
Normal file
@@ -0,0 +1,58 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe GraphQL::ApiTypesCreator do
|
||||
let(:created_types) { {} }
|
||||
|
||||
describe "::create_type" do
|
||||
it "creates fields for Int attributes" do
|
||||
debate_type = GraphQL::ApiTypesCreator.create_type(Debate, { id: :integer }, created_types)
|
||||
created_field = debate_type.fields['id']
|
||||
|
||||
expect(created_field).to be_a(GraphQL::Field)
|
||||
expect(created_field.type).to be_a(GraphQL::ScalarType)
|
||||
expect(created_field.type.name).to eq('Int')
|
||||
end
|
||||
|
||||
it "creates fields for String attributes" do
|
||||
debate_type = GraphQL::ApiTypesCreator.create_type(Debate, { title: :string }, created_types)
|
||||
created_field = debate_type.fields['title']
|
||||
|
||||
expect(created_field).to be_a(GraphQL::Field)
|
||||
expect(created_field.type).to be_a(GraphQL::ScalarType)
|
||||
expect(created_field.type.name).to eq('String')
|
||||
end
|
||||
|
||||
it "creates connections for :belongs_to associations" do
|
||||
user_type = GraphQL::ApiTypesCreator.create_type(User, { id: :integer }, created_types)
|
||||
debate_type = GraphQL::ApiTypesCreator.create_type(Debate, { author: User }, created_types)
|
||||
|
||||
connection = debate_type.fields['author']
|
||||
|
||||
expect(connection).to be_a(GraphQL::Field)
|
||||
expect(connection.type).to eq(user_type)
|
||||
expect(connection.name).to eq('author')
|
||||
end
|
||||
|
||||
it "creates connections for :has_one associations" do
|
||||
user_type = GraphQL::ApiTypesCreator.create_type(User, { organization: Organization }, created_types)
|
||||
organization_type = GraphQL::ApiTypesCreator.create_type(Organization, { id: :integer }, created_types)
|
||||
|
||||
connection = user_type.fields['organization']
|
||||
|
||||
expect(connection).to be_a(GraphQL::Field)
|
||||
expect(connection.type).to eq(organization_type)
|
||||
expect(connection.name).to eq('organization')
|
||||
end
|
||||
|
||||
it "creates connections for :has_many associations" do
|
||||
comment_type = GraphQL::ApiTypesCreator.create_type(Comment, { id: :integer }, created_types)
|
||||
debate_type = GraphQL::ApiTypesCreator.create_type(Debate, { comments: [Comment] }, created_types)
|
||||
|
||||
connection = debate_type.fields['comments']
|
||||
|
||||
expect(connection).to be_a(GraphQL::Field)
|
||||
expect(connection.type).to eq(comment_type.connection_type)
|
||||
expect(connection.name).to eq('comments')
|
||||
end
|
||||
end
|
||||
end
|
||||
35
spec/lib/graph_ql/query_type_creator_spec.rb
Normal file
35
spec/lib/graph_ql/query_type_creator_spec.rb
Normal file
@@ -0,0 +1,35 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe GraphQL::QueryTypeCreator do
|
||||
let(:api_type_definitions) do
|
||||
{
|
||||
ProposalNotification => { fields: { title: :string } },
|
||||
Proposal => { fields: { id: :integer, title: :string } }
|
||||
}
|
||||
end
|
||||
let(:api_types) { GraphQL::ApiTypesCreator.create(api_type_definitions) }
|
||||
|
||||
describe "::create" do
|
||||
let(:query_type) { GraphQL::QueryTypeCreator.create(api_types) }
|
||||
|
||||
it 'creates a QueryType with fields to retrieve single objects whose model fields included an ID' do
|
||||
field = query_type.fields['proposal']
|
||||
|
||||
expect(field).to be_a(GraphQL::Field)
|
||||
expect(field.type).to eq(api_types[Proposal])
|
||||
expect(field.name).to eq('proposal')
|
||||
end
|
||||
|
||||
it 'creates a QueryType without fields to retrieve single objects whose model fields did not include an ID' do
|
||||
expect(query_type.fields['proposal_notification']).to be_nil
|
||||
end
|
||||
|
||||
it "creates a QueryType with connections to retrieve collections of objects" do
|
||||
connection = query_type.fields['proposals']
|
||||
|
||||
expect(connection).to be_a(GraphQL::Field)
|
||||
expect(connection.type).to eq(api_types[Proposal].connection_type)
|
||||
expect(connection.name).to eq('proposals')
|
||||
end
|
||||
end
|
||||
end
|
||||
674
spec/lib/graphql_spec.rb
Normal file
674
spec/lib/graphql_spec.rb
Normal file
@@ -0,0 +1,674 @@
|
||||
require 'rails_helper'
|
||||
|
||||
api_types = GraphQL::ApiTypesCreator.create(API_TYPE_DEFINITIONS)
|
||||
query_type = GraphQL::QueryTypeCreator.create(api_types)
|
||||
ConsulSchema = GraphQL::Schema.define do
|
||||
query query_type
|
||||
max_depth 12
|
||||
end
|
||||
|
||||
def execute(query_string, context = {}, variables = {})
|
||||
ConsulSchema.execute(query_string, context: context, variables: variables)
|
||||
end
|
||||
|
||||
def dig(response, path)
|
||||
response.dig(*path.split('.'))
|
||||
end
|
||||
|
||||
def hidden_field?(response, field_name)
|
||||
data_is_empty = response['data'].nil?
|
||||
error_is_present = ((response['errors'].first['message'] =~ /Field '#{field_name}' doesn't exist on type '[[:alnum:]]*'/) == 0)
|
||||
data_is_empty && error_is_present
|
||||
end
|
||||
|
||||
def extract_fields(response, collection_name, field_chain)
|
||||
fields = field_chain.split('.')
|
||||
dig(response, "data.#{collection_name}.edges").collect do |node|
|
||||
begin
|
||||
if fields.size > 1
|
||||
node['node'][fields.first][fields.second]
|
||||
else
|
||||
node['node'][fields.first]
|
||||
end
|
||||
rescue NoMethodError
|
||||
end
|
||||
end.compact
|
||||
end
|
||||
|
||||
describe 'ConsulSchema' do
|
||||
let(:user) { create(:user) }
|
||||
let(:proposal) { create(:proposal, author: user) }
|
||||
|
||||
it 'returns fields of Int type' do
|
||||
response = execute("{ proposal(id: #{proposal.id}) { id } }")
|
||||
expect(dig(response, 'data.proposal.id')).to eq(proposal.id)
|
||||
end
|
||||
|
||||
it 'returns fields of String type' do
|
||||
response = execute("{ proposal(id: #{proposal.id}) { title } }")
|
||||
expect(dig(response, 'data.proposal.title')).to eq(proposal.title)
|
||||
end
|
||||
|
||||
xit 'returns has_one associations' do
|
||||
organization = create(:organization)
|
||||
response = execute("{ user(id: #{organization.user_id}) { organization { name } } }")
|
||||
expect(dig(response, 'data.user.organization.name')).to eq(organization.name)
|
||||
end
|
||||
|
||||
it 'returns belongs_to associations' do
|
||||
response = execute("{ proposal(id: #{proposal.id}) { public_author { username } } }")
|
||||
expect(dig(response, 'data.proposal.public_author.username')).to eq(proposal.public_author.username)
|
||||
end
|
||||
|
||||
it 'returns has_many associations' do
|
||||
comments_author = create(:user)
|
||||
comment_1 = create(:comment, author: comments_author, commentable: proposal)
|
||||
comment_2 = create(:comment, author: comments_author, commentable: proposal)
|
||||
|
||||
response = execute("{ proposal(id: #{proposal.id}) { comments { edges { node { body } } } } }")
|
||||
comments = dig(response, 'data.proposal.comments.edges').collect { |edge| edge['node'] }
|
||||
comment_bodies = comments.collect { |comment| comment['body'] }
|
||||
|
||||
expect(comment_bodies).to match_array([comment_1.body, comment_2.body])
|
||||
end
|
||||
|
||||
xit 'executes deeply nested queries' do
|
||||
org_user = create(:user)
|
||||
organization = create(:organization, user: org_user)
|
||||
org_proposal = create(:proposal, author: org_user)
|
||||
response = execute("{ proposal(id: #{org_proposal.id}) { public_author { organization { name } } } }")
|
||||
|
||||
expect(dig(response, 'data.proposal.public_author.organization.name')).to eq(organization.name)
|
||||
end
|
||||
|
||||
it 'hides confidential fields of Int type' do
|
||||
response = execute("{ user(id: #{user.id}) { failed_census_calls_count } }")
|
||||
expect(hidden_field?(response, 'failed_census_calls_count')).to be_truthy
|
||||
end
|
||||
|
||||
it 'hides confidential fields of String type' do
|
||||
response = execute("{ user(id: #{user.id}) { encrypted_password } }")
|
||||
expect(hidden_field?(response, 'encrypted_password')).to be_truthy
|
||||
end
|
||||
|
||||
xit 'hides confidential has_one associations' do
|
||||
user.administrator = create(:administrator)
|
||||
response = execute("{ user(id: #{user.id}) { administrator { id } } }")
|
||||
expect(hidden_field?(response, 'administrator')).to be_truthy
|
||||
end
|
||||
|
||||
it 'hides confidential belongs_to associations' do
|
||||
create(:failed_census_call, user: user)
|
||||
response = execute("{ user(id: #{user.id}) { failed_census_calls { id } } }")
|
||||
expect(hidden_field?(response, 'failed_census_calls')).to be_truthy
|
||||
end
|
||||
|
||||
it 'hides confidential has_many associations' do
|
||||
create(:direct_message, sender: user)
|
||||
response = execute("{ user(id: #{user.id}) { direct_messages_sent { id } } }")
|
||||
expect(hidden_field?(response, 'direct_messages_sent')).to be_truthy
|
||||
end
|
||||
|
||||
it 'hides confidential fields inside deeply nested queries' do
|
||||
response = execute("{ proposals(first: 1) { edges { node { public_author { encrypted_password } } } } }")
|
||||
expect(hidden_field?(response, 'encrypted_password')).to be_truthy
|
||||
end
|
||||
|
||||
describe 'Users' do
|
||||
let(:user) { create(:user, public_activity: false) }
|
||||
|
||||
it 'does not link debates if activity is not public' do
|
||||
create(:debate, author: user)
|
||||
|
||||
response = execute("{ user(id: #{user.id}) { public_debates { edges { node { title } } } } }")
|
||||
received_debates = dig(response, 'data.user.public_debates.edges')
|
||||
|
||||
expect(received_debates).to eq []
|
||||
end
|
||||
|
||||
it 'does not link proposals if activity is not public' do
|
||||
create(:proposal, author: user)
|
||||
|
||||
response = execute("{ user(id: #{user.id}) { public_proposals { edges { node { title } } } } }")
|
||||
received_proposals = dig(response, 'data.user.public_proposals.edges')
|
||||
|
||||
expect(received_proposals).to eq []
|
||||
end
|
||||
|
||||
it 'does not link comments if activity is not public' do
|
||||
create(:comment, author: user)
|
||||
|
||||
response = execute("{ user(id: #{user.id}) { public_comments { edges { node { body } } } } }")
|
||||
received_comments = dig(response, 'data.user.public_comments.edges')
|
||||
|
||||
expect(received_comments).to eq []
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe 'Proposals' do
|
||||
it 'does not include hidden proposals' do
|
||||
visible_proposal = create(:proposal)
|
||||
hidden_proposal = create(:proposal, :hidden)
|
||||
|
||||
response = execute('{ proposals { edges { node { title } } } }')
|
||||
received_titles = extract_fields(response, 'proposals', 'title')
|
||||
|
||||
expect(received_titles).to match_array [visible_proposal.title]
|
||||
end
|
||||
|
||||
xit 'only returns proposals of the Human Rights proceeding' do
|
||||
proposal = create(:proposal)
|
||||
human_rights_proposal = create(:proposal, proceeding: 'Derechos Humanos', sub_proceeding: 'Right to have a job')
|
||||
other_proceeding_proposal = create(:proposal)
|
||||
other_proceeding_proposal.update_attribute(:proceeding, 'Another proceeding')
|
||||
|
||||
response = execute('{ proposals { edges { node { title } } } }')
|
||||
received_titles = extract_fields(response, 'proposals', 'title')
|
||||
|
||||
expect(received_titles).to match_array [proposal.title, human_rights_proposal.title]
|
||||
end
|
||||
|
||||
it 'includes proposals of authors even if public activity is set to false' do
|
||||
visible_author = create(:user, public_activity: true)
|
||||
hidden_author = create(:user, public_activity: false)
|
||||
|
||||
visible_proposal = create(:proposal, author: visible_author)
|
||||
hidden_proposal = create(:proposal, author: hidden_author)
|
||||
|
||||
response = execute('{ proposals { edges { node { title } } } }')
|
||||
received_titles = extract_fields(response, 'proposals', 'title')
|
||||
|
||||
expect(received_titles).to match_array [visible_proposal.title, hidden_proposal.title]
|
||||
end
|
||||
|
||||
it 'does not link author if public activity is set to false' do
|
||||
visible_author = create(:user, public_activity: true)
|
||||
hidden_author = create(:user, public_activity: false)
|
||||
|
||||
visible_proposal = create(:proposal, author: visible_author)
|
||||
hidden_proposal = create(:proposal, author: hidden_author)
|
||||
|
||||
response = execute('{ proposals { edges { node { public_author { username } } } } }')
|
||||
received_authors = extract_fields(response, 'proposals', 'public_author.username')
|
||||
|
||||
expect(received_authors).to match_array [visible_author.username]
|
||||
end
|
||||
|
||||
it 'only returns date and hour for created_at' do
|
||||
created_at = Time.zone.parse("2017-12-31 9:30:15")
|
||||
create(:proposal, created_at: created_at)
|
||||
|
||||
response = execute('{ proposals { edges { node { public_created_at } } } }')
|
||||
received_timestamps = extract_fields(response, 'proposals', 'public_created_at')
|
||||
|
||||
expect(Time.zone.parse(received_timestamps.first)).to eq Time.zone.parse("2017-12-31 9:00:00")
|
||||
end
|
||||
|
||||
it 'only retruns tags with kind nil or category' do
|
||||
tag = create(:tag, name: 'Parks')
|
||||
category_tag = create(:tag, name: 'Health', kind: 'category')
|
||||
admin_tag = create(:tag, name: 'Admin tag', kind: 'admin')
|
||||
|
||||
proposal = create(:proposal, tag_list: 'Parks, Health, Admin tag')
|
||||
|
||||
response = execute("{ proposal(id: #{proposal.id}) { tags { edges { node { name } } } } }")
|
||||
received_tags = dig(response, 'data.proposal.tags.edges').map { |node| node['node']['name'] }
|
||||
|
||||
expect(received_tags).to match_array ['Parks', 'Health']
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe 'Debates' do
|
||||
it 'does not include hidden debates' do
|
||||
visible_debate = create(:debate)
|
||||
hidden_debate = create(:debate, :hidden)
|
||||
|
||||
response = execute('{ debates { edges { node { title } } } }')
|
||||
received_titles = extract_fields(response, 'debates', 'title')
|
||||
|
||||
expect(received_titles).to match_array [visible_debate.title]
|
||||
end
|
||||
|
||||
it 'includes debates of authors even if public activity is set to false' do
|
||||
visible_author = create(:user, public_activity: true)
|
||||
hidden_author = create(:user, public_activity: false)
|
||||
|
||||
visible_debate = create(:debate, author: visible_author)
|
||||
hidden_debate = create(:debate, author: hidden_author)
|
||||
|
||||
response = execute('{ debates { edges { node { title } } } }')
|
||||
received_titles = extract_fields(response, 'debates', 'title')
|
||||
|
||||
expect(received_titles).to match_array [visible_debate.title, hidden_debate.title]
|
||||
end
|
||||
|
||||
it 'does not link author if public activity is set to false' do
|
||||
visible_author = create(:user, public_activity: true)
|
||||
hidden_author = create(:user, public_activity: false)
|
||||
|
||||
visible_debate = create(:debate, author: visible_author)
|
||||
hidden_debate = create(:debate, author: hidden_author)
|
||||
|
||||
response = execute('{ debates { edges { node { public_author { username } } } } }')
|
||||
received_authors = extract_fields(response, 'debates', 'public_author.username')
|
||||
|
||||
expect(received_authors).to match_array [visible_author.username]
|
||||
end
|
||||
|
||||
it 'only returns date and hour for created_at' do
|
||||
created_at = Time.zone.parse("2017-12-31 9:30:15")
|
||||
create(:debate, created_at: created_at)
|
||||
|
||||
response = execute('{ debates { edges { node { public_created_at } } } }')
|
||||
received_timestamps = extract_fields(response, 'debates', 'public_created_at')
|
||||
|
||||
expect(Time.zone.parse(received_timestamps.first)).to eq Time.zone.parse("2017-12-31 9:00:00")
|
||||
end
|
||||
|
||||
it 'only retruns tags with kind nil or category' do
|
||||
tag = create(:tag, name: 'Parks')
|
||||
category_tag = create(:tag, name: 'Health', kind: 'category')
|
||||
admin_tag = create(:tag, name: 'Admin tag', kind: 'admin')
|
||||
|
||||
debate = create(:debate, tag_list: 'Parks, Health, Admin tag')
|
||||
|
||||
response = execute("{ debate(id: #{debate.id}) { tags { edges { node { name } } } } }")
|
||||
received_tags = dig(response, 'data.debate.tags.edges').map { |node| node['node']['name'] }
|
||||
|
||||
expect(received_tags).to match_array ['Parks', 'Health']
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Comments' do
|
||||
it 'only returns comments from proposals and debates' do
|
||||
proposal_comment = create(:comment, commentable: create(:proposal))
|
||||
debate_comment = create(:comment, commentable: create(:debate))
|
||||
spending_proposal_comment = build(:comment, commentable: create(:spending_proposal)).save(skip_validation: true)
|
||||
|
||||
response = execute('{ comments { edges { node { commentable_type } } } }')
|
||||
received_commentables = extract_fields(response, 'comments', 'commentable_type')
|
||||
|
||||
expect(received_commentables).to match_array ['Proposal', 'Debate']
|
||||
end
|
||||
|
||||
it 'displays comments of authors even if public activity is set to false' do
|
||||
visible_author = create(:user, public_activity: true)
|
||||
hidden_author = create(:user, public_activity: false)
|
||||
|
||||
visible_comment = create(:comment, user: visible_author)
|
||||
hidden_comment = create(:comment, user: hidden_author)
|
||||
|
||||
response = execute('{ comments { edges { node { body } } } }')
|
||||
received_comments = extract_fields(response, 'comments', 'body')
|
||||
|
||||
expect(received_comments).to match_array [visible_comment.body, hidden_comment.body]
|
||||
end
|
||||
|
||||
it 'does not link author if public activity is set to false' do
|
||||
visible_author = create(:user, public_activity: true)
|
||||
hidden_author = create(:user, public_activity: false)
|
||||
|
||||
visible_comment = create(:comment, author: visible_author)
|
||||
hidden_comment = create(:comment, author: hidden_author)
|
||||
|
||||
response = execute('{ comments { edges { node { public_author { username } } } } }')
|
||||
received_authors = extract_fields(response, 'comments', 'public_author.username')
|
||||
|
||||
expect(received_authors).to match_array [visible_author.username]
|
||||
end
|
||||
|
||||
it 'does not include hidden comments' do
|
||||
visible_comment = create(:comment)
|
||||
hidden_comment = create(:comment, hidden_at: Time.now)
|
||||
|
||||
response = execute('{ comments { edges { node { body } } } }')
|
||||
received_comments = extract_fields(response, 'comments', 'body')
|
||||
|
||||
expect(received_comments).to match_array [visible_comment.body]
|
||||
end
|
||||
|
||||
it 'does not include comments from hidden proposals' do
|
||||
visible_proposal = create(:proposal)
|
||||
hidden_proposal = create(:proposal, hidden_at: Time.now)
|
||||
|
||||
visible_proposal_comment = create(:comment, commentable: visible_proposal)
|
||||
hidden_proposal_comment = create(:comment, commentable: hidden_proposal)
|
||||
|
||||
response = execute('{ comments { edges { node { body } } } }')
|
||||
received_comments = extract_fields(response, 'comments', 'body')
|
||||
|
||||
expect(received_comments).to match_array [visible_proposal_comment.body]
|
||||
end
|
||||
|
||||
it 'does not include comments from hidden debates' do
|
||||
visible_debate = create(:debate)
|
||||
hidden_debate = create(:debate, hidden_at: Time.now)
|
||||
|
||||
visible_debate_comment = create(:comment, commentable: visible_debate)
|
||||
hidden_debate_comment = create(:comment, commentable: hidden_debate)
|
||||
|
||||
response = execute('{ comments { edges { node { body } } } }')
|
||||
received_comments = extract_fields(response, 'comments', 'body')
|
||||
|
||||
expect(received_comments).to match_array [visible_debate_comment.body]
|
||||
end
|
||||
|
||||
it 'does not include comments of debates that are not public' do
|
||||
not_public_debate = create(:debate, :hidden)
|
||||
not_public_debate_comment = create(:comment, commentable: not_public_debate)
|
||||
allow(Comment).to receive(:public_for_api).and_return([])
|
||||
|
||||
response = execute('{ comments { edges { node { body } } } }')
|
||||
received_comments = extract_fields(response, 'comments', 'body')
|
||||
|
||||
expect(received_comments).to_not include(not_public_debate_comment.body)
|
||||
end
|
||||
|
||||
it 'does not include comments of proposals that are not public' do
|
||||
not_public_proposal = create(:proposal)
|
||||
not_public_proposal_comment = create(:comment, commentable: not_public_proposal)
|
||||
allow(Comment).to receive(:public_for_api).and_return([])
|
||||
|
||||
response = execute('{ comments { edges { node { body } } } }')
|
||||
received_comments = extract_fields(response, 'comments', 'body')
|
||||
|
||||
expect(received_comments).to_not include(not_public_proposal_comment.body)
|
||||
end
|
||||
|
||||
it 'only returns date and hour for created_at' do
|
||||
created_at = Time.zone.parse("2017-12-31 9:30:15")
|
||||
create(:comment, created_at: created_at)
|
||||
|
||||
response = execute('{ comments { edges { node { public_created_at } } } }')
|
||||
received_timestamps = extract_fields(response, 'comments', 'public_created_at')
|
||||
|
||||
expect(Time.zone.parse(received_timestamps.first)).to eq Time.zone.parse("2017-12-31 9:00:00")
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Geozones' do
|
||||
it 'returns geozones' do
|
||||
geozone_names = [ create(:geozone), create(:geozone) ].map { |geozone| geozone.name }
|
||||
|
||||
response = execute('{ geozones { edges { node { name } } } }')
|
||||
received_names = extract_fields(response, 'geozones', 'name')
|
||||
|
||||
expect(received_names).to match_array geozone_names
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Proposal notifications' do
|
||||
|
||||
it 'does not include proposal notifications for hidden proposals' do
|
||||
visible_proposal = create(:proposal)
|
||||
hidden_proposal = create(:proposal, :hidden)
|
||||
|
||||
visible_proposal_notification = create(:proposal_notification, proposal: visible_proposal)
|
||||
hidden_proposal_notification = create(:proposal_notification, proposal: hidden_proposal)
|
||||
|
||||
response = execute('{ proposal_notifications { edges { node { title } } } }')
|
||||
received_notifications = extract_fields(response, 'proposal_notifications', 'title')
|
||||
|
||||
expect(received_notifications).to match_array [visible_proposal_notification.title]
|
||||
end
|
||||
|
||||
it 'does not include proposal notifications for proposals that are not public' do
|
||||
not_public_proposal = create(:proposal)
|
||||
not_public_proposal_notification = create(:proposal_notification, proposal: not_public_proposal)
|
||||
allow(ProposalNotification).to receive(:public_for_api).and_return([])
|
||||
|
||||
response = execute('{ proposal_notifications { edges { node { title } } } }')
|
||||
received_notifications = extract_fields(response, 'proposal_notifications', 'title')
|
||||
|
||||
expect(received_notifications).to_not include(not_public_proposal_notification.title)
|
||||
end
|
||||
|
||||
it 'only returns date and hour for created_at' do
|
||||
created_at = Time.zone.parse("2017-12-31 9:30:15")
|
||||
create(:proposal_notification, created_at: created_at)
|
||||
|
||||
response = execute('{ proposal_notifications { edges { node { public_created_at } } } }')
|
||||
received_timestamps = extract_fields(response, 'proposal_notifications', 'public_created_at')
|
||||
|
||||
expect(Time.zone.parse(received_timestamps.first)).to eq Time.zone.parse("2017-12-31 9:00:00")
|
||||
end
|
||||
|
||||
it 'only links proposal if public' do
|
||||
visible_proposal = create(:proposal)
|
||||
hidden_proposal = create(:proposal, :hidden)
|
||||
|
||||
visible_proposal_notification = create(:proposal_notification, proposal: visible_proposal)
|
||||
hidden_proposal_notification = create(:proposal_notification, proposal: hidden_proposal)
|
||||
|
||||
response = execute('{ proposal_notifications { edges { node { proposal { title } } } } }')
|
||||
received_proposals = extract_fields(response, 'proposal_notifications', 'proposal.title')
|
||||
|
||||
expect(received_proposals).to match_array [visible_proposal.title]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe 'Tags' do
|
||||
it 'only display tags with kind nil or category' do
|
||||
tag = create(:tag, name: 'Parks')
|
||||
category_tag = create(:tag, name: 'Health', kind: 'category')
|
||||
admin_tag = create(:tag, name: 'Admin tag', kind: 'admin')
|
||||
|
||||
proposal = create(:proposal, tag_list: 'Parks')
|
||||
proposal = create(:proposal, tag_list: 'Health')
|
||||
proposal = create(:proposal, tag_list: 'Admin tag')
|
||||
|
||||
response = execute('{ tags { edges { node { name } } } }')
|
||||
received_tags = extract_fields(response, 'tags', 'name')
|
||||
|
||||
expect(received_tags).to match_array ['Parks', 'Health']
|
||||
end
|
||||
|
||||
xit 'uppercase and lowercase tags work ok together for proposals' do
|
||||
create(:tag, name: 'Health')
|
||||
create(:tag, name: 'health')
|
||||
create(:proposal, tag_list: 'health')
|
||||
create(:proposal, tag_list: 'Health')
|
||||
|
||||
response = execute('{ tags { edges { node { name } } } }')
|
||||
received_tags = extract_fields(response, 'tags', 'name')
|
||||
|
||||
expect(received_tags).to match_array ['Health', 'health']
|
||||
end
|
||||
|
||||
xit 'uppercase and lowercase tags work ok together for debates' do
|
||||
create(:tag, name: 'Health')
|
||||
create(:tag, name: 'health')
|
||||
create(:debate, tag_list: 'Health')
|
||||
create(:debate, tag_list: 'health')
|
||||
|
||||
response = execute('{ tags { edges { node { name } } } }')
|
||||
received_tags = extract_fields(response, 'tags', 'name')
|
||||
|
||||
expect(received_tags).to match_array ['Health', 'health']
|
||||
end
|
||||
|
||||
it 'does not display tags for hidden proposals' do
|
||||
proposal = create(:proposal, tag_list: 'Health')
|
||||
hidden_proposal = create(:proposal, :hidden, tag_list: 'SPAM')
|
||||
|
||||
response = execute('{ tags { edges { node { name } } } }')
|
||||
received_tags = extract_fields(response, 'tags', 'name')
|
||||
|
||||
expect(received_tags).to match_array ['Health']
|
||||
end
|
||||
|
||||
it 'does not display tags for hidden debates' do
|
||||
debate = create(:debate, tag_list: 'Health, Transportation')
|
||||
hidden_debate = create(:debate, :hidden, tag_list: 'SPAM')
|
||||
|
||||
response = execute('{ tags { edges { node { name } } } }')
|
||||
received_tags = extract_fields(response, 'tags', 'name')
|
||||
|
||||
expect(received_tags).to match_array ['Health', 'Transportation']
|
||||
end
|
||||
|
||||
xit "does not display tags for proceeding's proposals" do
|
||||
valid_proceeding_proposal = create(:proposal, proceeding: 'Derechos Humanos', sub_proceeding: 'Right to a Home', tag_list: 'Health')
|
||||
invalid_proceeding_proposal = create(:proposal, tag_list: 'Animals')
|
||||
invalid_proceeding_proposal.update_attribute('proceeding', 'Random')
|
||||
|
||||
response = execute('{ tags { edges { node { name } } } }')
|
||||
received_tags = extract_fields(response, 'tags', 'name')
|
||||
|
||||
expect(received_tags).to match_array ['Health']
|
||||
end
|
||||
|
||||
it 'does not display tags for taggings that are not public' do
|
||||
proposal = create(:proposal, tag_list: 'Health')
|
||||
allow(ActsAsTaggableOn::Tag).to receive(:public_for_api).and_return([])
|
||||
|
||||
response = execute('{ tags { edges { node { name } } } }')
|
||||
received_tags = extract_fields(response, 'tags', 'name')
|
||||
|
||||
expect(received_tags).to_not include('Health')
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe 'Votes' do
|
||||
|
||||
it 'only returns votes from proposals, debates and comments' do
|
||||
proposal = create(:proposal)
|
||||
debate = create(:debate)
|
||||
comment = create(:comment)
|
||||
spending_proposal = create(:spending_proposal)
|
||||
|
||||
proposal_vote = create(:vote, votable: proposal)
|
||||
debate_vote = create(:vote, votable: debate)
|
||||
comment_vote = create(:vote, votable: comment)
|
||||
spending_proposal_vote = create(:vote, votable: spending_proposal)
|
||||
|
||||
response = execute('{ votes { edges { node { votable_type } } } }')
|
||||
received_votables = extract_fields(response, 'votes', 'votable_type')
|
||||
|
||||
expect(received_votables).to match_array ['Proposal', 'Debate', 'Comment']
|
||||
end
|
||||
|
||||
it 'does not include votes from hidden debates' do
|
||||
visible_debate = create(:debate)
|
||||
hidden_debate = create(:debate, :hidden)
|
||||
|
||||
visible_debate_vote = create(:vote, votable: visible_debate)
|
||||
hidden_debate_vote = create(:vote, votable: hidden_debate)
|
||||
|
||||
response = execute('{ votes { edges { node { votable_id } } } }')
|
||||
received_debates = extract_fields(response, 'votes', 'votable_id')
|
||||
|
||||
expect(received_debates).to match_array [visible_debate.id]
|
||||
end
|
||||
|
||||
it 'does not include votes of hidden proposals' do
|
||||
visible_proposal = create(:proposal)
|
||||
hidden_proposal = create(:proposal, hidden_at: Time.now)
|
||||
|
||||
visible_proposal_vote = create(:vote, votable: visible_proposal)
|
||||
hidden_proposal_vote = create(:vote, votable: hidden_proposal)
|
||||
|
||||
response = execute('{ votes { edges { node { votable_id } } } }')
|
||||
received_proposals = extract_fields(response, 'votes', 'votable_id')
|
||||
|
||||
expect(received_proposals).to match_array [visible_proposal.id]
|
||||
end
|
||||
|
||||
it 'does not include votes of hidden comments' do
|
||||
visible_comment = create(:comment)
|
||||
hidden_comment = create(:comment, hidden_at: Time.now)
|
||||
|
||||
visible_comment_vote = create(:vote, votable: visible_comment)
|
||||
hidden_comment_vote = create(:vote, votable: hidden_comment)
|
||||
|
||||
response = execute('{ votes { edges { node { votable_id } } } }')
|
||||
received_comments = extract_fields(response, 'votes', 'votable_id')
|
||||
|
||||
expect(received_comments).to match_array [visible_comment.id]
|
||||
end
|
||||
|
||||
it 'does not include votes of comments from a hidden proposal' do
|
||||
visible_proposal = create(:proposal)
|
||||
hidden_proposal = create(:proposal, :hidden)
|
||||
|
||||
visible_proposal_comment = create(:comment, commentable: visible_proposal)
|
||||
hidden_proposal_comment = create(:comment, commentable: hidden_proposal)
|
||||
|
||||
visible_proposal_comment_vote = create(:vote, votable: visible_proposal_comment)
|
||||
hidden_proposal_comment_vote = create(:vote, votable: hidden_proposal_comment)
|
||||
|
||||
response = execute('{ votes { edges { node { votable_id } } } }')
|
||||
received_votables = extract_fields(response, 'votes', 'votable_id')
|
||||
|
||||
expect(received_votables).to match_array [visible_proposal_comment.id]
|
||||
end
|
||||
|
||||
it 'does not include votes of comments from a hidden debate' do
|
||||
visible_debate = create(:debate)
|
||||
hidden_debate = create(:debate, :hidden)
|
||||
|
||||
visible_debate_comment = create(:comment, commentable: visible_debate)
|
||||
hidden_debate_comment = create(:comment, commentable: hidden_debate)
|
||||
|
||||
visible_debate_comment_vote = create(:vote, votable: visible_debate_comment)
|
||||
hidden_debate_comment_vote = create(:vote, votable: hidden_debate_comment)
|
||||
|
||||
response = execute('{ votes { edges { node { votable_id } } } }')
|
||||
received_votables = extract_fields(response, 'votes', 'votable_id')
|
||||
|
||||
expect(received_votables).to match_array [visible_debate_comment.id]
|
||||
end
|
||||
|
||||
it 'does not include votes of debates that are not public' do
|
||||
not_public_debate = create(:debate)
|
||||
allow(Vote).to receive(:public_for_api).and_return([])
|
||||
|
||||
not_public_debate_vote = create(:vote, votable: not_public_debate)
|
||||
|
||||
response = execute('{ votes { edges { node { votable_id } } } }')
|
||||
received_votables = extract_fields(response, 'votes', 'votable_id')
|
||||
|
||||
expect(received_votables).to_not include(not_public_debate.id)
|
||||
end
|
||||
|
||||
it 'does not include votes of a hidden proposals' do
|
||||
not_public_proposal = create(:proposal)
|
||||
allow(Vote).to receive(:public_for_api).and_return([])
|
||||
|
||||
not_public_proposal_vote = create(:vote, votable: not_public_proposal)
|
||||
|
||||
response = execute('{ votes { edges { node { votable_id } } } }')
|
||||
received_votables = extract_fields(response, 'votes', 'votable_id')
|
||||
|
||||
expect(received_votables).to_not include(not_public_proposal.id)
|
||||
end
|
||||
|
||||
it 'does not include votes of a hidden comments' do
|
||||
not_public_comment = create(:comment)
|
||||
allow(Vote).to receive(:public_for_api).and_return([])
|
||||
|
||||
not_public_comment_vote = create(:vote, votable: not_public_comment)
|
||||
|
||||
response = execute('{ votes { edges { node { votable_id } } } }')
|
||||
received_votables = extract_fields(response, 'votes', 'votable_id')
|
||||
|
||||
expect(received_votables).to_not include(not_public_comment.id)
|
||||
end
|
||||
|
||||
it 'only returns date and hour for created_at' do
|
||||
created_at = Time.zone.parse("2017-12-31 9:30:15")
|
||||
create(:vote, created_at: created_at)
|
||||
|
||||
response = execute('{ votes { edges { node { public_created_at } } } }')
|
||||
received_timestamps = extract_fields(response, 'votes', 'public_created_at')
|
||||
|
||||
expect(Time.zone.parse(received_timestamps.first)).to eq Time.zone.parse("2017-12-31 9:00:00")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -4,6 +4,8 @@ describe Comment do
|
||||
|
||||
let(:comment) { build(:comment) }
|
||||
|
||||
it_behaves_like "has_public_author"
|
||||
|
||||
it "is valid" do
|
||||
expect(comment).to be_valid
|
||||
end
|
||||
@@ -132,4 +134,58 @@ describe Comment do
|
||||
end
|
||||
end
|
||||
|
||||
describe "public_for_api scope" do
|
||||
it "returns comments" do
|
||||
comment = create(:comment)
|
||||
|
||||
expect(Comment.public_for_api).to include(comment)
|
||||
end
|
||||
|
||||
it "does not return hidden comments" do
|
||||
hidden_comment = create(:comment, :hidden)
|
||||
|
||||
expect(Comment.public_for_api).not_to include(hidden_comment)
|
||||
end
|
||||
|
||||
it "returns comments on debates" do
|
||||
debate = create(:debate)
|
||||
comment = create(:comment, commentable: debate)
|
||||
|
||||
expect(Comment.public_for_api).to include(comment)
|
||||
end
|
||||
|
||||
it "does not return comments on hidden debates" do
|
||||
hidden_debate = create(:debate, :hidden)
|
||||
comment = create(:comment, commentable: hidden_debate)
|
||||
|
||||
expect(Comment.public_for_api).not_to include(comment)
|
||||
end
|
||||
|
||||
it "returns comments on proposals" do
|
||||
proposal = create(:proposal)
|
||||
comment = create(:comment, commentable: proposal)
|
||||
|
||||
expect(Comment.public_for_api).to include(comment)
|
||||
end
|
||||
|
||||
it "does not return comments on hidden proposals" do
|
||||
hidden_proposal = create(:proposal, :hidden)
|
||||
comment = create(:comment, commentable: hidden_proposal)
|
||||
|
||||
expect(Comment.public_for_api).not_to include(comment)
|
||||
end
|
||||
|
||||
it 'does not return comments on elements which are not debates or proposals' do
|
||||
budget_investment = create(:budget_investment)
|
||||
comment = create(:comment, commentable: budget_investment)
|
||||
|
||||
expect(Comment.public_for_api).not_to include(comment)
|
||||
end
|
||||
|
||||
it 'does not return comments with no commentable' do
|
||||
comment = build(:comment, commentable: nil).save!(validate: false)
|
||||
|
||||
expect(Comment.public_for_api).to_not include(comment)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
21
spec/models/concerns/has_public_author.rb
Normal file
21
spec/models/concerns/has_public_author.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
require 'spec_helper'
|
||||
|
||||
shared_examples_for 'has_public_author' do
|
||||
let(:model) { described_class }
|
||||
|
||||
describe 'public_author' do
|
||||
it "returns author if author's activity is public" do
|
||||
author = create(:user, public_activity: true)
|
||||
authored_element = create(model.to_s.underscore.to_sym, author: author)
|
||||
|
||||
expect(authored_element.public_author).to eq(author)
|
||||
end
|
||||
|
||||
it "returns nil if author's activity is private" do
|
||||
author = create(:user, public_activity: false)
|
||||
authored_element = create(model.to_s.underscore.to_sym, author: author)
|
||||
|
||||
expect(authored_element.public_author).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4,6 +4,8 @@ require 'rails_helper'
|
||||
describe Debate do
|
||||
let(:debate) { build(:debate) }
|
||||
|
||||
it_behaves_like "has_public_author"
|
||||
|
||||
it "should be valid" do
|
||||
expect(debate).to be_valid
|
||||
end
|
||||
@@ -700,4 +702,16 @@ describe Debate do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'public_for_api scope' do
|
||||
it 'returns debates' do
|
||||
debate = create(:debate)
|
||||
expect(Debate.public_for_api).to include(debate)
|
||||
end
|
||||
|
||||
it 'does not return hidden debates' do
|
||||
debate = create(:debate, :hidden)
|
||||
expect(Debate.public_for_api).to_not include(debate)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -22,6 +22,28 @@ describe ProposalNotification do
|
||||
expect(notification).to_not be_valid
|
||||
end
|
||||
|
||||
describe "public_for_api scope" do
|
||||
it "returns proposal notifications" do
|
||||
proposal = create(:proposal)
|
||||
notification = create(:proposal_notification, proposal: proposal)
|
||||
|
||||
expect(ProposalNotification.public_for_api).to include(notification)
|
||||
end
|
||||
|
||||
it "blocks proposal notifications whose proposal is hidden" do
|
||||
proposal = create(:proposal, :hidden)
|
||||
notification = create(:proposal_notification, proposal: proposal)
|
||||
|
||||
expect(ProposalNotification.public_for_api).not_to include(notification)
|
||||
end
|
||||
|
||||
it "blocks proposal notifications without proposal" do
|
||||
proposal = build(:proposal_notification, proposal: nil).save!(validate: false)
|
||||
|
||||
expect(ProposalNotification.public_for_api).not_to include(notification)
|
||||
end
|
||||
end
|
||||
|
||||
describe "minimum interval between notifications" do
|
||||
|
||||
before(:each) do
|
||||
|
||||
@@ -4,6 +4,8 @@ require 'rails_helper'
|
||||
describe Proposal do
|
||||
let(:proposal) { build(:proposal) }
|
||||
|
||||
it_behaves_like "has_public_author"
|
||||
|
||||
it "should be valid" do
|
||||
expect(proposal).to be_valid
|
||||
end
|
||||
@@ -843,4 +845,16 @@ describe Proposal do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'public_for_api scope' do
|
||||
it 'returns proposals' do
|
||||
proposal = create(:proposal)
|
||||
expect(Proposal.public_for_api).to include(proposal)
|
||||
end
|
||||
|
||||
it 'does not return hidden proposals' do
|
||||
proposal = create(:proposal, :hidden)
|
||||
expect(Proposal.public_for_api).to_not include(proposal)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -40,4 +40,77 @@ describe 'Vote' do
|
||||
expect(vote.value).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'public_for_api scope' do
|
||||
it 'returns votes on debates' do
|
||||
debate = create(:debate)
|
||||
vote = create(:vote, votable: debate)
|
||||
|
||||
expect(Vote.public_for_api).to include(vote)
|
||||
end
|
||||
|
||||
it 'blocks votes on hidden debates' do
|
||||
debate = create(:debate, :hidden)
|
||||
vote = create(:vote, votable: debate)
|
||||
|
||||
expect(Vote.public_for_api).not_to include(vote)
|
||||
end
|
||||
|
||||
it 'returns votes on proposals' do
|
||||
proposal = create(:proposal)
|
||||
vote = create(:vote, votable: proposal)
|
||||
|
||||
expect(Vote.public_for_api).to include(vote)
|
||||
end
|
||||
|
||||
it 'blocks votes on hidden proposals' do
|
||||
proposal = create(:proposal, :hidden)
|
||||
vote = create(:vote, votable: proposal)
|
||||
|
||||
expect(Vote.public_for_api).not_to include(vote)
|
||||
end
|
||||
|
||||
it 'returns votes on comments' do
|
||||
comment = create(:comment)
|
||||
vote = create(:vote, votable: comment)
|
||||
|
||||
expect(Vote.public_for_api).to include(vote)
|
||||
end
|
||||
|
||||
it 'blocks votes on hidden comments' do
|
||||
comment = create(:comment, :hidden)
|
||||
vote = create(:vote, votable: comment)
|
||||
|
||||
expect(Vote.public_for_api).not_to include(vote)
|
||||
end
|
||||
|
||||
it 'blocks votes on comments on hidden proposals' do
|
||||
hidden_proposal = create(:proposal, :hidden)
|
||||
comment_on_hidden_proposal = create(:comment, commentable: hidden_proposal)
|
||||
vote = create(:vote, votable: comment_on_hidden_proposal)
|
||||
|
||||
expect(Vote.public_for_api).to_not include(vote)
|
||||
end
|
||||
|
||||
it 'blocks votes on comments on hidden debates' do
|
||||
hidden_debate = create(:debate, :hidden)
|
||||
comment_on_hidden_debate = create(:comment, commentable: hidden_debate)
|
||||
vote = create(:vote, votable: comment_on_hidden_debate)
|
||||
|
||||
expect(Vote.public_for_api).to_not include(vote)
|
||||
end
|
||||
|
||||
it 'blocks any other kind of votes' do
|
||||
spending_proposal = create(:spending_proposal)
|
||||
vote = create(:vote, votable: spending_proposal)
|
||||
|
||||
expect(Vote.public_for_api).not_to include(vote)
|
||||
end
|
||||
|
||||
it 'blocks votes without votable' do
|
||||
vote = build(:vote, votable: nil).save!(validate: false)
|
||||
|
||||
expect(Vote.public_for_api).not_to include(vote)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,6 +3,7 @@ require 'database_cleaner'
|
||||
require 'email_spec'
|
||||
require 'devise'
|
||||
require 'knapsack'
|
||||
Dir["./spec/models/concerns/*.rb"].each { |f| require f }
|
||||
Dir["./spec/support/**/*.rb"].sort.each { |f| require f }
|
||||
|
||||
RSpec.configure do |config|
|
||||
|
||||
Reference in New Issue
Block a user