Add setting to show in filter by status only statuses present in board posts (#411)

This commit is contained in:
Riccardo Graziosi
2024-09-17 18:36:54 +02:00
committed by GitHub
parent 675a8709ef
commit cab2229e09
17 changed files with 83 additions and 18 deletions

View File

@@ -4,4 +4,8 @@
scroll-margin-top: 96px;
}
.generalSiteSettingsSubmit {
@extend .mb-4;
}
}

View File

@@ -1,5 +1,6 @@
class BoardsController < ApplicationController
include ApplicationHelper
include BoardsHelper
before_action :authenticate_user!, only: [:create, :update, :update_order, :destroy]
@@ -12,6 +13,7 @@ class BoardsController < ApplicationController
def show
@board = Board.friendly.find(params[:id])
@page_title = @board.name
@post_statuses_to_show_in_filter = get_post_statuses_to_show_in_filter
end
def create

View File

@@ -1,4 +1,6 @@
class StaticPagesController < ApplicationController
include BoardsHelper
skip_before_action :load_tenant_data, only: [:showcase, :pending_tenant, :blocked_tenant]
before_action :allow_iframe_embedding, only: [:embedded_roadmap]
@@ -7,6 +9,7 @@ class StaticPagesController < ApplicationController
if @board
@page_title = @board.name
@post_statuses_to_show_in_filter = get_post_statuses_to_show_in_filter
render 'boards/show'
else
@page_title = t('roadmap.title')

View File

@@ -0,0 +1,9 @@
module BoardsHelper
def get_post_statuses_to_show_in_filter
if Current.tenant.tenant_setting.hide_unused_statuses_in_filter_by_status
@board.posts.map(&:post_status_id).uniq
else
PostStatus.pluck(:id) << nil
end
end
end

View File

@@ -23,6 +23,7 @@ interface Props {
currentUserFullName: string;
tenantSetting: ITenantSetting;
componentRenderedAt: number;
postStatusesToShowInFilter: Array<number>;
authenticityToken: string;
posts: PostsState;
postStatuses: PostStatusesState;
@@ -96,6 +97,7 @@ class BoardP extends React.Component<Props> {
currentUserFullName,
tenantSetting,
componentRenderedAt,
postStatusesToShowInFilter,
authenticityToken,
posts,
postStatuses,
@@ -140,14 +142,19 @@ class BoardP extends React.Component<Props> {
/>
</>
}
<PostStatusFilter
postStatuses={postStatuses.items}
areLoading={postStatuses.areLoading}
error={postStatuses.error}
{
postStatusesToShowInFilter.length > 0 &&
<PostStatusFilter
postStatuses={postStatuses.items.filter(postStatus => postStatusesToShowInFilter.includes(postStatus.id))}
areLoading={postStatuses.areLoading}
error={postStatuses.error}
currentFilter={filters.postStatusIds}
handleFilterClick={handlePostStatusFilterChange}
/>
currentFilter={filters.postStatusIds}
handleFilterClick={handlePostStatusFilterChange}
showNoStatusFilter={postStatusesToShowInFilter.includes(null)}
/>
}
</div>
{ tenantSetting.show_powered_by && <PoweredByLink /> }

View File

@@ -15,6 +15,8 @@ interface Props {
handleFilterClick(postStatusId: number): void;
currentFilter: Array<number>;
showNoStatusFilter?: boolean;
}
const PostStatusFilter = ({
@@ -24,6 +26,8 @@ const PostStatusFilter = ({
handleFilterClick,
currentFilter,
showNoStatusFilter = true,
}: Props) => (
<SidebarBox title={I18n.t('board.filter_box.title')} customClass="postStatusFilterContainer">
{
@@ -39,13 +43,18 @@ const PostStatusFilter = ({
/>
))
}
<PostStatusListItem
name={I18n.t('common.no_status')}
color='black'
handleClick={() => handleFilterClick(0)}
isCurrentFilter={currentFilter.includes(0)}
/>
{
showNoStatusFilter &&
<PostStatusListItem
name={I18n.t('common.no_status')}
color='black'
handleClick={() => handleFilterClick(0)}
isCurrentFilter={currentFilter.includes(0)}
/>
}
{ areLoading ? <Spinner /> : null }
{ error ? <DangerText>{error}</DangerText> : null }
</SidebarBox>

View File

@@ -17,6 +17,7 @@ interface Props {
currentUserFullName: string;
tenantSetting: ITenantSetting;
componentRenderedAt: number;
postStatusesToShowInFilter: Array<number>;
authenticityToken: string;
}
@@ -37,6 +38,7 @@ class BoardRoot extends React.Component<Props> {
currentUserFullName,
tenantSetting,
componentRenderedAt,
postStatusesToShowInFilter,
authenticityToken,
} = this.props;
@@ -49,6 +51,7 @@ class BoardRoot extends React.Component<Props> {
currentUserFullName={currentUserFullName}
tenantSetting={tenantSetting}
componentRenderedAt={componentRenderedAt}
postStatusesToShowInFilter={postStatusesToShowInFilter}
authenticityToken={authenticityToken}
/>
</Provider>

View File

@@ -40,6 +40,7 @@ export interface ISiteSettingsGeneralForm {
collapseBoardsInHeader: string;
showVoteCount: boolean;
showVoteButtonInBoard: boolean;
hideUnusedStatusesInFilterByStatus: boolean;
showPoweredBy: boolean;
}
@@ -69,6 +70,7 @@ interface Props {
collapseBoardsInHeader: string,
showVoteCount: boolean,
showVoteButtonInBoard: boolean,
hideUnusedStatusesInFilterByStatus: boolean,
showPoweredBy: boolean,
authenticityToken: string
): Promise<any>;
@@ -107,6 +109,7 @@ const GeneralSiteSettingsP = ({
collapseBoardsInHeader: originForm.collapseBoardsInHeader,
showVoteCount: originForm.showVoteCount,
showVoteButtonInBoard: originForm.showVoteButtonInBoard,
hideUnusedStatusesInFilterByStatus: originForm.hideUnusedStatusesInFilterByStatus,
showPoweredBy: originForm.showPoweredBy,
},
});
@@ -129,6 +132,7 @@ const GeneralSiteSettingsP = ({
data.collapseBoardsInHeader,
data.showVoteCount,
data.showVoteButtonInBoard,
data.hideUnusedStatusesInFilterByStatus,
data.showPoweredBy,
authenticityToken
).then(res => {
@@ -344,7 +348,7 @@ const GeneralSiteSettingsP = ({
className="selectPicker"
>
<option value={TENANT_SETTING_LOGO_LINKS_TO_ROOT_PAGE}>
{ I18n.t('site_settings.general.logo_links_to_root_page') }
{ I18n.t('site_settings.general.logo_links_to_root_page') } ({watch('rootBoardId') === '0' ? I18n.t('roadmap.title') : boards.find(board => board.id === Number(watch('rootBoardId')))?.name})
</option>
<option value={TENANT_SETTING_LOGO_LINKS_TO_CUSTOM_URL}>
{ I18n.t('site_settings.general.logo_links_to_custom_url') }
@@ -414,6 +418,16 @@ const GeneralSiteSettingsP = ({
</div>
</div>
<div className="formGroup">
<div className="checkboxSwitch">
<input {...register('hideUnusedStatusesInFilterByStatus')} type="checkbox" id="hide_unused_statuses_in_filter_by_status_checkbox" />
<label htmlFor="hide_unused_statuses_in_filter_by_status_checkbox">{ getLabel('tenant_setting', 'hide_unused_statuses_in_filter_by_status') }</label>
<SmallMutedText>
{ I18n.t('site_settings.general.hide_unused_statuses_in_filter_by_status_help') }
</SmallMutedText>
</div>
</div>
<div className="formGroup">
<div className="checkboxSwitch">
<input {...register('showPoweredBy')} type="checkbox" id="show_powered_by_checkbox" />
@@ -424,7 +438,7 @@ const GeneralSiteSettingsP = ({
<br />
<Button onClick={() => null} disabled={!isDirty}>
<Button onClick={() => null} disabled={!isDirty} className="generalSiteSettingsSubmit">
{I18n.t('common.buttons.update')}
</Button>
</form>
@@ -445,4 +459,4 @@ const GeneralSiteSettingsP = ({
);
}
export default GeneralSiteSettingsP;
export default GeneralSiteSettingsP;

View File

@@ -33,6 +33,7 @@ const mapDispatchToProps = (dispatch: any) => ({
collapseBoardsInHeader: TenantSettingCollapseBoardsInHeader,
showVoteCount: boolean,
showVoteButtonInBoard: boolean,
hideUnusedStatusesInFilterByStatus: boolean,
showPoweredBy: boolean,
authenticityToken: string
): Promise<any> {
@@ -52,6 +53,7 @@ const mapDispatchToProps = (dispatch: any) => ({
collapse_boards_in_header: collapseBoardsInHeader,
show_vote_count: showVoteCount,
show_vote_button_in_board: showVoteButtonInBoard,
hide_unused_statuses_in_filter_by_status: hideUnusedStatusesInFilterByStatus,
show_powered_by: showPoweredBy,
},
locale,

View File

@@ -61,6 +61,7 @@ interface ITenantSetting {
show_vote_count?: boolean;
show_vote_button_in_board?: boolean;
show_roadmap_in_header?: boolean;
hide_unused_statuses_in_filter_by_status?: boolean;
show_powered_by?: boolean;
collapse_boards_in_header?: TenantSettingCollapseBoardsInHeader;
logo_links_to?: TenantSettingLogoLinksTo;

View File

@@ -12,12 +12,13 @@ class TenantSettingPolicy < ApplicationPolicy
:feedback_approval_policy,
:show_vote_count,
:show_vote_button_in_board,
:hide_unused_statuses_in_filter_by_status,
:show_powered_by,
:logo_links_to,
:logo_custom_url,
:show_roadmap_in_header,
:collapse_boards_in_header,
:custom_css
:custom_css,
]
else
[]

View File

@@ -8,6 +8,7 @@
currentUserFullName: user_signed_in? ? current_user.full_name_or_email : '',
tenantSetting: @tenant_setting,
componentRenderedAt: Time.now.to_i,
postStatusesToShowInFilter: @post_statuses_to_show_in_filter,
authenticityToken: form_authenticity_token
}
)

View File

@@ -11,6 +11,7 @@
brandDisplaySetting: @tenant_setting.brand_display,
showVoteCount: @tenant_setting.show_vote_count,
showVoteButtonInBoard: @tenant_setting.show_vote_button_in_board,
hideUnusedStatusesInFilterByStatus: @tenant_setting.hide_unused_statuses_in_filter_by_status,
showPoweredBy: @tenant_setting.show_powered_by,
rootBoardId: @tenant_setting.root_board_id.to_s,
customDomain: @tenant.custom_domain,

View File

@@ -133,6 +133,7 @@ en:
feedback_approval_policy: 'Feedback approval policy'
show_vote_count: 'Show vote count to users'
show_vote_button_in_board: 'Show vote buttons in board page'
hide_unused_statuses_in_filter_by_status: 'Hide unused statuses in filter by status'
show_powered_by: 'Show "Powered by Astuto"'
root_board_id: 'Root page'
logo_links_to: 'Logo links to'

View File

@@ -206,6 +206,7 @@ en:
custom_domain_learn_more: 'Learn how to configure a custom domain'
show_vote_count_help: 'If you enable this setting, users will be able to see the vote count of posts. This may incentivize users to vote on already popular posts, leading to a snowball effect.'
show_vote_button_in_board_help: 'If you enable this setting, users will be able to vote posts from the board page. This may incentivize users to vote on more posts, leading to a higher number of votes but of lower significance.'
hide_unused_statuses_in_filter_by_status_help: 'If you enable this setting, only statuses that are assigned to at least one post in that board will be shown in the filter by status sidebar filter.'
boards:
title: 'Boards'
empty: 'There are no boards. Create one below!'

View File

@@ -0,0 +1,5 @@
class AddHideUnusedStatusesInFilterByStatusToTenantSettings < ActiveRecord::Migration[6.1]
def change
add_column :tenant_settings, :hide_unused_statuses_in_filter_by_status, :boolean, default: false, null: false
end
end

View File

@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2024_09_16_140807) do
ActiveRecord::Schema.define(version: 2024_09_17_140122) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -185,6 +185,7 @@ ActiveRecord::Schema.define(version: 2024_09_16_140807) do
t.boolean "use_browser_locale", default: false, null: false
t.integer "logo_links_to", default: 0, null: false
t.string "logo_custom_url"
t.boolean "hide_unused_statuses_in_filter_by_status", default: false, null: false
t.index ["tenant_id"], name: "index_tenant_settings_on_tenant_id"
end