From 9dfae3d472ad7fcf3953a1c781f5b3dacb6c2ab4 Mon Sep 17 00:00:00 2001 From: Keegan George Date: Tue, 25 Mar 2025 13:36:52 -0700 Subject: [PATCH] UX: Convert sentiment analysis overview to horizontal bars (#1216) The sentiment analysis report page initially showcases sentiments via category/tag by doughnut visualizations. However, this isn't an optimal view for quickly scanning and comparing each result. This PR updates the overview to include a table visualization with horizontal bars to represent sentiment analysis instead of doughnuts. Doughnut visualizations are still maintained however when accessing the sentiment data in the drill down for individual entries. This approach is an intermediary step, as we will eventually add whole clustering and sizing visualization instead of a table. As such, no relevant tests are added in this PR. --- .../admin-report-sentiment-analysis.gjs | 93 ++++++++++++++----- .../ai-sentiment-horizontal-bar.gjs | 30 ++++++ .../modules/sentiment/common/dashboard.scss | 51 ++++++++++ config/locales/client.en.yml | 6 ++ 4 files changed, 157 insertions(+), 23 deletions(-) create mode 100644 assets/javascripts/discourse/components/ai-sentiment-horizontal-bar.gjs diff --git a/assets/javascripts/discourse/components/admin-report-sentiment-analysis.gjs b/assets/javascripts/discourse/components/admin-report-sentiment-analysis.gjs index 62a18dfd..154d0730 100644 --- a/assets/javascripts/discourse/components/admin-report-sentiment-analysis.gjs +++ b/assets/javascripts/discourse/components/admin-report-sentiment-analysis.gjs @@ -23,6 +23,7 @@ import closeOnClickOutside from "discourse/modifiers/close-on-click-outside"; import { i18n } from "discourse-i18n"; import DTooltip from "float-kit/components/d-tooltip"; import DoughnutChart from "discourse/plugins/discourse-ai/discourse/components/doughnut-chart"; +import AiSentimentHorizontalBar from "../components/ai-sentiment-horizontal-bar"; export default class AdminReportSentimentAnalysis extends Component { @service router; @@ -82,6 +83,15 @@ export default class AdminReportSentimentAnalysis extends Component { } } + get groupingType() { + const dataSample = this.args.model.data[0]; + const localePrefix = + "discourse_ai.sentiments.sentiment_analysis.group_types"; + return dataSample.category_name + ? i18n(`${localePrefix}.category`) + : i18n(`${localePrefix}.tag`); + } + get colors() { return ["#2ecc71", "#95a5a6", "#e74c3c"]; } @@ -107,7 +117,17 @@ export default class AdminReportSentimentAnalysis extends Component { this.calculateNeutralScore(data), data.negative_count, ], + score_map: { + positive: data.positive_count, + neutral: this.calculateNeutralScore(data), + negative: data.negative_count, + }, total_score: data.total_count, + widths: { + positive: (data.positive_count / data.total_count) * 100, + neutral: (this.calculateNeutralScore(data) / data.total_count) * 100, + negative: (data.negative_count / data.total_count) * 100, + }, }; }); } @@ -302,29 +322,56 @@ export default class AdminReportSentimentAnalysis extends Component { {{#unless this.showingSelectedChart}}
- {{#each this.transformedData as |data|}} -
- -
- {{/each}} + + + + + + + + + {{#each this.transformedData as |data|}} + + + + + + {{/each}} + +
{{this.groupingType}}{{i18n + "discourse_ai.sentiments.sentiment_analysis.table.total_count" + }}{{i18n + "discourse_ai.sentiments.sentiment_analysis.table.sentiment" + }}
{{data.title}}{{data.total_score}} + + + +
{{/unless}} diff --git a/assets/javascripts/discourse/components/ai-sentiment-horizontal-bar.gjs b/assets/javascripts/discourse/components/ai-sentiment-horizontal-bar.gjs new file mode 100644 index 00000000..ae8cd50b --- /dev/null +++ b/assets/javascripts/discourse/components/ai-sentiment-horizontal-bar.gjs @@ -0,0 +1,30 @@ +import { concat } from "@ember/helper"; +import { htmlSafe } from "@ember/template"; +import { gt } from "truth-helpers"; +import { i18n } from "discourse-i18n"; +import DTooltip from "float-kit/components/d-tooltip"; + +const AiSentimentHorizontalBar = ; + +export default AiSentimentHorizontalBar; diff --git a/assets/stylesheets/modules/sentiment/common/dashboard.scss b/assets/stylesheets/modules/sentiment/common/dashboard.scss index 0ae79dde..4fd9d412 100644 --- a/assets/stylesheets/modules/sentiment/common/dashboard.scss +++ b/assets/stylesheets/modules/sentiment/common/dashboard.scss @@ -248,3 +248,54 @@ display: none; } } + +.sentiment-analysis-table { + margin: 1rem; + + &__total-score { + font-weight: bold; + font-size: var(--font-up-1); + } + + &__row { + cursor: pointer; + } +} + +.sentiment-horizontal-bar { + display: flex; + + &__count { + font-weight: bold; + font-size: var(--font-down-1); + color: var(--secondary); + } + + &__positive, + &__neutral, + &__negative { + display: flex; + flex-flow: column nowrap; + justify-content: flex-end; + align-items: center; + padding: 0.75rem; + border-left: 2px solid var(--secondary); + border-right: 2px solid var(--secondary); + } + + &__positive { + background: rgb(var(--d-sentiment-report-positive-rgb)); + border-top-left-radius: var(--d-border-radius); + border-bottom-left-radius: var(--d-border-radius); + } + + &__negative { + background: rgb(var(--d-sentiment-report-negative-rgb)); + } + + &__neutral { + background: rgb(var(--d-sentiment-report-neutral-rgb)); + border-top-right-radius: var(--d-border-radius); + border-bottom-right-radius: var(--d-border-radius); + } +} diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 5f4c7295..a7105cc9 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -700,6 +700,12 @@ en: positive: "Positive" neutral: "Neutral" negative: "Negative" + group_types: + category: "Category" + tag: "Tag" + table: + sentiment: "Sentiment" + total_count: "Total" summarization: chat: