diff --git a/app/cells/decidim/badges/show.erb b/app/cells/decidim/badges/show.erb
new file mode 100644
index 0000000..3dd58a3
--- /dev/null
+++ b/app/cells/decidim/badges/show.erb
@@ -0,0 +1,17 @@
+
+ <%= icon "information-line" %>
+
local modification
+
+
<%= t "decidim.gamification.title" %>
+
+ <%= t "decidim.gamification.description" %>
+ <%= link_to t("decidim.gamification.all_badges_link"), gamification_badges_path, class: "text-secondary underline" %>
+
+
+
+
+
+ <% available_badges.each do |badge| %>
+ <%= cell "decidim/badge", model, badge: %>
+ <% end %>
+
\ No newline at end of file
diff --git a/app/cells/decidim/profile_cell.rb b/app/cells/decidim/profile_cell.rb
new file mode 100644
index 0000000..e54d47b
--- /dev/null
+++ b/app/cells/decidim/profile_cell.rb
@@ -0,0 +1,130 @@
+# frozen_string_literal: true
+
+module Decidim
+ class ProfileCell < Decidim::ViewModel
+ include Decidim::Core::Engine.routes.url_helpers
+ include Decidim::UserProfileHelper
+ include Decidim::AriaSelectedLinkToHelper
+ include Decidim::ViewHooksHelper
+ include ActiveLinkTo
+
+ delegate :current_organization, :current_user, :user_groups_enabled?, to: :controller
+ delegate :avatar_url, :nickname, :personal_url, :followers_count, :users_followings, :officialized_as, to: :presented_profile
+
+ TABS_ITEMS = {
+ activity: { icon: "bubble-chart-line", path: :profile_activity_path },
+ badges: { icon: "award-line", path: :profile_badges_path },
+ following: { icon: "eye-2-line", path: :profile_following_path },
+ followers: { icon: "group-line", path: :profile_followers_path },
+ groups: { icon: "team-line", path: :profile_groups_path },
+ members: { icon: "contacts-line", path: :profile_members_path },
+ conversations: { icon: "question-answer-line", path: :profile_conversations_path },
+ proposals: { icon: "eye-2-line", path: :profile_proposals_path }
+ }.freeze
+
+ def show
+ return render :invalid if profile_holder.blank?
+ return render :inaccessible if profile_holder.blocked? && current_user_logged_in?
+
+ render :show
+ end
+
+ def profile_holder
+ model
+ end
+
+ private
+
+ def presented_profile
+ present(profile_holder)
+ end
+
+ def show_badge?
+ return if user_group?
+
+ profile_holder.officialized?
+ end
+
+ def officialization_text
+ translated_attribute(officialized_as).presence || t("decidim.profiles.show.officialized")
+ end
+
+ def details_items
+ [{ icon: "account-pin-circle-line", text: nickname }].tap do |items|
+ items.append(icon: "link", text: personal_url, url: personal_url) if personal_url.present?
+ if (following_count = users_followings.size).positive?
+ items.append(icon: TABS_ITEMS[:following][:icon], text: t("decidim.following.following_count", count: following_count))
+ end
+ items.append(icon: TABS_ITEMS[:followers][:icon], text: t("decidim.followers.followers_count", count: followers_count)) if profile_holder.followers_count.positive?
+ end
+ end
+
+ def description
+ decidim_html_escape presented_profile.about.to_s
+ end
+
+ def content_cell
+ context[:content_cell]
+ end
+
+ def active_content
+ context[:active_content]
+ end
+
+ def current_user_logged_in?
+ current_user && !current_user.admin?
+ end
+
+ def own_profile?
+ current_user && current_user == profile_holder
+ end
+
+ def manageable_group?
+ return false unless user_group?
+
+ current_user && current_user.manageable_user_groups.include?(profile_holder)
+ end
+
+ def tab_item(key)
+ values = TABS_ITEMS[key].dup
+ values[:path] = send(values[:path], nickname: profile_holder.nickname)
+ values[:text] = t(key, scope: "decidim.profiles.show")
+ values.merge!(extra_data[key]) if extra_data.has_key?(key)
+ values
+ end
+
+ def user_tabs
+ items = [:activity].tap do |keys|
+ keys << :badges if current_organization.badges_enabled?
+ keys.append(:following, :followers)
+ keys << :groups if user_groups_enabled?
+ end
+ items.map { |key| tab_item(key) }
+ end
+
+ def group_tabs
+ items = [:members].tap do |keys|
+ # keys.append(:badges, :followers, :proposals, :activity)
+ keys.append(:badges, :followers, :proposals)
+ keys << :conversations if manageable_group?
+ end
+ items.map { |key| tab_item(key) }
+ end
+
+ def extra_data
+ @extra_data ||= {}.tap do |v|
+ if current_user && user_group? && (count = profile_holder.unread_messages_count_for(current_user)).positive?
+ v[:conversations] = { count: }
+ end
+ end
+ end
+
+ def tab_items
+ user_group? ? group_tabs : user_tabs
+ end
+
+ def user_group?
+ profile_holder.is_a?(Decidim::UserGroup)
+ end
+ end
+end
diff --git a/app/cells/decidim/proposals/show.erb b/app/cells/decidim/proposals/show.erb
new file mode 100644
index 0000000..c7171df
--- /dev/null
+++ b/app/cells/decidim/proposals/show.erb
@@ -0,0 +1,16 @@
+<%# see : https://github.com/decidim/decidim/blob/release/0.29-stable/decidim-proposals/app/views/decidim/proposals/proposals/_proposals.html.erb %>
+
+<% if proposals.empty? %>
+ <%= cell("decidim/announcement", t("decidim.proposals.proposals.proposals.empty")) %>
+<% else %>
+
+
<%= t("count", scope: "decidim.proposals.proposals.index", count: @proposals.count) %>
+
+
+
+
+ <% @proposals.each do |proposal| %>
+ <%= render partial: "../views/decidim/proposals/proposals/proposal.html", locals: { proposal: proposal, card_size: card_size_for_view_mode(@view_mode) } %>
+ <% end %>
+
+<% end %>
\ No newline at end of file
diff --git a/app/cells/decidim/proposals_cell.rb b/app/cells/decidim/proposals_cell.rb
new file mode 100644
index 0000000..ebb5a8b
--- /dev/null
+++ b/app/cells/decidim/proposals_cell.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Decidim
+ class ProposalsCell < Decidim::ViewModel
+ include Decidim::CellsPaginateHelper
+ include Decidim::Core::Engine.routes.url_helpers
+ include Decidim::CardHelper
+ include Decidim::Proposals::ProposalsHelper
+ # include Decidim::Coauthorable
+
+ # see https://github.com/decidim/decidim/blob/release/0.29-stable/decidim-proposals/app/controllers/decidim/proposals/proposals_controller.rb
+ def show
+ proposals
+ @view_mode = "grid"
+ render :show
+ end
+
+ def proposals
+ @proposals ||= Decidim::Proposals::Proposal.joins(:coauthorships)
+ .where(
+ decidim_coauthorships: {
+ # decidim_author_type: "Decidim::UserBaseEntity",
+ decidim_user_group_id: profile_holder.id
+ }
+ )
+ # .with_type(type_key: "proposal")
+ .not_hidden
+ .published
+ .not_withdrawn
+ # @proposals ||= Decidim::Proposals::Proposal.joins(:coauthorships)
+ # .where("decidim_coauthorships.decidim_user_group_id": user_group.id)
+ end
+ end
+end
diff --git a/app/controllers/decidim/profiles_controller.rb b/app/controllers/decidim/profiles_controller.rb
new file mode 100644
index 0000000..1580acd
--- /dev/null
+++ b/app/controllers/decidim/profiles_controller.rb
@@ -0,0 +1,130 @@
+# frozen_string_literal: true
+
+module Decidim
+ # The controller to handle the user's public profile page.
+ #
+ # i18n-tasks-use t('decidim.profiles.show.badges')
+ # i18n-tasks-use t('decidim.profiles.show.groups')
+ # i18n-tasks-use t('decidim.profiles.show.group_admins')
+ # i18n-tasks-use t('decidim.profiles.show.group_members')
+ class ProfilesController < Decidim::ApplicationController
+ include UserGroups
+ include Flaggable
+ include HasProfileBreadcrumb
+
+ helper Decidim::Messaging::ConversationHelper
+
+ helper_method :profile_holder, :active_content, :context_menu
+
+ before_action :ensure_profile_holder
+ before_action :ensure_profile_holder_is_a_group, only: [:members]
+ before_action :ensure_profile_holder_is_a_user, only: [:groups, :following]
+ before_action :ensure_user_not_blocked
+
+ def show
+ return redirect_to profile_members_path if profile_holder.is_a?(Decidim::UserGroup)
+
+ redirect_to profile_activity_path(nickname: params[:nickname].downcase)
+ end
+
+ def tooltip
+ render json: { data: cell("decidim/author", profile_holder.presenter).profile_minicard }
+ end
+
+ def following
+ @content_cell = "decidim/following"
+ @title_key = "following"
+ render :show
+ end
+
+ def followers
+ @content_cell = "decidim/followers"
+ @title_key = "followers"
+ render :show
+ end
+
+ def badges
+ @content_cell = "decidim/badges"
+ @title_key = "badges"
+ render :show
+ end
+
+ def groups
+ enforce_user_groups_enabled
+
+ @content_cell = "decidim/groups"
+ @title_key = "groups"
+ render :show
+ end
+
+ def members
+ enforce_user_groups_enabled
+
+ @content_cell = "decidim/members"
+ @title_key = "members"
+ render :show
+ end
+
+ def group_admins
+ enforce_permission_to :manage, :user_group, user_group: profile_holder
+
+ @content_cell = "decidim/group_admins"
+ @title_key = "group_admins"
+ render :show
+ end
+
+ def group_members
+ enforce_permission_to :manage, :user_group, user_group: profile_holder
+
+ @content_cell = "decidim/group_members"
+ @title_key = "group_members"
+ render :show
+ end
+
+ def group_invites
+ enforce_permission_to :manage, :user_group, user_group: profile_holder
+
+ @content_cell = "decidim/group_invites"
+ @title_key = "group_invites"
+ render :show
+ end
+
+ def activity
+ @content_cell = "decidim/user_activity"
+ @title_key = "activity"
+ render :show
+ end
+
+ def proposals
+ enforce_user_groups_enabled
+
+ @content_cell = "decidim/proposals"
+ @title_key = "proposals"
+ render :show
+ end
+
+ private
+
+ def ensure_user_not_blocked
+ raise ActionController::RoutingError, "Blocked User" if profile_holder&.blocked? && !current_user&.admin?
+ end
+
+ def ensure_profile_holder_is_a_group
+ raise ActionController::RoutingError, "No user group with the given nickname" unless profile_holder.is_a?(Decidim::UserGroup)
+ end
+
+ def ensure_profile_holder_is_a_user
+ raise ActionController::RoutingError, "No user with the given nickname" unless profile_holder.is_a?(Decidim::User)
+ end
+
+ def ensure_profile_holder
+ raise ActionController::RoutingError, "No user or user group with the given nickname" if !profile_holder || profile_holder.nickname.blank?
+ end
+
+ def profile_holder
+ return if params[:nickname].blank?
+
+ @profile_holder ||= Decidim::UserBaseEntity.find_by("nickname = ? AND decidim_organization_id = ?", params[:nickname].downcase, current_organization.id)
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/packs/stylesheets/decidim/decidim_application.scss b/app/packs/stylesheets/decidim/decidim_application.scss
index ae29123..61bcf7e 100644
--- a/app/packs/stylesheets/decidim/decidim_application.scss
+++ b/app/packs/stylesheets/decidim/decidim_application.scss
@@ -37,3 +37,12 @@ header {
}
}
+/* BEGIN Group Profile Proposals List */
+
+.profile__scaffold__bottom-content {
+ .card__list-list, .card__grid-grid {
+ @apply my-8;
+ }
+}
+
+/* END Group Profile Proposals List */
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 0b337fd..67f686d 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -5,6 +5,9 @@ en:
assembly:
fields:
promoted: "Promoted"
+ profiles:
+ show:
+ proposals: "Proposals"
layouts:
decidim:
data_consent:
diff --git a/config/routes.rb b/config/routes.rb
index 2a8b9dc..2c00702 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -3,6 +3,12 @@
require "sidekiq/web"
require "sidekiq-scheduler/web"
+Decidim::Core::Engine.routes.draw do
+ scope "/profiles/:nickname", format: false, constraints: { nickname: %r{[^/]+} } do
+ get "proposals", to: "profiles#proposals", as: "profile_proposals"
+ end
+end
+
Rails.application.routes.draw do
if Rails.application.secrets.puma[:health_check][:enabled]
get "/stats", to: redirect { |_params, request| "http://#{request.host}:#{Rails.application.secrets.puma[:health_check][:port]}/stats?#{request.params.to_query}" }
@@ -22,3 +28,4 @@
mount Decidim::Core::Engine => "/"
end
+