diff --git a/.stylelintrc.yml b/.stylelintrc.yml index c5ba33f96..071155eba 100644 --- a/.stylelintrc.yml +++ b/.stylelintrc.yml @@ -83,6 +83,7 @@ rules: "@stylistic/indentation": - 2 - ignore: + - param - value "@stylistic/media-feature-parentheses-space-inside": never "@stylistic/no-eol-whitespace": true diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 1be22b348..8a9be181e 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -21,6 +21,7 @@ @import "account/**/*"; @import "budgets/**/*"; @import "comments/**/*"; +@import "dashboard/**/*"; @import "debates/**/*"; @import "devise/**/*"; @import "documents/**/*"; diff --git a/app/assets/stylesheets/dashboard.scss b/app/assets/stylesheets/dashboard.scss index 3233d17a8..cbcb40f88 100644 --- a/app/assets/stylesheets/dashboard.scss +++ b/app/assets/stylesheets/dashboard.scss @@ -2,7 +2,6 @@ // // 01. Dashboard global // 02. Actions -// 03. Resources // 04. Goals // 05. Sidebar // 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 // --------- @@ -429,28 +372,6 @@ 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 { border-bottom: 1px solid $border; margin-bottom: $line-height; diff --git a/app/assets/stylesheets/dashboard/poll.scss b/app/assets/stylesheets/dashboard/poll.scss new file mode 100644 index 000000000..754194621 --- /dev/null +++ b/app/assets/stylesheets/dashboard/poll.scss @@ -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; + } + } +} diff --git a/app/assets/stylesheets/dashboard/polls.scss b/app/assets/stylesheets/dashboard/polls.scss new file mode 100644 index 000000000..399bd3a73 --- /dev/null +++ b/app/assets/stylesheets/dashboard/polls.scss @@ -0,0 +1,4 @@ +.dashboard-polls { + @include dynamic-grid; + margin-top: $line-height; +} diff --git a/app/assets/stylesheets/dashboard/resources.scss b/app/assets/stylesheets/dashboard/resources.scss new file mode 100644 index 000000000..48d3da8ab --- /dev/null +++ b/app/assets/stylesheets/dashboard/resources.scss @@ -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; + } + } + } +} diff --git a/app/assets/stylesheets/layout.scss b/app/assets/stylesheets/layout.scss index b3eae06c4..0c69d0bae 100644 --- a/app/assets/stylesheets/layout.scss +++ b/app/assets/stylesheets/layout.scss @@ -403,18 +403,10 @@ button, vertical-align: top; } -.align-middle { - vertical-align: middle; -} - .table { display: table; } -.table-cell { - display: table-cell; -} - .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 // ------------------- @@ -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 // ------------- diff --git a/app/assets/stylesheets/mixins/layouts.scss b/app/assets/stylesheets/mixins/layouts.scss index 27fa294f1..a54c36fcc 100644 --- a/app/assets/stylesheets/mixins/layouts.scss +++ b/app/assets/stylesheets/mixins/layouts.scss @@ -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) { $global-padding: rem-calc(map-get($grid-column-gutter, medium)) * 0.5; bottom: 0; diff --git a/app/assets/stylesheets/participation.scss b/app/assets/stylesheets/participation.scss index 09b22bd4f..73ad32cdd 100644 --- a/app/assets/stylesheets/participation.scss +++ b/app/assets/stylesheets/participation.scss @@ -1390,12 +1390,6 @@ } } -.image-container { - background: #fafafa; - overflow: hidden; - position: relative; -} - .public .poll { border: 1px solid $border; margin-bottom: calc($line-height / 2); @@ -1415,7 +1409,8 @@ // 09. Polls results and stats // --------------------------- -.polls-results-stats { +.polls-results, +.polls-stats { table { table-layout: fixed; diff --git a/app/assets/stylesheets/polls/access_status.scss b/app/assets/stylesheets/polls/access_status.scss new file mode 100644 index 000000000..24391e76e --- /dev/null +++ b/app/assets/stylesheets/polls/access_status.scss @@ -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"; + } + } +} diff --git a/app/assets/stylesheets/polls/poll.scss b/app/assets/stylesheets/polls/poll.scss index 8e3446ef7..3cf046f2d 100644 --- a/app/assets/stylesheets/polls/poll.scss +++ b/app/assets/stylesheets/polls/poll.scss @@ -2,69 +2,37 @@ &.with-image { @include breakpoint(medium) { + display: flex; padding: 0 calc($line-height / 2) 0 0; - } - .image-container img { - height: 100%; - max-width: none; - position: absolute; + > * { + flex-basis: 25%; + } + + .poll-info { + flex-basis: 50%; + } + + > a { + align-self: center; + } + + > .poll-info, + > a { + margin-#{$global-left}: rem-calc(map-get($grid-column-gutter, medium)); + } } } - .icon-poll-answer { - border-bottom: 60px solid transparent; - border-top: 0; - height: 0; - position: absolute; - right: 0; - top: 0; - width: 0; + .image-container { + background: #fafafa; + overflow: hidden; + position: relative; - &.cant-answer::after, - &.not-logged-in::after, - &.already-answer::after, - &.unverified::after { - font-family: "icons" !important; - left: 34px; + img { + height: 100%; + max-width: none; 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"; - } } } diff --git a/app/assets/stylesheets/polls/results.scss b/app/assets/stylesheets/polls/results.scss new file mode 100644 index 000000000..769199149 --- /dev/null +++ b/app/assets/stylesheets/polls/results.scss @@ -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; + } + } +} diff --git a/app/assets/stylesheets/shared/recommended_index.scss b/app/assets/stylesheets/shared/recommended_index.scss new file mode 100644 index 000000000..c8b4b7385 --- /dev/null +++ b/app/assets/stylesheets/shared/recommended_index.scss @@ -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; + } + } +} diff --git a/app/assets/stylesheets/users/following.scss b/app/assets/stylesheets/users/following.scss new file mode 100644 index 000000000..0c325cd00 --- /dev/null +++ b/app/assets/stylesheets/users/following.scss @@ -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; + } + } +} diff --git a/app/components/dashboard/active_resource_component.html.erb b/app/components/dashboard/active_resource_component.html.erb new file mode 100644 index 000000000..0c4faec31 --- /dev/null +++ b/app/components/dashboard/active_resource_component.html.erb @@ -0,0 +1,23 @@ +
+

<%= resource.title %>

+ <% if is_new_action_since_last_login?(resource, new_actions_since_last_login) %> + <%= t("dashboard.progress.new_action") %> + <% end %> +

<%= resource.short_description %>

+ + <% 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) %> + <%= t("dashboard.resource.resource_requested") %> + <% elsif resource.active_for?(proposal) %> + <%= link_to t("dashboard.resource.request_resource"), + new_request_proposal_dashboard_action_path(proposal, resource), + class: "button" %> + <% else %> + <%= resource_availability_label %> + <% end %> +
diff --git a/app/components/dashboard/active_resource_component.rb b/app/components/dashboard/active_resource_component.rb new file mode 100644 index 000000000..26726af9a --- /dev/null +++ b/app/components/dashboard/active_resource_component.rb @@ -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 diff --git a/app/components/dashboard/default_resource_component.html.erb b/app/components/dashboard/default_resource_component.html.erb new file mode 100644 index 000000000..6d57a00ec --- /dev/null +++ b/app/components/dashboard/default_resource_component.html.erb @@ -0,0 +1,6 @@ +
+

<%= t("dashboard.menu.#{resource}") %>

+

<%= resource_description %>

+ + <%= link_to t("dashboard.resource.view_resource"), resource_path, class: "button" %> +
diff --git a/app/components/dashboard/default_resource_component.rb b/app/components/dashboard/default_resource_component.rb new file mode 100644 index 000000000..ae14d7e89 --- /dev/null +++ b/app/components/dashboard/default_resource_component.rb @@ -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 diff --git a/app/components/dashboard/poll_component.html.erb b/app/components/dashboard/poll_component.html.erb new file mode 100644 index 000000000..0dadf4f54 --- /dev/null +++ b/app/components/dashboard/poll_component.html.erb @@ -0,0 +1,34 @@ +
+

<%= link_to poll.title, proposal_poll_path(proposal, poll) %>

+ + <%= l(poll.starts_at.to_date) %> - <%= l(poll.ends_at.to_date) %> + + +

+ <%= t("dashboard.polls.poll.responses", count: poll.answer_count) %> +

+ +
+ <% 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 %> +
+ + <%= 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 %> +

<%= t("dashboard.polls.poll.show_results_help") %>

+ + <%= 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" %> +
diff --git a/app/components/dashboard/poll_component.rb b/app/components/dashboard/poll_component.rb new file mode 100644 index 000000000..8990576f2 --- /dev/null +++ b/app/components/dashboard/poll_component.rb @@ -0,0 +1,13 @@ +class Dashboard::PollComponent < ApplicationComponent + attr_reader :poll + + def initialize(poll) + @poll = poll + end + + private + + def proposal + poll.related + end +end diff --git a/app/components/dashboard/polls_component.html.erb b/app/components/dashboard/polls_component.html.erb new file mode 100644 index 000000000..9c79103e8 --- /dev/null +++ b/app/components/dashboard/polls_component.html.erb @@ -0,0 +1,5 @@ +
+ <% polls.each do |poll| %> + <%= render Dashboard::PollComponent.new(poll) %> + <% end %> +
diff --git a/app/components/dashboard/polls_component.rb b/app/components/dashboard/polls_component.rb new file mode 100644 index 000000000..4dad2c277 --- /dev/null +++ b/app/components/dashboard/polls_component.rb @@ -0,0 +1,11 @@ +class Dashboard::PollsComponent < ApplicationComponent + attr_reader :polls + + def initialize(polls) + @polls = polls + end + + def render? + polls.any? + end +end diff --git a/app/components/dashboard/resources_component.html.erb b/app/components/dashboard/resources_component.html.erb new file mode 100644 index 000000000..05df43d05 --- /dev/null +++ b/app/components/dashboard/resources_component.html.erb @@ -0,0 +1,12 @@ +
+

<%= t("dashboard.resources.available_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 %> +
+
diff --git a/app/components/dashboard/resources_component.rb b/app/components/dashboard/resources_component.rb new file mode 100644 index 000000000..8517653a2 --- /dev/null +++ b/app/components/dashboard/resources_component.rb @@ -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 diff --git a/app/components/polls/access_status_component.html.erb b/app/components/polls/access_status_component.html.erb new file mode 100644 index 000000000..d82f5d669 --- /dev/null +++ b/app/components/polls/access_status_component.html.erb @@ -0,0 +1,3 @@ +
+ <%= text %> +
diff --git a/app/components/polls/access_status_component.rb b/app/components/polls/access_status_component.rb new file mode 100644 index 000000000..f93c38a97 --- /dev/null +++ b/app/components/polls/access_status_component.rb @@ -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 diff --git a/app/components/polls/poll_component.html.erb b/app/components/polls/poll_component.html.erb index 110bef076..17002ba0c 100644 --- a/app/components/polls/poll_component.html.erb +++ b/app/components/polls/poll_component.html.erb @@ -1,56 +1,33 @@
- <% if current_user %> - <% if current_user.unverified? %> -
"> - <%= t("polls.index.unverified") %> -
- <% elsif cannot?(:answer, poll) %> -
"> - <%= t("polls.index.cant_answer") %> -
- <% elsif !poll.votable_by?(current_user) %> -
"> - <%= t("polls.index.already_answer") %> -
- <% end %> - <% else %> -
"> - <%= t("polls.index.not_logged_in") %> -
- <% end %> -
-
-
- <% if poll.image.present? %> - <%= image_tag poll.image.variant(:large), alt: poll.image.title.unicode_normalize %> - <% end %> -
-
-
- <% if poll.questions.one? %> -

<%= link_to_poll poll.questions.first.title, poll %>

-
<%= dates %>
- <% else %> -

<%= link_to_poll poll.name, poll %>

-
<%= dates %>
+ <%= render Polls::AccessStatusComponent.new(poll) %> -
    - <% poll.questions.sort_for_list.each do |question| %> -
  • <%= question.title %>
  • - <% end %> -
- <% end %> - <%= render Polls::GeozonesComponent.new(poll) %> - <%= render SDG::TagListComponent.new(poll, limit: 5, linkable: false) %> -
-
-
- <% if poll.expired? %> - <%= link_to_poll t("polls.index.participate_button_expired"), poll, class: "button hollow expanded" %> - <% else %> - <%= link_to_poll t("polls.index.participate_button"), poll, class: "button hollow expanded" %> - <% end %> -
-
+
+ <% if poll.image.present? %> + <%= image_tag poll.image.variant(:large), alt: poll.image.title.unicode_normalize %> + <% end %>
+ +
+ <% if poll.questions.one? %> +

<%= link_to_poll poll.questions.first.title, poll %>

+
<%= dates %>
+ <% else %> +

<%= link_to_poll poll.name, poll %>

+
<%= dates %>
+ +
    + <% poll.questions.sort_for_list.each do |question| %> +
  • <%= question.title %>
  • + <% end %> +
+ <% end %> + <%= render Polls::GeozonesComponent.new(poll) %> + <%= render SDG::TagListComponent.new(poll, limit: 5, linkable: false) %> +
+ + <% if poll.expired? %> + <%= link_to_poll t("polls.index.participate_button_expired"), poll, class: "button hollow expanded" %> + <% else %> + <%= link_to_poll t("polls.index.participate_button"), poll, class: "button hollow expanded" %> + <% end %>
diff --git a/app/components/polls/poll_component.rb b/app/components/polls/poll_component.rb index a1cda60ea..af70041d7 100644 --- a/app/components/polls/poll_component.rb +++ b/app/components/polls/poll_component.rb @@ -1,6 +1,6 @@ class Polls::PollComponent < ApplicationComponent attr_reader :poll - use_helpers :cannot?, :current_user, :link_to_poll + use_helpers :can? def initialize(poll) @poll = poll @@ -11,4 +11,14 @@ class Polls::PollComponent < ApplicationComponent def dates t("polls.dates", open_at: l(poll.starts_at.to_date), closed_at: l(poll.ends_at.to_date)) 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 diff --git a/app/components/polls/results_component.html.erb b/app/components/polls/results_component.html.erb new file mode 100644 index 000000000..28e33ad82 --- /dev/null +++ b/app/components/polls/results_component.html.erb @@ -0,0 +1,22 @@ +<% provide :title, poll.name %> + +
+ <%= render Polls::PollHeaderComponent.new(poll) %> + + <%= render "poll_subnav" %> + +
+
+

<%= t("polls.show.results.title") %>

+ +
+ +
+ <%= render Polls::Results::QuestionComponent.with_collection(poll.questions) %> +
+
+
diff --git a/app/components/polls/results_component.rb b/app/components/polls/results_component.rb new file mode 100644 index 000000000..9da3eb130 --- /dev/null +++ b/app/components/polls/results_component.rb @@ -0,0 +1,7 @@ +class Polls::ResultsComponent < ApplicationComponent + attr_reader :poll + + def initialize(poll) + @poll = poll + end +end diff --git a/app/components/shared/recommended_index_component.html.erb b/app/components/shared/recommended_index_component.html.erb new file mode 100644 index 000000000..a535dcbfc --- /dev/null +++ b/app/components/shared/recommended_index_component.html.erb @@ -0,0 +1,30 @@ + diff --git a/app/components/shared/recommended_index_component.rb b/app/components/shared/recommended_index_component.rb new file mode 100644 index 000000000..f22a87de3 --- /dev/null +++ b/app/components/shared/recommended_index_component.rb @@ -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 diff --git a/app/views/users/_following.html.erb b/app/components/users/following_component.html.erb similarity index 78% rename from app/views/users/_following.html.erb rename to app/components/users/following_component.html.erb index d09f99fe6..2a1e683f0 100644 --- a/app/views/users/_following.html.erb +++ b/app/components/users/following_component.html.erb @@ -1,5 +1,5 @@ -
-
+
+
-
+
<%= render "interests", user: user %>
diff --git a/app/components/users/following_component.rb b/app/components/users/following_component.rb new file mode 100644 index 000000000..94a04c6dd --- /dev/null +++ b/app/components/users/following_component.rb @@ -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 diff --git a/app/components/users/public_activity_component.html.erb b/app/components/users/public_activity_component.html.erb index 03554e9a6..86009368c 100644 --- a/app/components/users/public_activity_component.html.erb +++ b/app/components/users/public_activity_component.html.erb @@ -16,7 +16,7 @@ <% 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 %> <%= render_user_partial current_filter %> <% end %> diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index 5e2be5d03..aa0784f83 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -1,5 +1,5 @@ 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 :authorize_dashboard, except: :publish @@ -30,13 +30,6 @@ class DashboardController < Dashboard::BaseController private - def active_resources - @active_resources ||= Dashboard::Action.active - .resources - .by_proposal(proposal) - .order(required_supports: :asc, day_offset: :asc) - end - def course @course ||= Dashboard::Action.course_for(proposal) end diff --git a/app/helpers/followables_helper.rb b/app/helpers/followables_helper.rb index ab199567f..d4e81ef3b 100644 --- a/app/helpers/followables_helper.rb +++ b/app/helpers/followables_helper.rb @@ -1,26 +1,4 @@ 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) Follow.find_or_initialize_by(user: user, followable: followable) end diff --git a/app/helpers/polls_helper.rb b/app/helpers/polls_helper.rb index b6cadd423..9705c3fda 100644 --- a/app/helpers/polls_helper.rb +++ b/app/helpers/polls_helper.rb @@ -4,16 +4,6 @@ module PollsHelper booth.name + location 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? controller_name == "polls" && action_name == "results" end diff --git a/app/helpers/proposals_dashboard_helper.rb b/app/helpers/proposals_dashboard_helper.rb index f7144320a..dfac3b52f 100644 --- a/app/helpers/proposals_dashboard_helper.rb +++ b/app/helpers/proposals_dashboard_helper.rb @@ -56,18 +56,6 @@ module ProposalsDashboardHelper controller_name == "dashboard" && action_name == "new_request" && dashboard_action&.id == id 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 return nil if params[:group_by].blank? @@ -86,21 +74,6 @@ module ProposalsDashboardHelper "hollow" 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) sanitize proposed_action.description.truncate(200) end diff --git a/app/helpers/proposals_helper.rb b/app/helpers/proposals_helper.rb index cfd9a79b0..468cc788d 100644 --- a/app/helpers/proposals_helper.rb +++ b/app/helpers/proposals_helper.rb @@ -73,6 +73,6 @@ module ProposalsHelper end def show_recommended_proposals? - params[:selected].blank? && feature?("user.recommendations") && @recommended_proposals.present? + params[:selected].blank? end end diff --git a/app/helpers/welcome_helper.rb b/app/helpers/welcome_helper.rb index 85ee522da..cda8b110e 100644 --- a/app/helpers/welcome_helper.rb +++ b/app/helpers/welcome_helper.rb @@ -7,17 +7,6 @@ module WelcomeHelper "display: none;" if index.positive? 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) image_path = calculate_image_path(recommended) image_tag(image_path) if image_path.present? diff --git a/app/views/dashboard/_mailing_resource.html.erb b/app/views/dashboard/_mailing_resource.html.erb deleted file mode 100644 index 2ec0beb40..000000000 --- a/app/views/dashboard/_mailing_resource.html.erb +++ /dev/null @@ -1,16 +0,0 @@ -<% if can?(:manage_mailing, proposal) %> -
-
-

<%= t("dashboard.menu.mailing") %>

-

- <%= Setting["proposals.email_short_title"] %> -

- -
- <%= link_to t("dashboard.resource.view_resource"), - new_proposal_dashboard_mailing_path(proposal.to_param), - class: "button expanded" %> -
-
-
-<% end %> diff --git a/app/views/dashboard/_poll_resource.html.erb b/app/views/dashboard/_poll_resource.html.erb deleted file mode 100644 index 39d21ea8e..000000000 --- a/app/views/dashboard/_poll_resource.html.erb +++ /dev/null @@ -1,16 +0,0 @@ -<% if can?(:manage_polls, proposal) %> -
-
-

<%= t("dashboard.menu.polls") %>

-

- <%= Setting["proposals.poll_short_title"] %> -

- -
- <%= link_to t("dashboard.resource.view_resource"), - proposal_dashboard_polls_path(proposal.to_param), - class: "button expanded" %> -
-
-
-<% end %> diff --git a/app/views/dashboard/_poster_resource.html.erb b/app/views/dashboard/_poster_resource.html.erb deleted file mode 100644 index 0f47d6c32..000000000 --- a/app/views/dashboard/_poster_resource.html.erb +++ /dev/null @@ -1,16 +0,0 @@ -<% if can?(:manage_poster, proposal) %> -
-
-

<%= t("dashboard.menu.poster") %>

-

- <%= Setting["proposals.poster_short_title"] %> -

- -
- <%= link_to t("dashboard.resource.view_resource"), - new_proposal_dashboard_poster_path(proposal.to_param), - class: "button expanded" %> -
-
-
-<% end %> diff --git a/app/views/dashboard/_resource.html.erb b/app/views/dashboard/_resource.html.erb deleted file mode 100644 index bef2c194f..000000000 --- a/app/views/dashboard/_resource.html.erb +++ /dev/null @@ -1,31 +0,0 @@ -
-
-

<%= resource.title %>

- <% if is_new_action_since_last_login?(resource, @new_actions_since_last_login) %> - <%= t("dashboard.progress.new_action") %> - <% end %> -

- <%= resource.short_description %> -

- -
- <% 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) %> - <%= t("dashboard.resource.resource_requested") %> - <% elsif resource.active_for?(proposal) %> - <%= link_to t("dashboard.resource.request_resource"), - new_request_proposal_dashboard_action_path(proposal, resource), - class: "button expanded" %> - <% else %> - - <%= resource_availability_label(resource) %> - - <% end %> -
-
-
diff --git a/app/views/dashboard/_resources.html.erb b/app/views/dashboard/_resources.html.erb deleted file mode 100644 index 6c82994fc..000000000 --- a/app/views/dashboard/_resources.html.erb +++ /dev/null @@ -1,10 +0,0 @@ -
-

<%= t("dashboard.resources.available_resources") %>

- -
- <%= render "poll_resource" %> - <%= render "mailing_resource" %> - <%= render "poster_resource" %> - <%= render partial: "resource", collection: active_resources %> -
-
diff --git a/app/views/dashboard/polls/_poll.html.erb b/app/views/dashboard/polls/_poll.html.erb deleted file mode 100644 index 4c3eca69d..000000000 --- a/app/views/dashboard/polls/_poll.html.erb +++ /dev/null @@ -1,36 +0,0 @@ -
-
-

<%= link_to poll.title, proposal_poll_path(proposal, poll) %>

- - <%= l(poll.starts_at.to_date) %> - <%= l(poll.ends_at.to_date) %> - - -

- <%= t("dashboard.polls.poll.responses", count: poll.answer_count) %> -

- -
- <% 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 %> -
- - <%= 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 %> -

<%= t("dashboard.polls.poll.show_results_help") %>

- - <%= 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" %> -
-
diff --git a/app/views/dashboard/polls/index.html.erb b/app/views/dashboard/polls/index.html.erb index fb7eebae8..035874638 100644 --- a/app/views/dashboard/polls/index.html.erb +++ b/app/views/dashboard/polls/index.html.erb @@ -3,11 +3,7 @@
<%= Setting["proposals.poll_description"] %> - <% if @polls.any? %> -
- <%= render @polls %> -
- <% end %> + <%= render Dashboard::PollsComponent.new(@polls) %>
diff --git a/app/views/dashboard/progress.html.erb b/app/views/dashboard/progress.html.erb index de9e94be8..6b0d75816 100644 --- a/app/views/dashboard/progress.html.erb +++ b/app/views/dashboard/progress.html.erb @@ -38,4 +38,4 @@ <%= render "summary_recommended_actions" %> <% end %> -<%= render "resources" %> +<%= render Dashboard::ResourcesComponent.new(proposal, @new_actions_since_last_login) %> diff --git a/app/views/debates/index.html.erb b/app/views/debates/index.html.erb index fd35a031c..ed1b688c4 100644 --- a/app/views/debates/index.html.erb +++ b/app/views/debates/index.html.erb @@ -18,11 +18,7 @@ <%= render "shared/section_header", i18n_namespace: "debates.index.section_header", image: "debates" %> <% end %> -<% if feature?("user.recommendations") && @recommended_debates.present? %> - <%= render "shared/recommended_index", recommended: @recommended_debates, - disable_recommendations_path: recommendations_disable_debates_path, - namespace: "debates" %> -<% end %> +<%= render Shared::RecommendedIndexComponent.new(@recommended_debates, namespace: "debates") %>
diff --git a/app/views/polls/results.html.erb b/app/views/polls/results.html.erb index 8f6cddd44..44af3685a 100644 --- a/app/views/polls/results.html.erb +++ b/app/views/polls/results.html.erb @@ -1,22 +1 @@ -<% provide :title, @poll.name %> - -
- <%= render Polls::PollHeaderComponent.new(@poll) %> - - <%= render "poll_subnav" %> - -
- - -
- <%= render Polls::Results::QuestionComponent.with_collection(@poll.questions) %> -
-
-
+<%= render Polls::ResultsComponent.new(@poll) %> diff --git a/app/views/polls/stats.html.erb b/app/views/polls/stats.html.erb index b99052f48..8c1766181 100644 --- a/app/views/polls/stats.html.erb +++ b/app/views/polls/stats.html.erb @@ -1,6 +1,6 @@ <% provide :title, @poll.name %> -
+
<%= render Polls::PollHeaderComponent.new(@poll) %> <%= render "poll_subnav" %> diff --git a/app/views/proposals/index.html.erb b/app/views/proposals/index.html.erb index c8be341e8..3cdd444e3 100644 --- a/app/views/proposals/index.html.erb +++ b/app/views/proposals/index.html.erb @@ -25,9 +25,7 @@ <% end %> <% if show_recommended_proposals? %> - <%= render "shared/recommended_index", recommended: @recommended_proposals, - disable_recommendations_path: recommendations_disable_proposals_path, - namespace: "proposals" %> + <%= render Shared::RecommendedIndexComponent.new(@recommended_proposals, namespace: "proposals") %> <% end %>
diff --git a/app/views/shared/_recommended_index.html.erb b/app/views/shared/_recommended_index.html.erb deleted file mode 100644 index eb3783d58..000000000 --- a/app/views/shared/_recommended_index.html.erb +++ /dev/null @@ -1,34 +0,0 @@ - diff --git a/app/views/welcome/_recommended_carousel.html.erb b/app/views/welcome/_recommended_carousel.html.erb index da2ac25f5..def55377d 100644 --- a/app/views/welcome/_recommended_carousel.html.erb +++ b/app/views/welcome/_recommended_carousel.html.erb @@ -13,7 +13,7 @@
<%= render_recommendation_image(recommended) %>
- <%= link_to recommended_path(recommended) do %> + <%= link_to polymorphic_path(recommended) do %>

<%= recommended.title %>

<% end %> <%= wysiwyg(recommended.description) %> diff --git a/spec/components/dashboard/poll_component_spec.rb b/spec/components/dashboard/poll_component_spec.rb new file mode 100644 index 000000000..f31c7fd15 --- /dev/null +++ b/spec/components/dashboard/poll_component_spec.rb @@ -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 diff --git a/spec/components/dashboard/polls_component_spec.rb b/spec/components/dashboard/polls_component_spec.rb new file mode 100644 index 000000000..2ac73f65a --- /dev/null +++ b/spec/components/dashboard/polls_component_spec.rb @@ -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 diff --git a/spec/components/dashboard/resources_component_spec.rb b/spec/components/dashboard/resources_component_spec.rb new file mode 100644 index 000000000..00978ecb4 --- /dev/null +++ b/spec/components/dashboard/resources_component_spec.rb @@ -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 diff --git a/spec/components/polls/access_status_component_spec.rb b/spec/components/polls/access_status_component_spec.rb new file mode 100644 index 000000000..9ac1b77cc --- /dev/null +++ b/spec/components/polls/access_status_component_spec.rb @@ -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 diff --git a/spec/components/polls/poll_component_spec.rb b/spec/components/polls/poll_component_spec.rb index a12041ff8..4317ad2a8 100644 --- a/spec/components/polls/poll_component_spec.rb +++ b/spec/components/polls/poll_component_spec.rb @@ -3,45 +3,6 @@ require "rails_helper" describe Polls::PollComponent do 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 it "renders the dates inside an HTML tag" do poll = create(:poll, starts_at: "2015-07-15", ends_at: "2015-07-22") diff --git a/spec/system/dashboard/dashboard_spec.rb b/spec/system/dashboard/dashboard_spec.rb index 379478cec..64154806f 100644 --- a/spec/system/dashboard/dashboard_spec.rb +++ b/spec/system/dashboard/dashboard_spec.rb @@ -138,116 +138,6 @@ describe "Proposal's dashboard" do expect(page).not_to have_content(action.title) 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 feature = create(:dashboard_action, :resource, :active) @@ -480,29 +370,6 @@ describe "Proposal's dashboard" do describe "detect_new_actions_after_last_login" do 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 proposed_action = create(:dashboard_action, :proposed_action, :active, day_offset: 0, published_proposal: false) diff --git a/spec/system/dashboard/polls_spec.rb b/spec/system/dashboard/polls_spec.rb index cb9ed5821..2ef729541 100644 --- a/spec/system/dashboard/polls_spec.rb +++ b/spec/system/dashboard/polls_spec.rb @@ -123,26 +123,6 @@ describe "Polls" do expect(page).to have_content("Edit poll") 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 poll = create(:poll, related: proposal, starts_at: 1.week.from_now) create(:poll_question, poll: poll) @@ -224,36 +204,6 @@ describe "Polls" do expect(page).to have_content(poll.name) 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 poll = create(:poll, :expired, related: proposal) @@ -276,18 +226,4 @@ describe "Polls" do expect(find_field("Show results")).not_to be_checked 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