Merge pull request #5897 from consuldemocracy/replace_equalizer_with_flex

Use flex and grid layouts instead of data-equalizer
This commit is contained in:
Javi Martín
2025-08-28 14:40:53 +02:00
committed by GitHub
62 changed files with 948 additions and 814 deletions

View File

@@ -83,6 +83,7 @@ rules:
"@stylistic/indentation": "@stylistic/indentation":
- 2 - 2
- ignore: - ignore:
- param
- value - value
"@stylistic/media-feature-parentheses-space-inside": never "@stylistic/media-feature-parentheses-space-inside": never
"@stylistic/no-eol-whitespace": true "@stylistic/no-eol-whitespace": true

View File

@@ -21,6 +21,7 @@
@import "account/**/*"; @import "account/**/*";
@import "budgets/**/*"; @import "budgets/**/*";
@import "comments/**/*"; @import "comments/**/*";
@import "dashboard/**/*";
@import "debates/**/*"; @import "debates/**/*";
@import "devise/**/*"; @import "devise/**/*";
@import "documents/**/*"; @import "documents/**/*";

View File

@@ -2,7 +2,6 @@
// //
// 01. Dashboard global // 01. Dashboard global
// 02. Actions // 02. Actions
// 03. Resources
// 04. Goals // 04. Goals
// 05. Sidebar // 05. Sidebar
// 06. Community // 06. Community
@@ -165,62 +164,6 @@
} }
} }
// 03. Resources
// -------------
.resource-card {
background: #d1f5eb;
border-radius: rem-calc(4);
margin-bottom: $line-height;
min-height: $line-height * 9;
padding: $line-height * 2 $line-height $line-height;
position: relative;
text-align: center;
&.alert {
background: #feeaeb;
&::before {
color: #fb9497;
content: "\74";
}
}
&::before {
border: 2px solid;
border-radius: rem-calc(40);
color: #00cb96;
content: "\6c";
font-family: "icons";
font-size: rem-calc(20);
height: rem-calc(36);
position: absolute;
right: 12px;
top: 12px;
width: rem-calc(36);
}
.label {
left: 0;
position: absolute;
top: 20px;
}
h4 {
margin-top: $line-height;
}
.resource-description {
min-height: $line-height * 4;
}
.button {
background: #00cb96;
color: #000;
font-weight: bold;
}
}
// 04. Goals // 04. Goals
// --------- // ---------
@@ -429,28 +372,6 @@
padding: $line-height; padding: $line-height;
} }
.poll-card {
background: #e7f3fd;
border-radius: rem-calc(4);
margin-bottom: $line-height;
min-height: $line-height * 9;
padding: $line-height;
position: relative;
text-align: center;
.button {
font-weight: bold;
}
button {
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
}
.community-poll { .community-poll {
border-bottom: 1px solid $border; border-bottom: 1px solid $border;
margin-bottom: $line-height; margin-bottom: $line-height;

View File

@@ -0,0 +1,22 @@
.dashboard-poll {
background: #e7f3fd;
border-radius: rem-calc(4);
display: flex;
flex-direction: column;
min-height: $line-height * 9;
padding: $line-height;
position: relative;
text-align: center;
.button {
font-weight: bold;
}
button {
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
}

View File

@@ -0,0 +1,4 @@
.dashboard-polls {
@include dynamic-grid;
margin-top: $line-height;
}

View File

@@ -0,0 +1,55 @@
.dashboard-resources {
.resources {
@include dynamic-grid;
.resource-card {
background: #d1f5eb;
border-radius: rem-calc(4);
display: flex;
flex-direction: column;
justify-content: space-between;
padding: $line-height * 2 $line-height $line-height;
position: relative;
text-align: center;
&.alert {
background: #feeaeb;
&::before {
color: #fb9497;
content: "\74";
}
}
&::before {
border: 2px solid;
border-radius: rem-calc(40);
color: #00cb96;
content: "\6c";
font-family: "icons";
font-size: rem-calc(20);
height: rem-calc(36);
position: absolute;
right: 12px;
top: 12px;
width: rem-calc(36);
}
.label {
left: 0;
position: absolute;
top: 20px;
}
h4 {
margin-top: $line-height;
}
.button {
background: #00cb96;
color: #000;
font-weight: bold;
}
}
}
}

View File

@@ -403,18 +403,10 @@ button,
vertical-align: top; vertical-align: top;
} }
.align-middle {
vertical-align: middle;
}
.table { .table {
display: table; display: table;
} }
.table-cell {
display: table-cell;
}
.uppercase { .uppercase {
text-transform: uppercase; text-transform: uppercase;
} }
@@ -1865,40 +1857,6 @@ table {
} }
} }
.following {
.follow-list {
list-style-type: circle;
padding: calc($line-height / 2);
li {
margin-bottom: calc($line-height / 2);
margin-left: $line-height;
}
}
h3 {
font-size: rem-calc(24);
margin-top: $line-height;
padding-left: rem-calc(30);
position: relative;
span {
left: 0;
position: absolute;
top: 2px;
}
}
.interests {
@include breakpoint(medium) {
border-left: 1px solid #ececec;
padding-left: $line-height;
}
}
}
// 19. Recommendations // 19. Recommendations
// ------------------- // -------------------
@@ -2039,67 +1997,6 @@ table {
} }
} }
.recommended-index {
@include full-width-background;
@include full-width-border(bottom, 1px solid #eee);
@include full-width-border(top, 1px solid #fafafa);
background: #fafafa;
margin-bottom: $line-height;
margin-top: rem-calc(-25);
padding: $line-height 0 calc($line-height / 2);
@include breakpoint(medium) {
padding-top: 0;
}
h2 {
font-size: $small-font-size;
text-transform: uppercase;
}
h3 {
font-size: $base-font-size;
margin-bottom: 0;
}
a {
&:hover {
text-decoration: none;
}
}
.recommendation {
@include card;
background: $body-background;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.15);
display: block;
margin-bottom: calc($line-height / 4);
padding: calc($line-height / 2);
z-index: 1;
&:hover:not(:focus-within) {
box-shadow: 0 4px 6px 0 rgba(0, 0, 0, 0.15);
}
}
}
.hide-recommendations {
color: $text-light;
cursor: pointer;
font-size: $small-font-size;
line-height: inherit;
position: absolute;
right: 12px;
top: -18px;
z-index: 2;
&:focus,
&:hover {
@include anchor-color-hover;
}
}
// 20. Documents // 20. Documents
// ------------- // -------------

View File

@@ -32,6 +32,15 @@
} }
} }
@mixin dynamic-grid($min-column-width: 15rem,
$row-gap: $line-height,
$column-gap: rem-calc(map-get($grid-column-gutter, medium))) {
display: grid;
gap: $row-gap $column-gap;
grid-auto-rows: 1fr;
grid-template-columns: repeat(auto-fit, minmax($min-column-width, 1fr));
}
@mixin full-width-cover($adjust-margin: true, $adjust-padding: false) { @mixin full-width-cover($adjust-margin: true, $adjust-padding: false) {
$global-padding: rem-calc(map-get($grid-column-gutter, medium)) * 0.5; $global-padding: rem-calc(map-get($grid-column-gutter, medium)) * 0.5;
bottom: 0; bottom: 0;

View File

@@ -1390,12 +1390,6 @@
} }
} }
.image-container {
background: #fafafa;
overflow: hidden;
position: relative;
}
.public .poll { .public .poll {
border: 1px solid $border; border: 1px solid $border;
margin-bottom: calc($line-height / 2); margin-bottom: calc($line-height / 2);
@@ -1415,7 +1409,8 @@
// 09. Polls results and stats // 09. Polls results and stats
// --------------------------- // ---------------------------
.polls-results-stats { .polls-results,
.polls-stats {
table { table {
table-layout: fixed; table-layout: fixed;

View File

@@ -0,0 +1,55 @@
.poll .access-status {
border-bottom: 60px solid transparent;
border-top: 0;
height: 0;
position: absolute;
right: 0;
top: 0;
width: 0;
&.cant-answer::after,
&.not-logged-in::after,
&.already-answer::after,
&.unverified::after {
font-family: "icons" !important;
left: 34px;
position: absolute;
top: 5px;
}
&.cant-answer {
border-right: 60px solid $alert-bg;
&::after {
color: $color-alert;
content: "\74";
}
}
&.not-logged-in {
border-right: 60px solid $info-bg;
&::after {
color: $color-info;
content: "\6f";
}
}
&.unverified {
border-right: 60px solid $warning-bg;
&::after {
color: $color-warning;
content: "\6f";
}
}
&.already-answer {
border-right: 60px solid $success-bg;
&::after {
color: $color-success;
content: "\59";
}
}
}

View File

@@ -2,72 +2,40 @@
&.with-image { &.with-image {
@include breakpoint(medium) { @include breakpoint(medium) {
display: flex;
padding: 0 calc($line-height / 2) 0 0; padding: 0 calc($line-height / 2) 0 0;
> * {
flex-basis: 25%;
} }
.image-container img { .poll-info {
flex-basis: 50%;
}
> a {
align-self: center;
}
> .poll-info,
> a {
margin-#{$global-left}: rem-calc(map-get($grid-column-gutter, medium));
}
}
}
.image-container {
background: #fafafa;
overflow: hidden;
position: relative;
img {
height: 100%; height: 100%;
max-width: none; max-width: none;
position: absolute; position: absolute;
} }
} }
.icon-poll-answer {
border-bottom: 60px solid transparent;
border-top: 0;
height: 0;
position: absolute;
right: 0;
top: 0;
width: 0;
&.cant-answer::after,
&.not-logged-in::after,
&.already-answer::after,
&.unverified::after {
font-family: "icons" !important;
left: 34px;
position: absolute;
top: 5px;
}
&.cant-answer {
border-right: 60px solid $alert-bg;
&::after {
color: $color-alert;
content: "\74";
}
}
&.not-logged-in {
border-right: 60px solid $info-bg;
&::after {
color: $color-info;
content: "\6f";
}
}
&.unverified {
border-right: 60px solid $warning-bg;
&::after {
color: $color-warning;
content: "\6f";
}
}
&.already-answer {
border-right: 60px solid $success-bg;
&::after {
color: $color-success;
content: "\59";
}
}
}
.dates { .dates {
color: $text-medium; color: $text-medium;
font-size: $small-font-size; font-size: $small-font-size;

View File

@@ -0,0 +1,22 @@
.polls-results {
.polls-results-content {
margin-bottom: $line-height;
margin-top: $line-height;
@include breakpoint(medium) {
display: flex;
> :first-child {
flex-basis: 25%;
}
> :last-child {
flex-basis: 75%;
}
}
> * {
@include grid-column-gutter;
}
}
}

View File

@@ -0,0 +1,77 @@
.recommended-index {
@include full-width-background;
@include full-width-border(bottom, 1px solid #eee);
@include full-width-border(top, 1px solid #fafafa);
background: #fafafa;
margin-bottom: $line-height;
margin-top: rem-calc(-25);
padding: $line-height 0 calc($line-height / 2);
@include breakpoint(medium) {
padding-top: 0;
}
h2 {
font-size: $small-font-size;
text-transform: uppercase;
}
ul {
$row-gap: calc($line-height / 4);
@include dynamic-grid($row-gap: $row-gap);
@include grid-column-gutter;
font-weight: bold;
list-style: none;
margin: 0;
@include breakpoint(medium) {
+ * {
text-align: $global-right;
}
}
+ * {
@include grid-column-gutter;
margin-top: $row-gap;
}
}
a {
&:hover {
text-decoration: none;
}
}
.recommendation {
background: $body-background;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.15);
display: block;
padding: calc($line-height / 2);
z-index: 1;
&:hover:not(:focus-within) {
box-shadow: 0 4px 6px 0 rgba(0, 0, 0, 0.15);
}
}
.hide-recommendations {
color: $text-light;
cursor: pointer;
font-size: $small-font-size;
line-height: inherit;
position: absolute;
right: 12px;
top: 0;
z-index: 2;
@include breakpoint(medium) {
top: -18px;
}
&:focus,
&:hover {
@include anchor-color-hover;
}
}
}

View File

@@ -0,0 +1,40 @@
.users-following {
margin-top: $line-height;
@include breakpoint(medium) {
display: flex;
> .followables {
flex-basis: calc(100% * 2 / 3);
}
> .interests {
border-left: 1px solid #ececec;
flex-basis: calc(100% / 3);
padding-left: $line-height;
}
}
.follow-list {
list-style-type: circle;
padding: calc($line-height / 2);
li {
margin-bottom: calc($line-height / 2);
margin-left: $line-height;
}
}
h3 {
font-size: rem-calc(24);
margin-top: $line-height;
padding-left: rem-calc(30);
position: relative;
span {
left: 0;
position: absolute;
top: 2px;
}
}
}

View File

@@ -0,0 +1,23 @@
<div id="<%= dom_id(resource) %>"
class="resource-card <%= resource_card_class %>"
title="<%= resource_tooltip %>">
<h4><%= resource.title %></h4>
<% if is_new_action_since_last_login?(resource, new_actions_since_last_login) %>
<span class="label"><%= t("dashboard.progress.new_action") %></span>
<% end %>
<p class="resource-description"><%= resource.short_description %></p>
<% if resource.executed_for?(proposal) || (!resource.request_to_administrators && resource.active_for?(proposal)) %>
<%= link_to t("dashboard.resource.view_resource"),
new_request_proposal_dashboard_action_path(proposal, resource),
class: "button" %>
<% elsif resource.requested_for?(proposal) %>
<strong><%= t("dashboard.resource.resource_requested") %></strong>
<% elsif resource.active_for?(proposal) %>
<%= link_to t("dashboard.resource.request_resource"),
new_request_proposal_dashboard_action_path(proposal, resource),
class: "button" %>
<% else %>
<strong><%= resource_availability_label %></strong>
<% end %>
</div>

View File

@@ -0,0 +1,37 @@
class Dashboard::ActiveResourceComponent < ApplicationComponent
attr_reader :resource, :proposal, :new_actions_since_last_login
use_helpers :is_new_action_since_last_login?
def initialize(resource, proposal, new_actions_since_last_login)
@resource = resource
@proposal = proposal
@new_actions_since_last_login = new_actions_since_last_login
end
def resource_card_class
return "alert" unless resource.active_for?(proposal)
return "success" if resource.executed_for?(proposal)
"primary"
end
def resource_tooltip
return t("dashboard.resource.resource_locked") unless resource.active_for?(proposal)
return t("dashboard.resource.view_resource") if resource.executed_for?(proposal)
return t("dashboard.resource.resource_requested") if resource.requested_for?(proposal)
t("dashboard.resource.request_resource")
end
def resource_availability_label
label = []
label << t("dashboard.resource.required_days",
days: resource.day_offset) if resource.day_offset > 0
label << t("dashboard.resource.required_supports",
supports: number_with_delimiter(resource.required_supports,
delimiter: ".")) if resource.required_supports > 0
safe_join label, h(" #{t("dashboard.resource.and")})") + tag(:br)
end
end

View File

@@ -0,0 +1,6 @@
<div class="resource-card">
<h4><%= t("dashboard.menu.#{resource}") %></h4>
<p class="resource-description"><%= resource_description %></p>
<%= link_to t("dashboard.resource.view_resource"), resource_path, class: "button" %>
</div>

View File

@@ -0,0 +1,29 @@
class Dashboard::DefaultResourceComponent < ApplicationComponent
attr_reader :resource, :proposal
use_helpers :can?
def initialize(resource, proposal)
@resource = resource
@proposal = proposal
end
def render?
can?(:"manage_#{resource}", proposal)
end
def resource_description
if resource == "mailing"
Setting["proposals.email_short_title"]
else
Setting["proposals.#{resource}_short_title"]
end
end
def resource_path
if resource == "polls"
proposal_dashboard_polls_path(proposal)
else
send("new_proposal_dashboard_#{resource}_path", proposal)
end
end
end

View File

@@ -0,0 +1,34 @@
<div id="<%= dom_id(poll) %>" class="dashboard-poll">
<h4><%= link_to poll.title, proposal_poll_path(proposal, poll) %></h4>
<span class="small">
<%= l(poll.starts_at.to_date) %> - <%= l(poll.ends_at.to_date) %>
</span>
<p class="margin-top">
<strong><%= t("dashboard.polls.poll.responses", count: poll.answer_count) %></strong>
</p>
<div class="small-12 column small-centered margin-top">
<% if poll.starts_at.to_date >= Date.current %>
<%= link_to t("dashboard.polls.poll.edit_poll"),
edit_proposal_dashboard_poll_path(proposal, poll), class: "button hollow" %>
<% else %>
<%= link_to t("dashboard.polls.poll.view_results"),
results_proposal_poll_path(proposal, poll),
class: "button" %>
<% end %>
</div>
<%= form_for poll, remote: true,
data: { type: :json },
url: proposal_dashboard_poll_path(proposal, poll) do |f| %>
<%= f.check_box :results_enabled, class: "js-submit-on-change" %>
<% end %>
<p class="help-text"><%= t("dashboard.polls.poll.show_results_help") %></p>
<%= button_to t("dashboard.polls.poll.delete"),
proposal_dashboard_poll_path(proposal, poll),
method: :delete,
"data-confirm": t("dashboard.polls.poll.alert_notice"),
class: "delete" %>
</div>

View File

@@ -0,0 +1,13 @@
class Dashboard::PollComponent < ApplicationComponent
attr_reader :poll
def initialize(poll)
@poll = poll
end
private
def proposal
poll.related
end
end

View File

@@ -0,0 +1,5 @@
<div class="dashboard-polls">
<% polls.each do |poll| %>
<%= render Dashboard::PollComponent.new(poll) %>
<% end %>
</div>

View File

@@ -0,0 +1,11 @@
class Dashboard::PollsComponent < ApplicationComponent
attr_reader :polls
def initialize(polls)
@polls = polls
end
def render?
polls.any?
end
end

View File

@@ -0,0 +1,12 @@
<div class="dashboard-resources">
<h3 class="title"><%= t("dashboard.resources.available_resources") %></h3>
<div class="resources">
<% default_resources.each do |resource| %>
<%= render Dashboard::DefaultResourceComponent.new(resource, proposal) %>
<% end %>
<% active_resources.each do |resource| %>
<%= render Dashboard::ActiveResourceComponent.new(resource, proposal, new_actions_since_last_login) %>
<% end %>
</div>
</div>

View File

@@ -0,0 +1,21 @@
class Dashboard::ResourcesComponent < ApplicationComponent
attr_reader :proposal, :new_actions_since_last_login
def initialize(proposal, new_actions_since_last_login = [])
@proposal = proposal
@new_actions_since_last_login = new_actions_since_last_login
end
private
def default_resources
%w[polls mailing poster]
end
def active_resources
@active_resources ||= Dashboard::Action.active
.resources
.by_proposal(proposal)
.order(required_supports: :asc, day_offset: :asc)
end
end

View File

@@ -0,0 +1,3 @@
<div class="access-status <%= html_class %>" title="<%= text %>">
<span class="show-for-sr"><%= text %></span>
</div>

View File

@@ -0,0 +1,36 @@
class Polls::AccessStatusComponent < ApplicationComponent
attr_reader :poll
use_helpers :cannot?, :current_user
def initialize(poll)
@poll = poll
end
def render?
attributes.present?
end
private
def text
attributes[:text]
end
def html_class
attributes[:class]
end
def attributes
if current_user
if current_user.unverified?
{ text: t("polls.index.unverified"), class: "unverified" }
elsif cannot?(:answer, poll)
{ text: t("polls.index.cant_answer"), class: "cant-answer" }
elsif !poll.votable_by?(current_user)
{ text: t("polls.index.already_answer"), class: "already-answer" }
end
else
{ text: t("polls.index.not_logged_in"), class: "not-logged-in" }
end
end
end

View File

@@ -1,32 +1,13 @@
<div class="poll with-image"> <div class="poll with-image">
<% if current_user %> <%= render Polls::AccessStatusComponent.new(poll) %>
<% if current_user.unverified? %>
<div class="icon-poll-answer unverified" title="<%= t("polls.index.unverified") %>"> <div class="image-container">
<span class="show-for-sr"><%= t("polls.index.unverified") %></span>
</div>
<% elsif cannot?(:answer, poll) %>
<div class="icon-poll-answer cant-answer" title="<%= t("polls.index.cant_answer") %>">
<span class="show-for-sr"><%= t("polls.index.cant_answer") %></span>
</div>
<% elsif !poll.votable_by?(current_user) %>
<div class="icon-poll-answer already-answer" title="<%= t("polls.index.already_answer") %>">
<span class="show-for-sr"><%= t("polls.index.already_answer") %></span>
</div>
<% end %>
<% else %>
<div class="icon-poll-answer not-logged-in" title="<%= t("polls.index.not_logged_in") %>">
<span class="show-for-sr"><%= t("polls.index.not_logged_in") %></span>
</div>
<% end %>
<div class="row" data-equalizer data-equalize-on="medium">
<div class="small-12 medium-3 column">
<div class="image-container" data-equalizer-watch>
<% if poll.image.present? %> <% if poll.image.present? %>
<%= image_tag poll.image.variant(:large), alt: poll.image.title.unicode_normalize %> <%= image_tag poll.image.variant(:large), alt: poll.image.title.unicode_normalize %>
<% end %> <% end %>
</div> </div>
</div>
<div class="small-12 medium-6 column" data-equalizer-watch> <div class="poll-info">
<% if poll.questions.one? %> <% if poll.questions.one? %>
<h4><%= link_to_poll poll.questions.first.title, poll %></h4> <h4><%= link_to_poll poll.questions.first.title, poll %></h4>
<div class="dates"><%= dates %></div> <div class="dates"><%= dates %></div>
@@ -43,14 +24,10 @@
<%= render Polls::GeozonesComponent.new(poll) %> <%= render Polls::GeozonesComponent.new(poll) %>
<%= render SDG::TagListComponent.new(poll, limit: 5, linkable: false) %> <%= render SDG::TagListComponent.new(poll, limit: 5, linkable: false) %>
</div> </div>
<div class="small-12 medium-3 column table" data-equalizer-watch>
<div class="table-cell align-middle">
<% if poll.expired? %> <% if poll.expired? %>
<%= link_to_poll t("polls.index.participate_button_expired"), poll, class: "button hollow expanded" %> <%= link_to_poll t("polls.index.participate_button_expired"), poll, class: "button hollow expanded" %>
<% else %> <% else %>
<%= link_to_poll t("polls.index.participate_button"), poll, class: "button hollow expanded" %> <%= link_to_poll t("polls.index.participate_button"), poll, class: "button hollow expanded" %>
<% end %> <% end %>
</div>
</div>
</div>
</div> </div>

View File

@@ -1,6 +1,6 @@
class Polls::PollComponent < ApplicationComponent class Polls::PollComponent < ApplicationComponent
attr_reader :poll attr_reader :poll
use_helpers :cannot?, :current_user, :link_to_poll use_helpers :can?
def initialize(poll) def initialize(poll)
@poll = poll @poll = poll
@@ -11,4 +11,14 @@ class Polls::PollComponent < ApplicationComponent
def dates def dates
t("polls.dates", open_at: l(poll.starts_at.to_date), closed_at: l(poll.ends_at.to_date)) t("polls.dates", open_at: l(poll.starts_at.to_date), closed_at: l(poll.ends_at.to_date))
end end
def link_to_poll(text, poll, options = {})
if can?(:results, poll)
link_to text, results_poll_path(id: poll.slug || poll.id), options
elsif can?(:stats, poll)
link_to text, stats_poll_path(id: poll.slug || poll.id), options
else
link_to text, poll_path(id: poll.slug || poll.id), options
end
end
end end

View File

@@ -0,0 +1,22 @@
<% provide :title, poll.name %>
<div class="polls-results">
<%= render Polls::PollHeaderComponent.new(poll) %>
<%= render "poll_subnav" %>
<div class="polls-results-content">
<div>
<p><strong><%= t("polls.show.results.title") %></strong></p>
<ul class="menu vertical">
<%- poll.questions.each do |question| %>
<li><%= link_to question.title, "##{question.title.parameterize}" %></li>
<% end %>
</ul>
</div>
<div>
<%= render Polls::Results::QuestionComponent.with_collection(poll.questions) %>
</div>
</div>
</div>

View File

@@ -0,0 +1,7 @@
class Polls::ResultsComponent < ApplicationComponent
attr_reader :poll
def initialize(poll)
@poll = poll
end
end

View File

@@ -0,0 +1,30 @@
<div class="recommended-index">
<h2 class="show-for-sr"><%= t("shared.recommended_index.title") %></h2>
<div id="recommendations" data-toggler=".hide">
<%= button_to disable_recommendations_path, title: t("shared.recommended_index.hide"),
class: "hide-recommendations",
data: {
toggle: "recommendations",
confirm: t("#{namespace}.index.recommendations.disable")
},
method: :put do %>
<span class="icon-x"></span>
<span class="show-for-sr"><%= t("shared.recommended_index.hide") %></span>
<% end %>
<ul>
<% recommendations.each do |recommendation| %>
<li>
<%= link_to recommendation.title, polymorphic_path(recommendation), class: "recommendation" %>
</li>
<% end %>
</ul>
<div>
<%= link_to t("shared.recommended_index.see_more"),
current_path_with_query_params(order: "recommendations"),
class: "small" %>
</div>
</div>
</div>

View File

@@ -0,0 +1,23 @@
class Shared::RecommendedIndexComponent < ApplicationComponent
attr_reader :recommendations, :namespace
use_helpers :current_path_with_query_params
def initialize(recommendations, namespace:)
@recommendations = recommendations
@namespace = namespace
end
def render?
feature?("user.recommendations") && recommendations.present?
end
private
def disable_recommendations_path
if namespace == "debates"
recommendations_disable_debates_path
else
recommendations_disable_proposals_path
end
end
end

View File

@@ -1,5 +1,5 @@
<div class="row following margin-top" data-equalizer data-equalize-on="medium"> <div class="users-following">
<div class="small-12 medium-8 column" data-equalizer-watch> <div class="followables">
<ul class="menu simple clear"> <ul class="menu simple clear">
<% follows.each do |followable_type, follows| %> <% follows.each do |followable_type, follows| %>
<li><%= link_to followable_type_title(followable_type), "##{followable_type_title(followable_type).parameterize.underscore}" %></li> <li><%= link_to followable_type_title(followable_type), "##{followable_type_title(followable_type).parameterize.underscore}" %></li>
@@ -21,7 +21,7 @@
<% end %> <% end %>
</div> </div>
<div class="small-12 medium-4 column interests" data-equalizer-watch> <div class="interests">
<%= render "interests", user: user %> <%= render "interests", user: user %>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,32 @@
class Users::FollowingComponent < ApplicationComponent
attr_reader :user, :follows
def initialize(user, follows:)
@user = user
@follows = follows
end
private
def followable_type_title(followable_type)
t("activerecord.models.#{followable_type.underscore}.other")
end
def followable_icon(followable)
{ proposals: "Proposal", budget: "Budget::Investment" }.invert[followable]
end
def render_follow(follow)
return if follow.followable.blank?
followable = follow.followable
partial = "#{followable_class_name(followable)}_follow"
locals = { followable_class_name(followable).to_sym => followable }
render partial, locals
end
def followable_class_name(followable)
followable.class.to_s.parameterize(separator: "_")
end
end

View File

@@ -16,7 +16,7 @@
</ul> </ul>
<% if current_filter == "follows" %> <% if current_filter == "follows" %>
<%= render "users/following", user: user, follows: follows.group_by(&:followable_type) %> <%= render Users::FollowingComponent.new(user, follows: follows.group_by(&:followable_type)) %>
<% else %> <% else %>
<%= render_user_partial current_filter %> <%= render_user_partial current_filter %>
<% end %> <% end %>

View File

@@ -1,5 +1,5 @@
class DashboardController < Dashboard::BaseController class DashboardController < Dashboard::BaseController
helper_method :dashboard_action, :active_resources, :course helper_method :dashboard_action, :course
before_action :set_done_and_pending_actions, only: [:recommended_actions, :progress] before_action :set_done_and_pending_actions, only: [:recommended_actions, :progress]
before_action :authorize_dashboard, except: :publish before_action :authorize_dashboard, except: :publish
@@ -30,13 +30,6 @@ class DashboardController < Dashboard::BaseController
private private
def active_resources
@active_resources ||= Dashboard::Action.active
.resources
.by_proposal(proposal)
.order(required_supports: :asc, day_offset: :asc)
end
def course def course
@course ||= Dashboard::Action.course_for(proposal) @course ||= Dashboard::Action.course_for(proposal)
end end

View File

@@ -1,26 +1,4 @@
module FollowablesHelper module FollowablesHelper
def followable_type_title(followable_type)
t("activerecord.models.#{followable_type.underscore}.other")
end
def followable_icon(followable)
{ proposals: "Proposal", budget: "Budget::Investment" }.invert[followable]
end
def render_follow(follow)
return if follow.followable.blank?
followable = follow.followable
partial = "#{followable_class_name(followable)}_follow"
locals = { followable_class_name(followable).to_sym => followable }
render partial, locals
end
def followable_class_name(followable)
followable.class.to_s.parameterize(separator: "_")
end
def find_or_build_follow(user, followable) def find_or_build_follow(user, followable)
Follow.find_or_initialize_by(user: user, followable: followable) Follow.find_or_initialize_by(user: user, followable: followable)
end end

View File

@@ -4,16 +4,6 @@ module PollsHelper
booth.name + location booth.name + location
end end
def link_to_poll(text, poll, options = {})
if can?(:results, poll)
link_to text, results_poll_path(id: poll.slug || poll.id), options
elsif can?(:stats, poll)
link_to text, stats_poll_path(id: poll.slug || poll.id), options
else
link_to text, poll_path(id: poll.slug || poll.id), options
end
end
def results_menu? def results_menu?
controller_name == "polls" && action_name == "results" controller_name == "polls" && action_name == "results"
end end

View File

@@ -56,18 +56,6 @@ module ProposalsDashboardHelper
controller_name == "dashboard" && action_name == "new_request" && dashboard_action&.id == id controller_name == "dashboard" && action_name == "new_request" && dashboard_action&.id == id
end end
def resource_availability_label(resource)
label = []
label << t("dashboard.resource.required_days",
days: resource.day_offset) if resource.day_offset > 0
label << t("dashboard.resource.required_supports",
supports: number_with_delimiter(resource.required_supports,
delimiter: ".")) if resource.required_supports > 0
safe_join label, h(" #{t("dashboard.resource.and")})") + tag(:br)
end
def daily_selected_class def daily_selected_class
return nil if params[:group_by].blank? return nil if params[:group_by].blank?
@@ -86,21 +74,6 @@ module ProposalsDashboardHelper
"hollow" "hollow"
end end
def resource_card_class(resource, proposal)
return "alert" unless resource.active_for?(proposal)
return "success" if resource.executed_for?(proposal)
"primary"
end
def resource_tooltip(resource, proposal)
return t("dashboard.resource.resource_locked") unless resource.active_for?(proposal)
return t("dashboard.resource.view_resource") if resource.executed_for?(proposal)
return t("dashboard.resource.resource_requested") if resource.requested_for?(proposal)
t("dashboard.resource.request_resource")
end
def proposed_action_description(proposed_action) def proposed_action_description(proposed_action)
sanitize proposed_action.description.truncate(200) sanitize proposed_action.description.truncate(200)
end end

View File

@@ -73,6 +73,6 @@ module ProposalsHelper
end end
def show_recommended_proposals? def show_recommended_proposals?
params[:selected].blank? && feature?("user.recommendations") && @recommended_proposals.present? params[:selected].blank?
end end
end end

View File

@@ -7,17 +7,6 @@ module WelcomeHelper
"display: none;" if index.positive? "display: none;" if index.positive?
end end
def recommended_path(recommended)
case recommended.class.name
when "Debate"
debate_path(recommended)
when "Proposal"
proposal_path(recommended)
else
"#"
end
end
def render_recommendation_image(recommended) def render_recommendation_image(recommended)
image_path = calculate_image_path(recommended) image_path = calculate_image_path(recommended)
image_tag(image_path) if image_path.present? image_tag(image_path) if image_path.present?

View File

@@ -1,16 +0,0 @@
<% if can?(:manage_mailing, proposal) %>
<div class="small-12 medium-6 large-3 column end">
<div class="resource-card" data-equalizer-watch="resources">
<h4><%= t("dashboard.menu.mailing") %></h4>
<p class="resource-description">
<%= Setting["proposals.email_short_title"] %>
</p>
<div class="small-12 column small-centered margin-top">
<%= link_to t("dashboard.resource.view_resource"),
new_proposal_dashboard_mailing_path(proposal.to_param),
class: "button expanded" %>
</div>
</div>
</div>
<% end %>

View File

@@ -1,16 +0,0 @@
<% if can?(:manage_polls, proposal) %>
<div class="small-12 medium-6 large-3 column end">
<div class="resource-card" data-equalizer-watch="resources">
<h4><%= t("dashboard.menu.polls") %></h4>
<p class="resource-description">
<%= Setting["proposals.poll_short_title"] %>
</p>
<div class="small-12 column small-centered margin-top">
<%= link_to t("dashboard.resource.view_resource"),
proposal_dashboard_polls_path(proposal.to_param),
class: "button expanded" %>
</div>
</div>
</div>
<% end %>

View File

@@ -1,16 +0,0 @@
<% if can?(:manage_poster, proposal) %>
<div class="small-12 medium-6 large-3 column end">
<div class="resource-card" data-equalizer-watch="resources">
<h4><%= t("dashboard.menu.poster") %></h4>
<p class="resource-description">
<%= Setting["proposals.poster_short_title"] %>
</p>
<div class="small-12 column small-centered margin-top">
<%= link_to t("dashboard.resource.view_resource"),
new_proposal_dashboard_poster_path(proposal.to_param),
class: "button expanded" %>
</div>
</div>
</div>
<% end %>

View File

@@ -1,31 +0,0 @@
<div id="<%= dom_id(resource) %>" class="small-12 medium-6 large-3 column end">
<div class="resource-card <%= resource_card_class(resource, proposal) %>"
data-equalizer-watch="resources"
title="<%= resource_tooltip(resource, proposal) %>">
<h4><%= resource.title %></h4>
<% if is_new_action_since_last_login?(resource, @new_actions_since_last_login) %>
<span class="label"><%= t("dashboard.progress.new_action") %></span>
<% end %>
<p class="resource-description">
<%= resource.short_description %>
</p>
<div class="small-12 column small-centered margin-top">
<% if resource.executed_for?(proposal) || (!resource.request_to_administrators && resource.active_for?(proposal)) %>
<%= link_to t("dashboard.resource.view_resource"),
new_request_proposal_dashboard_action_path(proposal, resource),
class: "button expanded" %>
<% elsif resource.requested_for?(proposal) %>
<strong><%= t("dashboard.resource.resource_requested") %></strong>
<% elsif resource.active_for?(proposal) %>
<%= link_to t("dashboard.resource.request_resource"),
new_request_proposal_dashboard_action_path(proposal, resource),
class: "button expanded" %>
<% else %>
<strong>
<%= resource_availability_label(resource) %>
</strong>
<% end %>
</div>
</div>
</div>

View File

@@ -1,10 +0,0 @@
<div id="available-resources-section">
<h3 class="title"><%= t("dashboard.resources.available_resources") %></h3>
<div data-equalizer="resources" data-equalize-on="medium">
<%= render "poll_resource" %>
<%= render "mailing_resource" %>
<%= render "poster_resource" %>
<%= render partial: "resource", collection: active_resources %>
</div>
</div>

View File

@@ -1,36 +0,0 @@
<div id="<%= dom_id(poll) %>" class="small-12 medium-6 large-4 column end">
<div class="poll-card" data-equalizer-watch="poll-cards">
<h4><%= link_to poll.title, proposal_poll_path(proposal, poll) %></h4>
<span class="small">
<%= l(poll.starts_at.to_date) %> - <%= l(poll.ends_at.to_date) %>
</span>
<p class="margin-top">
<strong><%= t("dashboard.polls.poll.responses", count: poll.answer_count) %></strong>
</p>
<div class="small-12 column small-centered margin-top">
<% if poll.starts_at.to_date >= Date.current %>
<%= link_to t("dashboard.polls.poll.edit_poll"),
edit_proposal_dashboard_poll_path(proposal, poll), class: "button hollow" %>
<% else %>
<%= link_to t("dashboard.polls.poll.view_results"),
results_proposal_poll_path(proposal, poll),
class: "button" %>
<% end %>
</div>
<%= form_for poll, remote: true,
data: { type: :json },
url: proposal_dashboard_poll_path(proposal, poll) do |f| %>
<%= f.check_box :results_enabled, class: "js-submit-on-change" %>
<% end %>
<p class="help-text"><%= t("dashboard.polls.poll.show_results_help") %></p>
<%= button_to t("dashboard.polls.poll.delete"),
proposal_dashboard_poll_path(proposal, poll),
method: :delete,
"data-confirm": t("dashboard.polls.poll.alert_notice"),
class: "delete" %>
</div>
</div>

View File

@@ -3,11 +3,7 @@
<div class="small-12 medium-9 column"> <div class="small-12 medium-9 column">
<%= Setting["proposals.poll_description"] %> <%= Setting["proposals.poll_description"] %>
<% if @polls.any? %> <%= render Dashboard::PollsComponent.new(@polls) %>
<div class="row expanded margin-top" data-equalizer="poll-cards" data-equalize-on="medium">
<%= render @polls %>
</div>
<% end %>
</div> </div>
<div class="small-12 medium-3 column"> <div class="small-12 medium-3 column">

View File

@@ -38,4 +38,4 @@
<%= render "summary_recommended_actions" %> <%= render "summary_recommended_actions" %>
<% end %> <% end %>
<%= render "resources" %> <%= render Dashboard::ResourcesComponent.new(proposal, @new_actions_since_last_login) %>

View File

@@ -18,11 +18,7 @@
<%= render "shared/section_header", i18n_namespace: "debates.index.section_header", image: "debates" %> <%= render "shared/section_header", i18n_namespace: "debates.index.section_header", image: "debates" %>
<% end %> <% end %>
<% if feature?("user.recommendations") && @recommended_debates.present? %> <%= render Shared::RecommendedIndexComponent.new(@recommended_debates, namespace: "debates") %>
<%= render "shared/recommended_index", recommended: @recommended_debates,
disable_recommendations_path: recommendations_disable_debates_path,
namespace: "debates" %>
<% end %>
<div class="row"> <div class="row">
<div id="debates" class="debates-list small-12 medium-9 column"> <div id="debates" class="debates-list small-12 medium-9 column">

View File

@@ -1,22 +1 @@
<% provide :title, @poll.name %> <%= render Polls::ResultsComponent.new(@poll) %>
<div class="polls-results-stats">
<%= render Polls::PollHeaderComponent.new(@poll) %>
<%= render "poll_subnav" %>
<div class="row margin" data-equalizer data-equalize-on="medium">
<div class="small-12 medium-3 column sidebar" data-equalizer-watch>
<p><strong><%= t("polls.show.results.title") %></strong></p>
<ul class="menu vertical">
<%- @poll.questions.each do |question| %>
<li><%= link_to question.title, "##{question.title.parameterize}" %></li>
<% end %>
</ul>
</div>
<div class="small-12 medium-9 column" data-equalizer-watch>
<%= render Polls::Results::QuestionComponent.with_collection(@poll.questions) %>
</div>
</div>
</div>

View File

@@ -1,6 +1,6 @@
<% provide :title, @poll.name %> <% provide :title, @poll.name %>
<div class="participation-stats polls-results-stats"> <div class="participation-stats polls-stats">
<%= render Polls::PollHeaderComponent.new(@poll) %> <%= render Polls::PollHeaderComponent.new(@poll) %>
<%= render "poll_subnav" %> <%= render "poll_subnav" %>

View File

@@ -25,9 +25,7 @@
<% end %> <% end %>
<% if show_recommended_proposals? %> <% if show_recommended_proposals? %>
<%= render "shared/recommended_index", recommended: @recommended_proposals, <%= render Shared::RecommendedIndexComponent.new(@recommended_proposals, namespace: "proposals") %>
disable_recommendations_path: recommendations_disable_proposals_path,
namespace: "proposals" %>
<% end %> <% end %>
<div class="row"> <div class="row">

View File

@@ -1,34 +0,0 @@
<div class="recommended-index">
<div class="row relative" data-equalizer data-equalizer-on="medium">
<div class="small-12 column">
<h2 class="show-for-sr"><%= t("shared.recommended_index.title") %></h2>
</div>
<div id="recommendations" data-toggler=".hide">
<%= button_to disable_recommendations_path, title: t("shared.recommended_index.hide"),
class: "hide-recommendations",
data: {
toggle: "recommendations",
confirm: t("#{namespace}.index.recommendations.disable")
},
method: :put do %>
<span class="icon-x"></span>
<span class="show-for-sr"><%= t("shared.recommended_index.hide") %></span>
<% end %>
<% recommended.each_with_index do |recommended, index| %>
<div class="small-12 medium-6 large-4 column end">
<div class="recommendation" data-equalizer-watch>
<h3><%= link_to recommended.title, recommended_path(recommended) %></h3>
</div>
</div>
<% end %>
<div class="small-12 column">
<%= link_to t("shared.recommended_index.see_more"),
current_path_with_query_params(order: "recommendations"),
class: "float-right-medium small" %>
</div>
</div>
</div>
</div>

View File

@@ -13,7 +13,7 @@
<div class="card"> <div class="card">
<%= render_recommendation_image(recommended) %> <%= render_recommendation_image(recommended) %>
<div class="card-section"> <div class="card-section">
<%= link_to recommended_path(recommended) do %> <%= link_to polymorphic_path(recommended) do %>
<h4 class="truncate-horizontal-text"><%= recommended.title %></h4> <h4 class="truncate-horizontal-text"><%= recommended.title %></h4>
<% end %> <% end %>
<%= wysiwyg(recommended.description) %> <%= wysiwyg(recommended.description) %>

View File

@@ -0,0 +1,51 @@
require "rails_helper"
describe Dashboard::PollComponent do
include Rails.application.routes.url_helpers
let(:proposal) { create(:proposal, :draft) }
before { sign_in(proposal.author) }
describe "Poll card content" do
describe "actions visibility" do
it "shows results link for current polls" do
current = create(:poll, related: proposal)
render_inline Dashboard::PollComponent.new(current)
expect(page).not_to have_link "Edit survey"
expect(page).to have_link "View results", href: results_proposal_poll_path(proposal, current)
end
it "shows results link for expired polls" do
expired = create(:poll, :expired, related: proposal)
render_inline Dashboard::PollComponent.new(expired)
expect(page).not_to have_link "Edit survey"
expect(page).to have_link "View results", href: results_proposal_poll_path(proposal, expired)
end
it "shows edit link for upcoming polls" do
upcoming = create(:poll, related: proposal, starts_at: 1.week.from_now)
render_inline Dashboard::PollComponent.new(upcoming)
expect(page).to have_link "Edit survey", href: edit_proposal_dashboard_poll_path(proposal, upcoming)
expect(page).not_to have_link "View results"
end
end
it "renders poll title and dates" do
expired = create(:poll, :expired, related: proposal)
render_inline Dashboard::PollComponent.new(expired)
expect(page).to have_content I18n.l(expired.starts_at.to_date)
expect(page).to have_content I18n.l(expired.ends_at.to_date)
expect(page).to have_link expired.title
expect(page).to have_link expired.title, href: proposal_poll_path(proposal, expired)
end
end
end

View File

@@ -0,0 +1,9 @@
require "rails_helper"
describe Dashboard::PollsComponent do
it "is not rendered when there aren't any polls" do
render_inline Dashboard::PollsComponent.new(Poll.none)
expect(page).not_to be_rendered
end
end

View File

@@ -0,0 +1,131 @@
require "rails_helper"
describe Dashboard::ResourcesComponent do
let(:proposal) { create(:proposal, :draft) }
before { sign_in(proposal.author) }
describe "Available resources section" do
let!(:available) { create(:dashboard_action, :resource, :active, title: "Available!") }
let(:requested) { create(:dashboard_action, :resource, :admin_request, :active, title: "Requested!") }
let(:executed_action) do
create(
:dashboard_executed_action,
action: requested,
proposal: proposal,
executed_at: Time.current
)
end
let(:solved) { create(:dashboard_action, :resource, :admin_request, :active, title: "Solved!") }
let(:executed_solved_action) do
create(
:dashboard_executed_action,
action: solved,
proposal: proposal,
executed_at: Time.current
)
end
let!(:unavailable) do
create(
:dashboard_action,
:resource,
:active,
required_supports: proposal.votes_for.size + 1_000,
title: "Unavailable!"
)
end
before do
create(:dashboard_administrator_task, :pending, source: executed_action)
create(:dashboard_administrator_task, :done, source: executed_solved_action)
end
it "shows available resources for proposal drafts" do
render_inline Dashboard::ResourcesComponent.new(proposal)
expect(page).to have_content "Polls"
expect(page).to have_content "E-mail"
expect(page).to have_content "Poster"
expect(page).to have_content available.title
expect(page).to have_content unavailable.title
expect(page).to have_content requested.title
expect(page).to have_content solved.title
page.find(".resource-card", text: "Available!") do |dashboard_action_available|
expect(dashboard_action_available).to have_link "See resource"
end
page.find(".resource-card", text: "Requested!") do |dashboard_action_requested|
expect(dashboard_action_requested).to have_content "Resource already requested"
end
page.find(".resource-card", text: "Unavailable!") do |dashboard_action_unavailable|
expect(dashboard_action_unavailable).to have_content "1.000 supports required"
end
page.find(".resource-card", text: "Solved!") do |dashboard_action_solved|
expect(dashboard_action_solved).to have_link "See resource"
end
end
it "shows available resources for published proposals" do
proposal.update!(published_at: Date.current)
render_inline Dashboard::ResourcesComponent.new(proposal)
expect(page).to have_content "Polls"
expect(page).to have_content "E-mail"
expect(page).to have_content "Poster"
expect(page).to have_content available.title
expect(page).to have_content unavailable.title
expect(page).to have_content requested.title
expect(page).to have_content solved.title
page.find(".resource-card", text: "Available!") do |dashboard_action_available|
expect(dashboard_action_available).to have_link "See resource"
end
page.find(".resource-card", text: "Requested!") do |dashboard_action_requested|
expect(dashboard_action_requested).to have_content "Resource already requested"
end
page.find(".resource-card", text: "Unavailable!") do |dashboard_action_unavailable|
expect(dashboard_action_unavailable).to have_content "1.000 supports required"
end
page.find(".resource-card", text: "Solved!") do |dashboard_action_solved|
expect(dashboard_action_solved).to have_link "See resource"
end
end
it "does not show resources with published_proposal: true" do
available.update!(published_proposal: true)
unavailable.update!(published_proposal: true)
render_inline Dashboard::ResourcesComponent.new(proposal)
expect(page).to have_content "Polls"
expect(page).to have_content "E-mail"
expect(page).to have_content "Poster"
expect(page).not_to have_content available.title
expect(page).not_to have_content unavailable.title
end
end
describe "Tags for new actions" do
it "displays tag 'new' only when resource is detected like new action" do
create(:dashboard_action, :resource, :active, title: "Old!")
new_resource = create(:dashboard_action, :resource, :active, title: "Recent!")
new_actions_since_last_login = [new_resource.id]
render_inline Dashboard::ResourcesComponent.new(proposal, new_actions_since_last_login)
page.find(".resource-card", text: "Recent!") do |dashboard_action_new_resource|
expect(dashboard_action_new_resource).to have_content "New"
end
page.find(".resource-card", text: "Old!") do |dashboard_action_old_resource|
expect(dashboard_action_old_resource).not_to have_content "New"
end
end
end
end

View File

@@ -0,0 +1,48 @@
require "rails_helper"
describe Polls::AccessStatusComponent do
it "asks anonymous users to sign in" do
render_inline Polls::AccessStatusComponent.new(create(:poll))
expect(page).to have_css ".not-logged-in", count: 1
expect(page).to have_content "You must sign in or sign up to participate"
end
it "asks unverified users to verify their account" do
sign_in(create(:user))
render_inline Polls::AccessStatusComponent.new(create(:poll))
expect(page).to have_css ".unverified", count: 1
expect(page).to have_content "You must verify your account to participate"
end
it "tell users from different geozones that the poll isn't available" do
sign_in(create(:user, :level_two))
render_inline Polls::AccessStatusComponent.new(create(:poll, geozone_restricted: true))
expect(page).to have_css ".cant-answer", count: 1
expect(page).to have_content "This poll is not available on your geozone"
end
it "informs users when they've already participated" do
user = create(:user, :level_two)
poll = create(:poll)
create(:poll_voter, user: user, poll: poll)
sign_in(user)
render_inline Polls::AccessStatusComponent.new(poll)
expect(page).to have_css ".already-answer", count: 1
expect(page).to have_content "You already have participated in this poll"
end
it "is not rendered when users can vote" do
sign_in(create(:user, :level_two))
render_inline Polls::AccessStatusComponent.new(create(:poll))
expect(page).not_to be_rendered
end
end

View File

@@ -3,45 +3,6 @@ require "rails_helper"
describe Polls::PollComponent do describe Polls::PollComponent do
include Rails.application.routes.url_helpers include Rails.application.routes.url_helpers
describe "status message" do
it "asks anonymous users to sign in" do
render_inline Polls::PollComponent.new(create(:poll))
expect(page).to have_css ".not-logged-in", count: 1
expect(page).to have_content "You must sign in or sign up to participate"
end
it "asks unverified users to verify their account" do
sign_in(create(:user))
render_inline Polls::PollComponent.new(create(:poll))
expect(page).to have_css ".unverified", count: 1
expect(page).to have_content "You must verify your account to participate"
end
it "tell users from different geozones that the poll isn't available" do
sign_in(create(:user, :level_two))
render_inline Polls::PollComponent.new(create(:poll, geozone_restricted: true))
expect(page).to have_css ".cant-answer", count: 1
expect(page).to have_content "This poll is not available on your geozone"
end
it "informs users when they've already participated" do
user = create(:user, :level_two)
poll = create(:poll)
create(:poll_voter, user: user, poll: poll)
sign_in(user)
render_inline Polls::PollComponent.new(poll)
expect(page).to have_css ".already-answer", count: 1
expect(page).to have_content "You already have participated in this poll"
end
end
describe "dates" do describe "dates" do
it "renders the dates inside an HTML tag" do it "renders the dates inside an HTML tag" do
poll = create(:poll, starts_at: "2015-07-15", ends_at: "2015-07-22") poll = create(:poll, starts_at: "2015-07-15", ends_at: "2015-07-22")

View File

@@ -138,116 +138,6 @@ describe "Proposal's dashboard" do
expect(page).not_to have_content(action.title) expect(page).not_to have_content(action.title)
end end
scenario "Dashboard progress show available resources for proposal draft" do
available = create(:dashboard_action, :resource, :active)
requested = create(:dashboard_action, :resource, :admin_request, :active)
executed_action = create(:dashboard_executed_action, action: requested,
proposal: proposal,
executed_at: Time.current)
_task = create(:dashboard_administrator_task, :pending, source: executed_action)
solved = create(:dashboard_action, :resource, :admin_request, :active)
executed_solved_action = create(:dashboard_executed_action, action: solved,
proposal: proposal,
executed_at: Time.current)
_solved_task = create(:dashboard_administrator_task, :done, source: executed_solved_action)
unavailable = create(:dashboard_action, :resource, :active,
required_supports: proposal.votes_for.size + 1_000)
visit progress_proposal_dashboard_path(proposal)
within "div#available-resources-section" do
expect(page).to have_content("Polls")
expect(page).to have_content("E-mail")
expect(page).to have_content("Poster")
expect(page).to have_content(available.title)
expect(page).to have_content(unavailable.title)
expect(page).to have_content(requested.title)
expect(page).to have_content(solved.title)
within "div#dashboard_action_#{available.id}" do
expect(page).to have_link "See resource"
end
within "div#dashboard_action_#{requested.id}" do
expect(page).to have_content("Resource already requested")
end
within "div#dashboard_action_#{unavailable.id}" do
expect(page).to have_content("1.000 supports required")
end
within "div#dashboard_action_#{solved.id}" do
expect(page).to have_link("See resource")
end
end
end
scenario "Dashboard progress show available resources for published proposal" do
proposal.update!(published_at: Date.current)
available = create(:dashboard_action, :resource, :active)
requested = create(:dashboard_action, :resource, :admin_request, :active)
executed_action = create(:dashboard_executed_action, action: requested,
proposal: proposal,
executed_at: Time.current)
_task = create(:dashboard_administrator_task, :pending, source: executed_action)
solved = create(:dashboard_action, :resource, :admin_request, :active)
executed_solved_action = create(:dashboard_executed_action, action: solved,
proposal: proposal,
executed_at: Time.current)
_solved_task = create(:dashboard_administrator_task, :done, source: executed_solved_action)
unavailable = create(:dashboard_action, :resource, :active,
required_supports: proposal.votes_for.size + 1_000)
visit progress_proposal_dashboard_path(proposal)
within "div#available-resources-section" do
expect(page).to have_content("Polls")
expect(page).to have_content("E-mail")
expect(page).to have_content("Poster")
expect(page).to have_content(available.title)
expect(page).to have_content(unavailable.title)
expect(page).to have_content(requested.title)
expect(page).to have_content(solved.title)
within "div#dashboard_action_#{available.id}" do
expect(page).to have_link "See resource"
end
within "div#dashboard_action_#{requested.id}" do
expect(page).to have_content("Resource already requested")
end
within "div#dashboard_action_#{unavailable.id}" do
expect(page).to have_content("1.000 supports required")
end
within "div#dashboard_action_#{solved.id}" do
expect(page).to have_link("See resource")
end
end
end
scenario "Dashboard progress dont show resources with published_proposal: true" do
available = create(:dashboard_action, :resource, :active, published_proposal: true)
unavailable = create(:dashboard_action, :resource, :active,
required_supports: proposal.votes_for.size + 1_000,
published_proposal: true)
visit progress_proposal_dashboard_path(proposal)
within "div#available-resources-section" do
expect(page).to have_content("Polls")
expect(page).to have_content("E-mail")
expect(page).to have_content("Poster")
expect(page).not_to have_content(available.title)
expect(page).not_to have_content(unavailable.title)
end
end
scenario "Dashboard has a link to resources on main menu" do scenario "Dashboard has a link to resources on main menu" do
feature = create(:dashboard_action, :resource, :active) feature = create(:dashboard_action, :resource, :active)
@@ -480,29 +370,6 @@ describe "Proposal's dashboard" do
describe "detect_new_actions_after_last_login" do describe "detect_new_actions_after_last_login" do
before { proposal.author.update!(current_sign_in_at: 1.day.ago) } before { proposal.author.update!(current_sign_in_at: 1.day.ago) }
scenario "Display tag 'new' on resouce when it is new for author since last login" do
resource = create(:dashboard_action, :resource, :active, day_offset: 0,
published_proposal: false)
visit progress_proposal_dashboard_path(proposal)
within "#dashboard_action_#{resource.id}" do
expect(page).to have_content("New")
end
end
scenario "Not display tag 'new' on resouce when there is not new resources since last login" do
resource = create(:dashboard_action, :resource, :active, day_offset: 0,
published_proposal: false)
proposal.author.update!(current_sign_in_at: Date.current)
visit progress_proposal_dashboard_path(proposal)
within "#dashboard_action_#{resource.id}" do
expect(page).not_to have_content("New")
end
end
scenario "Display tag 'new' on proposed_action when it is new for author since last login" do scenario "Display tag 'new' on proposed_action when it is new for author since last login" do
proposed_action = create(:dashboard_action, :proposed_action, :active, day_offset: 0, proposed_action = create(:dashboard_action, :proposed_action, :active, day_offset: 0,
published_proposal: false) published_proposal: false)

View File

@@ -123,26 +123,6 @@ describe "Polls" do
expect(page).to have_content("Edit poll") expect(page).to have_content("Edit poll")
end end
scenario "Edit poll is not allowed for current polls" do
poll = create(:poll, related: proposal)
visit proposal_dashboard_polls_path(proposal)
within "div#poll_#{poll.id}" do
expect(page).not_to have_content("Edit survey")
end
end
scenario "Edit poll is not allowed for expired polls" do
poll = create(:poll, :expired, related: proposal)
visit proposal_dashboard_polls_path(proposal)
within "div#poll_#{poll.id}" do
expect(page).not_to have_content("Edit survey")
end
end
scenario "Edit poll should allow to remove questions" do scenario "Edit poll should allow to remove questions" do
poll = create(:poll, related: proposal, starts_at: 1.week.from_now) poll = create(:poll, related: proposal, starts_at: 1.week.from_now)
create(:poll_question, poll: poll) create(:poll_question, poll: poll)
@@ -224,36 +204,6 @@ describe "Polls" do
expect(page).to have_content(poll.name) expect(page).to have_content(poll.name)
end end
scenario "View results not available for upcoming polls" do
poll = create(:poll, related: proposal, starts_at: 1.week.from_now)
visit proposal_dashboard_polls_path(proposal)
within "div#poll_#{poll.id}" do
expect(page).not_to have_content("View results")
end
end
scenario "View results available for current polls" do
poll = create(:poll, related: proposal)
visit proposal_dashboard_polls_path(proposal)
within "div#poll_#{poll.id}" do
expect(page).to have_content("View results")
end
end
scenario "View results available for expired polls" do
poll = create(:poll, :expired, related: proposal)
visit proposal_dashboard_polls_path(proposal)
within "div#poll_#{poll.id}" do
expect(page).to have_content("View results")
end
end
scenario "View results redirects to results in public zone" do scenario "View results redirects to results in public zone" do
poll = create(:poll, :expired, related: proposal) poll = create(:poll, :expired, related: proposal)
@@ -276,18 +226,4 @@ describe "Polls" do
expect(find_field("Show results")).not_to be_checked expect(find_field("Show results")).not_to be_checked
end end
scenario "Poll card" do
poll = create(:poll, :expired, related: proposal)
visit proposal_dashboard_polls_path(proposal)
within "div#poll_#{poll.id}" do
expect(page).to have_content(I18n.l(poll.starts_at.to_date))
expect(page).to have_content(I18n.l(poll.ends_at.to_date))
expect(page).to have_link(poll.title)
expect(page).to have_link(poll.title, href: proposal_poll_path(proposal, poll))
expect(page).to have_link("View results", href: results_proposal_poll_path(proposal, poll))
end
end
end end