decidim-meetings 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d1998702c63adcee35cf6ba712d2f8ea986f05bd
4
- data.tar.gz: bd7b21aa2f323b62e7b195c42eae8238d3e28e15
3
+ metadata.gz: 8e19fd2bced64fedffeeb7e466c5afcbcad2b814
4
+ data.tar.gz: 8fadc5f684e7324c085da6f956923b66eb7c1818
5
5
  SHA512:
6
- metadata.gz: 48441620d5786ab9855fecc90165b6cd87a0b8fbf1ddf7d0b90391f4fa8de14bbbad538bc015990e6c56c05d2149748887b23012cd55cdf2ca864d5552738626
7
- data.tar.gz: 289779e9f898af0c999b83ff61ae254b481d721cd3682c3f243f3734fb64beada153d64480bd4b116ce8d696de04ca7879f4602aceea72801a5780cc5d8c5b0c
6
+ metadata.gz: 7784edad8cef64c16f94fd8c94c175a23b61361beb7dafa50cf4b1ec8fc0aec0c86005578923baf7dce7c4bc2c13a55357375c21263926701a5c226fbe44fc1f
7
+ data.tar.gz: 17e3efaa9f8d2e42642811a6b0336ae04409344f7e6b0ec19a12a18f3171b5e386432f884017674c8a30625bebce40e4b543c3b331728e6321cf4fc37ddca5dd
data/Rakefile CHANGED
@@ -1,2 +1,2 @@
1
1
  # frozen_string_literal: true
2
- require "decidim/common_rake"
2
+ require "decidim/dev/common_rake"
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36.02 36.02"><path d="M693.07,543.84a18,18,0,1,1,18-18A18,18,0,0,1,693.07,543.84Zm0-34a16,16,0,1,0,16,16A16,16,0,0,0,693.07,509.83Z" transform="translate(-675.07 -507.83)"/><path d="M693.07,526.93a4.1,4.1,0,1,1,4.1-4.1A4.1,4.1,0,0,1,693.07,526.93Zm0-6.19a2.1,2.1,0,1,0,2.1,2.1A2.1,2.1,0,0,0,693.07,520.74Z" transform="translate(-675.07 -507.83)"/><path d="M693.07,536.65a1,1,0,0,1-.67-0.26c-0.29-.26-7.11-6.5-7.11-14a7.78,7.78,0,0,1,15.57,0c0,7.46-6.82,13.71-7.11,14A1,1,0,0,1,693.07,536.65Zm0-20a5.79,5.79,0,0,0-5.78,5.78c0,5.29,4.13,10.11,5.78,11.82,1.65-1.72,5.78-6.54,5.78-11.82A5.79,5.79,0,0,0,693.07,516.64Z" transform="translate(-675.07 -507.83)"/></svg>
@@ -23,6 +23,8 @@ module Decidim
23
23
 
24
24
  def create_meeting
25
25
  Meeting.create!(
26
+ scope: @form.scope,
27
+ category: @form.category,
26
28
  title: @form.title,
27
29
  short_description: @form.short_description,
28
30
  description: @form.description,
@@ -28,6 +28,8 @@ module Decidim
28
28
 
29
29
  def update_meeting
30
30
  @meeting.update_attributes!(
31
+ scope: @form.scope,
32
+ category: @form.category,
31
33
  title: @form.title,
32
34
  short_description: @form.short_description,
33
35
  description: @form.description,
@@ -2,23 +2,47 @@
2
2
 
3
3
  module Decidim
4
4
  module Meetings
5
- # This controller is the abstract class from which all other controllers of
6
- # this engine inherit.
7
- #
8
- # Note that it inherits from `Decidim::Features::BaseController`, which
9
- # override its layout and provide all kinds of useful methods.
5
+ # Exposes the meeting resource so users can view them
10
6
  class MeetingsController < Decidim::Meetings::ApplicationController
7
+ include FilterResource
8
+
11
9
  helper_method :meetings, :meeting
12
10
 
11
+ def index; end
12
+
13
13
  private
14
14
 
15
15
  def meetings
16
- @meetings ||= Meeting.where(decidim_feature_id: current_feature.id).order(start_time: :asc)
16
+ @meetings ||= search.results
17
17
  end
18
18
 
19
19
  def meeting
20
20
  @meeting ||= meetings.find(params[:id])
21
21
  end
22
+
23
+ def search_klass
24
+ MeetingSearch
25
+ end
26
+
27
+ def default_search_params
28
+ {
29
+ page: params[:page],
30
+ per_page: 12
31
+ }
32
+ end
33
+
34
+ def default_filter_params
35
+ {
36
+ order_start_time: "asc",
37
+ search_text: "",
38
+ scope_id: "",
39
+ category_id: ""
40
+ }
41
+ end
42
+
43
+ def context_params
44
+ { feature: current_feature, organization: current_organization }
45
+ end
22
46
  end
23
47
  end
24
48
  end
@@ -14,6 +14,8 @@ module Decidim
14
14
  attribute :address, String
15
15
  attribute :start_time, DateTime
16
16
  attribute :end_time, DateTime
17
+ attribute :decidim_scope_id, Integer
18
+ attribute :decidim_category_id, Integer
17
19
 
18
20
  validates :title, translatable_presence: true
19
21
  validates :short_description, translatable_presence: true
@@ -24,6 +26,18 @@ module Decidim
24
26
  validates :end_time, presence: true, date: { after: :start_time }
25
27
 
26
28
  validates :current_feature, presence: true
29
+ validates :scope, presence: true, if: ->(form) { form.decidim_scope_id.present? }
30
+ validates :category, presence: true, if: ->(form) { form.decidim_category_id.present? }
31
+
32
+ def scope
33
+ return unless current_feature
34
+ @scope ||= current_feature.scopes.where(id: decidim_scope_id).first
35
+ end
36
+
37
+ def category
38
+ return unless current_feature
39
+ @category ||= current_feature.categories.where(id: decidim_category_id).first
40
+ end
27
41
  end
28
42
  end
29
43
  end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Meetings
4
+ # Custom helpers, scoped to the meetings engine.
5
+ #
6
+ module ApplicationHelper
7
+ include PaginateHelper
8
+ end
9
+ end
10
+ end
@@ -5,8 +5,25 @@ module Decidim
5
5
  # title, description and any other useful information to render a custom meeting.
6
6
  class Meeting < Meetings::ApplicationRecord
7
7
  belongs_to :feature, foreign_key: "decidim_feature_id", class_name: Decidim::Feature
8
-
8
+ belongs_to :scope, foreign_key: "decidim_scope_id", class_name: Decidim::Scope
9
+ belongs_to :category, foreign_key: "decidim_category_id", class_name: Decidim::Category
10
+ has_one :organization, through: :feature
9
11
  validates :title, presence: true
12
+
13
+ validate :scope_belongs_to_organization
14
+ validate :category_belongs_to_organization
15
+
16
+ private
17
+
18
+ def scope_belongs_to_organization
19
+ return unless scope
20
+ errors.add(:scope, :invalid) unless feature.scopes.where(id: scope.id).exists?
21
+ end
22
+
23
+ def category_belongs_to_organization
24
+ return unless category
25
+ errors.add(:category, :invalid) unless feature.categories.where(id: category.id).exists?
26
+ end
10
27
  end
11
28
  end
12
29
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Meetings
4
+ # This class handles search and filtering of meetings. Needs a
5
+ # `current_feature` param with a `Decidim::Feature` in order to
6
+ # find the meetings.
7
+ class MeetingSearch < ResourceSearch
8
+ # Public: Initializes the service.
9
+ # feature - A Decidim::Feature to get the meetings from.
10
+ # page - The page number to paginate the results.
11
+ # per_page - The number of proposals to return per page.
12
+ def initialize(options = {})
13
+ super(Meeting.all, options)
14
+ end
15
+
16
+ # Handle the search_text filter
17
+ def search_search_text
18
+ query
19
+ .where(localized_search_text_in(:title), text: "%#{search_text}%")
20
+ .or(query.where(localized_search_text_in(:description), text: "%#{search_text}%"))
21
+ .or(query.where(localized_search_text_in(:short_description), text: "%#{search_text}%"))
22
+ end
23
+
24
+ # Handle the order_start_time filter
25
+ def search_order_start_time
26
+ query.order(start_time: order_start_time)
27
+ end
28
+
29
+ # Handle the scope_id filter
30
+ def search_scope_id
31
+ query.where(decidim_scope_id: scope_id)
32
+ end
33
+
34
+ private
35
+
36
+ # Internal: builds the needed query to search for a text in the organization's
37
+ # available locales. Note that it is intended to be used as follows:
38
+ #
39
+ # Example:
40
+ # Resource.where(localized_search_text_for(:title, text: "my_query"))
41
+ #
42
+ # The Hash with the `:text` key is required or it won't work.
43
+ def localized_search_text_in(field)
44
+ options[:organization].available_locales.map { |l| "#{field} ->> '#{l}' ILIKE :text" }.join(" OR ")
45
+ end
46
+ end
47
+ end
48
+ end
@@ -11,11 +11,11 @@
11
11
  </div>
12
12
 
13
13
  <div class="field" >
14
- <%= form.translated :editor, :location %>
14
+ <%= form.translated :text_area, :location %>
15
15
  </div>
16
16
 
17
17
  <div class="field" >
18
- <%= form.translated :editor, :location_hints %>
18
+ <%= form.translated :text_area, :location_hints %>
19
19
  </div>
20
20
 
21
21
  <div class="field" >
@@ -30,3 +30,10 @@
30
30
  <%= form.datetime_field :end_time %>
31
31
  </div>
32
32
 
33
+ <div class="field" >
34
+ <%= form.collection_select :decidim_scope_id, current_organization.scopes, :id, :name, include_blank: true %>
35
+ </div>
36
+
37
+ <div class="field" >
38
+ <%= form.categories_select :decidim_category_id, current_participatory_process.categories, prompt: "", disable_parents: false %>
39
+ </div>
@@ -0,0 +1,24 @@
1
+ <%= filter_form_for filter do |form| %>
2
+ <div class="filters__section">
3
+ <div class="filters__search">
4
+ <div class="input-group">
5
+ <%= form.search_field :search_text, label: false, class: "input-group-field", placeholder: t('.search') %>
6
+ <div class="input-group-button">
7
+ <button type="submit" class="button button--muted">
8
+ <%= icon "magnifying-glass", aria_label: t('.search') %>
9
+ </button>
10
+ </div>
11
+ </div>
12
+ </div>
13
+ </div>
14
+
15
+ <%= form.collection_radio_buttons :order_start_time, [["asc", t('.ascendent')], ["desc", t('.descendent')]], :first, :last, legend_title: t('.date') %>
16
+
17
+ <% if current_organization.scopes.any? %>
18
+ <%= form.collection_check_boxes :scope_id, current_organization.scopes, lambda {|scope| scope.id.to_s}, :name, legend_title: t('.scopes') %>
19
+ <% end %>
20
+
21
+ <% if current_feature.categories.any? %>
22
+ <%= form.categories_select :category_id, current_feature.categories, legend_title: t('.category'), disable_parents: false, label: false, include_blank: true %>
23
+ <% end %>
24
+ <% end %>
@@ -0,0 +1,30 @@
1
+ <div class="row small-up-1 medium-up-2 card-grid">
2
+ <% meetings.each do |meeting| %>
3
+ <div class="column">
4
+ <article class="card card--meeting">
5
+ <div class="card__content">
6
+ <%= link_to meeting, class: "card__link" do %>
7
+ <h5 class="card__title"><%= translated_attribute meeting.title %></h5>
8
+ <% end %>
9
+ <div class="card__datetime">
10
+ <div class="card__datetime__date">
11
+ <%= l meeting.start_time, format: "%d" %> <span class="card__datetime__month"><%= l meeting.start_time, format: "%B" %></span>
12
+ </div>
13
+ <div class="card__datetime__time">
14
+ <%= meeting.start_time.strftime("%H:%M") %> - <%= meeting.end_time.strftime("%H:%M") %>
15
+ </div>
16
+ </div>
17
+ <%== translated_attribute meeting.short_description %>
18
+ <%= render partial: "tags", locals: { meeting: meeting } %>
19
+ <div class="address card__extra">
20
+ <div class="address__icon">
21
+ <%= icon "meetings", remove_icon_class: true, width: 40, height: 70 %>
22
+ </div>
23
+ <%= render partial: 'address_details', locals: { meeting: meeting } %>
24
+ </div>
25
+ </div>
26
+ </article>
27
+ </div>
28
+ <% end %>
29
+ </div>
30
+ <%= decidim_paginate meetings, order_start_time: params[:order_start_time], scope_id: params[:scope_id] %>
@@ -0,0 +1,10 @@
1
+ <% if meeting.category.present? || meeting.scope.present? %>
2
+ <ul class="tags tags--meeting" >
3
+ <% if meeting.category.present? %>
4
+ <li><%= link_to translated_attribute(meeting.category.name), decidim_meetings.meetings_path(filter: { category_id: meeting.category.id }) %></li>
5
+ <% end %>
6
+ <% if meeting.scope.present? %>
7
+ <li><%= link_to meeting.scope.name, decidim_meetings.meetings_path(filter: { scope_id: [meeting.scope.id] }) %></li>
8
+ <% end %>
9
+ </ul>
10
+ <% end %>
@@ -1,30 +1,12 @@
1
1
  <% content_for(:title, t(".title")) %>
2
2
 
3
- <div class="row small-up-1 medium-up-2 large-up-3 card-grid">
4
- <% meetings.each do |meeting| %>
5
- <div class="column">
6
- <article class="card card--meeting">
7
- <div class="card__content">
8
- <%= link_to meeting, class: "card__link" do %>
9
- <h5 class="card__title"><%= translated_attribute meeting.title %></h5>
10
- <% end %>
11
- <div class="card__datetime">
12
- <div class="card__datetime__date">
13
- <%= l meeting.start_time, format: "%d" %> <span class="card__datetime__month"><%= l meeting.start_time, format: "%B" %></span>
14
- </div>
15
- <div class="card__datetime__time">
16
- <%= meeting.start_time.strftime("%H:%M") %> - <%= meeting.end_time.strftime("%H:%M") %>
17
- </div>
18
- </div>
19
- <%== translated_attribute meeting.description %>
20
- <div class="address card__extra">
21
- <div class="address__icon">
22
- <%= icon "meetings", removeIconClass: true, width: 40, height: 70 %>
23
- </div>
24
- <%= render partial: 'address_details', locals: { meeting: meeting } %>
25
- </div>
26
- </div>
27
- </article>
3
+ <div class="row">
4
+ <div class="columns mediumlarge-4 large-3">
5
+ <div class="card card--secondary show-for-mediumlarge" >
6
+ <%= render partial: "filters" %>
28
7
  </div>
29
- <% end %>
8
+ </div>
9
+ <div id="meetings" class="columns mediumlarge-8 large-9">
10
+ <%= render partial: "meetings" %>
11
+ </div>
30
12
  </div>
@@ -0,0 +1,2 @@
1
+ var $meetings = $('#meetings');
2
+ $meetings.html('<%= j(render partial: "meetings") %>');
@@ -30,6 +30,7 @@
30
30
  </div>
31
31
  </div>
32
32
  </div>
33
+ <%= render partial: "tags", locals: { meeting: meeting } %>
33
34
  </div>
34
35
  <div class="section">
35
36
  <h3 class="section-heading"><%= t(".meeting_description") %></h3>
@@ -32,6 +32,13 @@ ca:
32
32
  meeting:
33
33
  name: Trobada
34
34
  meetings:
35
+ filters:
36
+ ascendent: Ascendent
37
+ category: Categoria
38
+ date: Data
39
+ descendent: Descendent
40
+ scopes: Àmbits
41
+ search: Cerca
35
42
  index:
36
43
  title: Llistat de trobades
37
44
  show:
@@ -33,6 +33,13 @@ en:
33
33
  meeting:
34
34
  name: Meeting
35
35
  meetings:
36
+ filters:
37
+ ascendent: Ascendent
38
+ category: Category
39
+ date: Date
40
+ descendent: Descendent
41
+ scopes: Scopes
42
+ search: Search
36
43
  index:
37
44
  title: Meetings
38
45
  show:
@@ -32,6 +32,13 @@ es:
32
32
  meeting:
33
33
  name: Encuentro
34
34
  meetings:
35
+ filters:
36
+ ascendent: Ascendente
37
+ category: Categoría
38
+ date: Fecha
39
+ descendent: Descendiente
40
+ scopes: Ámbitos
41
+ search: Buscar
35
42
  index:
36
43
  title: Lista de encuentros
37
44
  show:
@@ -11,6 +11,8 @@ class CreateMeetings < ActiveRecord::Migration[5.0]
11
11
  t.jsonb :location_hints
12
12
  t.references :decidim_feature, index: true
13
13
  t.references :decidim_author, index: true
14
+ t.references :decidim_scope, index: true
15
+ t.references :decidim_category, index: true
14
16
 
15
17
  t.timestamps
16
18
  end
@@ -5,8 +5,9 @@ require_dependency "decidim/features/namer"
5
5
  Decidim.register_feature(:meetings) do |feature|
6
6
  feature.engine = Decidim::Meetings::ListEngine
7
7
  feature.admin_engine = Decidim::Meetings::AdminEngine
8
+ feature.icon = "decidim/meetings/icon.svg"
8
9
 
9
- feature.on(:destroy) do |instance|
10
+ feature.on(:before_destroy) do |instance|
10
11
  raise StandardError, "Can't remove this feature" if Decidim::Meetings::Meeting.where(feature: instance).any?
11
12
  end
12
13
 
@@ -21,8 +22,10 @@ Decidim.register_feature(:meetings) do |feature|
21
22
  )
22
23
 
23
24
  3.times do
24
- Decidim::Meetings::Meeting.create!(
25
+ meeting = Decidim::Meetings::Meeting.create!(
25
26
  feature: feature,
27
+ scope: process.organization.scopes.sample,
28
+ category: process.categories.sample,
26
29
  title: Decidim::Faker::Localized.sentence(2),
27
30
  description: Decidim::Faker::Localized.wrapped("<p>", "</p>") do
28
31
  Decidim::Faker::Localized.paragraph(3)
@@ -36,6 +39,8 @@ Decidim.register_feature(:meetings) do |feature|
36
39
  end_time: 3.weeks.from_now + 4.hours,
37
40
  address: "#{Faker::Address.street_address} #{Faker::Address.zip} #{Faker::Address.city}"
38
41
  )
42
+
43
+ Decidim::Comments::Seed.comments_for(meeting)
39
44
  end
40
45
  end
41
46
  end
@@ -1,4 +1,7 @@
1
1
  # frozen_string_literal: true
2
+ require "searchlight"
3
+ require "kaminari"
4
+
2
5
  module Decidim
3
6
  module Meetings
4
7
  # This is the engine that runs on the public interface of `decidim-meetings`.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: decidim-meetings
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josep Jaume Rey Peroy
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2016-12-21 00:00:00.000000000 Z
13
+ date: 2017-01-23 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: decidim-core
@@ -18,14 +18,14 @@ dependencies:
18
18
  requirements:
19
19
  - - '='
20
20
  - !ruby/object:Gem::Version
21
- version: 0.0.1
21
+ version: 0.0.2
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
26
  - - '='
27
27
  - !ruby/object:Gem::Version
28
- version: 0.0.1
28
+ version: 0.0.2
29
29
  - !ruby/object:Gem::Dependency
30
30
  name: rectify
31
31
  requirement: !ruby/object:Gem::Requirement
@@ -54,20 +54,48 @@ dependencies:
54
54
  - - "~>"
55
55
  - !ruby/object:Gem::Version
56
56
  version: '0.9'
57
+ - !ruby/object:Gem::Dependency
58
+ name: searchlight
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: 4.1.0
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: 4.1.0
71
+ - !ruby/object:Gem::Dependency
72
+ name: kaminari
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: 1.0.1
78
+ type: :runtime
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: 1.0.1
57
85
  - !ruby/object:Gem::Dependency
58
86
  name: decidim-dev
59
87
  requirement: !ruby/object:Gem::Requirement
60
88
  requirements:
61
89
  - - '='
62
90
  - !ruby/object:Gem::Version
63
- version: 0.0.1
91
+ version: 0.0.2
64
92
  type: :development
65
93
  prerelease: false
66
94
  version_requirements: !ruby/object:Gem::Requirement
67
95
  requirements:
68
96
  - - '='
69
97
  - !ruby/object:Gem::Version
70
- version: 0.0.1
98
+ version: 0.0.2
71
99
  description: ''
72
100
  email:
73
101
  - josepjaume@gmail.com
@@ -79,6 +107,7 @@ extra_rdoc_files: []
79
107
  files:
80
108
  - README.md
81
109
  - Rakefile
110
+ - app/assets/images/decidim/meetings/icon.svg
82
111
  - app/commands/decidim/meetings/admin/create_meeting.rb
83
112
  - app/commands/decidim/meetings/admin/update_meeting.rb
84
113
  - app/controllers/decidim/meetings/admin/application_controller.rb
@@ -86,14 +115,20 @@ files:
86
115
  - app/controllers/decidim/meetings/application_controller.rb
87
116
  - app/controllers/decidim/meetings/meetings_controller.rb
88
117
  - app/forms/decidim/meetings/admin/meeting_form.rb
118
+ - app/helpers/decidim/meetings/application_helper.rb
89
119
  - app/models/decidim/meetings/application_record.rb
90
120
  - app/models/decidim/meetings/meeting.rb
121
+ - app/services/decidim/meetings/meeting_search.rb
91
122
  - app/views/decidim/meetings/admin/meetings/_form.html.erb
92
123
  - app/views/decidim/meetings/admin/meetings/edit.html.erb
93
124
  - app/views/decidim/meetings/admin/meetings/index.html.erb
94
125
  - app/views/decidim/meetings/admin/meetings/new.html.erb
95
126
  - app/views/decidim/meetings/meetings/_address_details.html.erb
127
+ - app/views/decidim/meetings/meetings/_filters.html.erb
128
+ - app/views/decidim/meetings/meetings/_meetings.html.erb
129
+ - app/views/decidim/meetings/meetings/_tags.html.erb
96
130
  - app/views/decidim/meetings/meetings/index.html.erb
131
+ - app/views/decidim/meetings/meetings/index.js.erb
97
132
  - app/views/decidim/meetings/meetings/show.html.erb
98
133
  - config/i18n-tasks.yml
99
134
  - config/locales/ca.yml
@@ -125,7 +160,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
160
  version: '0'
126
161
  requirements: []
127
162
  rubyforge_project:
128
- rubygems_version: 2.5.2
163
+ rubygems_version: 2.6.8
129
164
  signing_key:
130
165
  specification_version: 4
131
166
  summary: A meetings component for decidim's participatory processes.