mirror of
https://github.com/lingble/chatwoot.git
synced 2025-10-29 18:22:53 +00:00
fix: Include uncategorized articles in the all article section to allow edit/delete (#10153)
Fixes https://github.com/chatwoot/chatwoot/issues/9935 Fixes https://github.com/chatwoot/chatwoot/issues/8213 The articles were grouped by category, with locale being a derived attribute from the category. If a category was deleted, the article wouldn't appear on the dashboard. However, due to a bug, it would show up in the uncategorized section on the public portal, leaving agents unable to edit or update the article. To address this issue, I've added a locale attribute directly to the article. This attribute is automatically set from the category or the portal's default locale if not supplied. The API parameters now use this attribute to filter articles. As a result, the dashboard will display articles even if they're not associated with a category, improving the overall workflow. **Main updates:** - Add locale attribute to the Article model. Add db migration to back fill the data based on the above logic. - Add a new scope search_by_locale and use it instead of search_by_category_locale. - Update the ERB template to include the locale filter. - Move from `joins` to `left_outer_joins` to include the articles with no categories. --------- Co-authored-by: Sojan <sojan@pepalo.com>
This commit is contained in:
@@ -819,7 +819,7 @@ GEM
|
||||
rack-proxy (>= 0.6.1)
|
||||
railties (>= 5.2)
|
||||
semantic_range (>= 2.3.0)
|
||||
webrick (1.8.1)
|
||||
webrick (1.8.2)
|
||||
websocket-driver (0.7.6)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.5)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
# id :bigint not null, primary key
|
||||
# content :text
|
||||
# description :text
|
||||
# locale :string default("en"), not null
|
||||
# meta :jsonb
|
||||
# position :integer
|
||||
# slug :string not null
|
||||
@@ -55,12 +56,14 @@ class Article < ApplicationRecord
|
||||
|
||||
# ensuring that the position is always set correctly
|
||||
before_create :add_position_to_article
|
||||
before_create :add_locale_to_article
|
||||
after_save :category_id_changed_action, if: :saved_change_to_category_id?
|
||||
|
||||
enum status: { draft: 0, published: 1, archived: 2 }
|
||||
|
||||
scope :search_by_category_slug, ->(category_slug) { where(categories: { slug: category_slug }) if category_slug.present? }
|
||||
scope :search_by_category_locale, ->(locale) { where(categories: { locale: locale }) if locale.present? }
|
||||
scope :search_by_locale, ->(locale) { where(locale: locale) if locale.present? }
|
||||
scope :search_by_author, ->(author_id) { where(author_id: author_id) if author_id.present? }
|
||||
scope :search_by_status, ->(status) { where(status: status) if status.present? }
|
||||
scope :order_by_updated_at, -> { reorder(updated_at: :desc) }
|
||||
@@ -83,11 +86,11 @@ class Article < ApplicationRecord
|
||||
)
|
||||
|
||||
def self.search(params)
|
||||
records = joins(
|
||||
records = left_outer_joins(
|
||||
:category
|
||||
).search_by_category_slug(
|
||||
params[:category_slug]
|
||||
).search_by_category_locale(params[:locale]).search_by_author(params[:author_id]).search_by_status(params[:status])
|
||||
).search_by_locale(params[:locale]).search_by_author(params[:author_id]).search_by_status(params[:status])
|
||||
|
||||
records = records.text_search(params[:query]) if params[:query].present?
|
||||
records
|
||||
@@ -140,6 +143,14 @@ class Article < ApplicationRecord
|
||||
update_article_position_in_category
|
||||
end
|
||||
|
||||
def add_locale_to_article
|
||||
self.locale = if category.present?
|
||||
category.locale
|
||||
else
|
||||
portal.default_locale
|
||||
end
|
||||
end
|
||||
|
||||
def add_position_to_article
|
||||
# on creation if a position is already present, ignore it
|
||||
return if position.present?
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</div>
|
||||
<%# Uncategorized articles %>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-x-6 gap-y-6">
|
||||
<% if @portal.articles.where(status: :published, category_id: nil).count > 0 %>
|
||||
<% if @portal.articles.where(status: :published, category_id: nil, locale: @locale).count > 0 %>
|
||||
<%= render "public/api/v1/portals/uncategorized-block", category: "Uncategorized", portal: @portal %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
24
db/migrate/20240923215335_add_locale_to_article.rb
Normal file
24
db/migrate/20240923215335_add_locale_to_article.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
class AddLocaleToArticle < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :articles, :locale, :string, default: 'en', null: false
|
||||
|
||||
set_locale_from_category
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_locale_from_category
|
||||
Article.find_in_batches do |article_batch|
|
||||
article_batch.each do |article|
|
||||
locale = if article.category.present?
|
||||
article.category.locale
|
||||
else
|
||||
article.portal.default_locale
|
||||
end
|
||||
# rubocop:disable Rails/SkipsModelValidations
|
||||
article.update_columns(locale: locale)
|
||||
# rubocop:enable Rails/SkipsModelValidations
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.0].define(version: 2024_07_26_220747) do
|
||||
ActiveRecord::Schema[7.0].define(version: 2024_09_23_215335) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_stat_statements"
|
||||
enable_extension "pg_trgm"
|
||||
@@ -147,6 +147,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_07_26_220747) do
|
||||
t.jsonb "meta", default: {}
|
||||
t.string "slug", null: false
|
||||
t.integer "position"
|
||||
t.string "locale", default: "en", null: false
|
||||
t.index ["associated_article_id"], name: "index_articles_on_associated_article_id"
|
||||
t.index ["author_id"], name: "index_articles_on_author_id"
|
||||
t.index ["slug"], name: "index_articles_on_slug", unique: true
|
||||
|
||||
@@ -194,6 +194,20 @@ RSpec.describe 'Api::V1::Accounts::Articles', type: :request do
|
||||
expect(json_response['payload'].count).to be 2
|
||||
end
|
||||
|
||||
it 'get all articles with uncategorized articles' do
|
||||
article2 = create(:article, account_id: account.id, portal: portal, category: nil, locale: 'en', author_id: agent.id)
|
||||
expect(article2.id).not_to be_nil
|
||||
|
||||
get "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/articles",
|
||||
headers: agent.create_new_auth_token,
|
||||
params: {}
|
||||
expect(response).to have_http_status(:success)
|
||||
json_response = response.parsed_body
|
||||
expect(json_response['payload'].count).to be 2
|
||||
expect(json_response['payload'][0]['id']).to eq article2.id
|
||||
expect(json_response['payload'][0]['category']['id']).to be_nil
|
||||
end
|
||||
|
||||
it 'get all articles with searched params' do
|
||||
article2 = create(:article, account_id: account.id, portal: portal, category: category, author_id: agent.id)
|
||||
expect(article2.id).not_to be_nil
|
||||
|
||||
@@ -2,6 +2,7 @@ FactoryBot.define do
|
||||
factory :article, class: 'Article' do
|
||||
account_id { 1 }
|
||||
category_id { 1 }
|
||||
locale { 'en' }
|
||||
author_id { 1 }
|
||||
title { "#{Faker::Movie.title} #{SecureRandom.hex}" }
|
||||
content { 'MyText' }
|
||||
|
||||
@@ -40,6 +40,25 @@ RSpec.describe Article do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'add_locale_to_article' do
|
||||
let(:portal) { create(:portal, config: { allowed_locales: %w[en es pt], default_locale: 'es' }) }
|
||||
let(:category) { create(:category, slug: 'category_1', locale: 'pt', portal_id: portal.id) }
|
||||
|
||||
it 'adds locale to article from category' do
|
||||
article = create(:article, category_id: category.id, content: 'This is the content', description: 'this is the description',
|
||||
slug: 'this-is-title', title: 'this is title',
|
||||
portal_id: portal.id, author_id: user.id)
|
||||
expect(article.locale).to eq(category.locale)
|
||||
end
|
||||
|
||||
it 'adds locale to article from portal' do
|
||||
article = create(:article, content: 'This is the content', description: 'this is the description',
|
||||
slug: 'this-is-title', title: 'this is title',
|
||||
portal_id: portal.id, author_id: user.id)
|
||||
expect(article.locale).to eq(portal.default_locale)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'search' do
|
||||
let!(:portal_2) { create(:portal, account_id: account.id, config: { allowed_locales: %w[en es] }) }
|
||||
let!(:category_2) { create(:category, slug: 'category_2', locale: 'es', portal_id: portal_1.id) }
|
||||
|
||||
Reference in New Issue
Block a user