Skip to content
This repository was archived by the owner on Jul 22, 2025. It is now read-only.

Commit a8d4d0f

Browse files
committed
WIP: selected chart UX improvements
1 parent 3066dff commit a8d4d0f

File tree

4 files changed

+142
-59
lines changed

4 files changed

+142
-59
lines changed

app/controllers/discourse_ai/sentiment/sentiment_controller.rb

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@ class SentimentController < ::Admin::StaffController
66
include Constants
77
requires_plugin ::DiscourseAi::PLUGIN_NAME
88

9-
# DEFAULT_POSTS_LIMIT = 50
10-
# MAX_POSTS_LIMIT = 100
11-
DEFAULT_POSTS_LIMIT = 3
12-
MAX_POSTS_LIMIT = 3
9+
DEFAULT_POSTS_LIMIT = 50
10+
MAX_POSTS_LIMIT = 100
1311

1412
def posts
1513
group_by = params.required(:group_by)&.to_sym

assets/javascripts/discourse/components/admin-report-sentiment-analysis.gjs

Lines changed: 120 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { tracked } from "@glimmer/tracking";
33
import { fn, hash } from "@ember/helper";
44
import { on } from "@ember/modifier";
55
import { action, get } from "@ember/object";
6+
import { and } from "truth-helpers";
7+
import DButton from "discourse/components/d-button";
68
import PostList from "discourse/components/post-list";
79
import dIcon from "discourse/helpers/d-icon";
810
import { ajax } from "discourse/lib/ajax";
@@ -11,12 +13,14 @@ import Post from "discourse/models/post";
1113
import closeOnClickOutside from "discourse/modifiers/close-on-click-outside";
1214
import { i18n } from "discourse-i18n";
1315
import DoughnutChart from "discourse/plugins/discourse-ai/discourse/components/doughnut-chart";
16+
import HorizontalOverflowNav from "discourse/components/horizontal-overflow-nav";
1417

1518
export default class AdminReportSentimentAnalysis extends Component {
1619
@tracked selectedChart = null;
1720
@tracked posts = null;
1821
@tracked hasMorePosts = false;
1922
@tracked nextOffset = 0;
23+
@tracked showingSelectedChart = false;
2024

2125
get colors() {
2226
return ["#2ecc71", "#95a5a6", "#e74c3c"];
@@ -66,11 +70,17 @@ export default class AdminReportSentimentAnalysis extends Component {
6670

6771
@action
6872
async showDetails(data) {
73+
if (this.selectedChart === data) {
74+
console.log("already selected bro!");
75+
// Don't do anything if the same chart is clicked again
76+
return;
77+
}
78+
6979
this.selectedChart = data;
80+
this.showingSelectedChart = true;
7081

7182
try {
7283
const response = await this.postRequest();
73-
7484
this.posts = response.posts.map((post) => Post.create(post));
7585
this.hasMorePosts = response.has_more;
7686
this.nextOffset = response.next_offset;
@@ -102,88 +112,144 @@ export default class AdminReportSentimentAnalysis extends Component {
102112
return {
103113
id: "positive",
104114
text: i18n(
105-
"discourse_ai.sentiments.sentiment_analysis.score_types.positive"
115+
"discourse_ai.sentiments.sentiment_analysis.filter_types.positive"
106116
),
107117
icon: "face-smile",
108118
};
109119
case "neutral":
110120
return {
111121
id: "neutral",
112122
text: i18n(
113-
"discourse_ai.sentiments.sentiment_analysis.score_types.neutral"
123+
"discourse_ai.sentiments.sentiment_analysis.filter_types.neutral"
114124
),
115125
icon: "face-meh",
116126
};
117127
case "negative":
118128
return {
119129
id: "negative",
120130
text: i18n(
121-
"discourse_ai.sentiments.sentiment_analysis.score_types.negative"
131+
"discourse_ai.sentiments.sentiment_analysis.filter_types.negative"
122132
),
123133
icon: "face-angry",
124134
};
125135
}
126136
}
127137

128138
doughnutTitle(data) {
129-
if (data?.total_score) {
130-
return `${data.title} (${data.total_score})`;
131-
} else {
132-
return data.title;
139+
const MAX_TITLE_LENGTH = 18;
140+
const title = data?.title || "";
141+
const score = data?.total_score ? ` (${data.total_score})` : "";
142+
143+
if (title.length + score.length > MAX_TITLE_LENGTH) {
144+
return (
145+
title.substring(0, MAX_TITLE_LENGTH - score.length) + "..." + score
146+
);
133147
}
148+
149+
return title + score;
150+
}
151+
152+
@action
153+
backToAllCharts() {
154+
this.showingSelectedChart = false;
155+
this.selectedChart = null;
156+
}
157+
158+
get postFilters() {
159+
return [
160+
{
161+
id: "all",
162+
text: `${i18n(
163+
"discourse_ai.sentiments.sentiment_analysis.filter_types.all"
164+
)} (${this.selectedChart.total_score})`,
165+
icon: "bars-staggered",
166+
active: true,
167+
},
168+
{
169+
id: "positive",
170+
text: `${i18n(
171+
"discourse_ai.sentiments.sentiment_analysis.filter_types.positive"
172+
)} (${this.selectedChart.scores[0]})`,
173+
icon: "face-smile",
174+
active: false,
175+
},
176+
{
177+
id: "neutral",
178+
text: `${i18n(
179+
"discourse_ai.sentiments.sentiment_analysis.filter_types.neutral"
180+
)} (${this.selectedChart.scores[1]})`,
181+
icon: "face-meh",
182+
active: false,
183+
},
184+
{
185+
id: "negative",
186+
text: `${i18n(
187+
"discourse_ai.sentiments.sentiment_analysis.filter_types.negative"
188+
)} (${this.selectedChart.scores[2]})`,
189+
icon: "face-angry",
190+
active: false,
191+
},
192+
];
134193
}
135194

136195
<template>
137-
<div class="admin-report-sentiment-analysis">
138-
{{#each this.transformedData as |data|}}
139-
<div
140-
class="admin-report-sentiment-analysis__chart-wrapper"
141-
role="button"
142-
{{on "click" (fn this.showDetails data)}}
143-
{{closeOnClickOutside
144-
(fn (mut this.selectedChart) null)
145-
(hash
146-
targetSelector=".admin-report-sentiment-analysis-details"
147-
secondaryTargetSelector=".admin-report-sentiment-analysis"
148-
)
149-
}}
150-
>
151-
<DoughnutChart
152-
@labels={{@model.labels}}
153-
@colors={{this.colors}}
154-
@data={{data.scores}}
155-
@doughnutTitle={{this.doughnutTitle data}}
156-
/>
157-
</div>
158-
{{/each}}
159-
</div>
160-
161-
{{#if this.selectedChart}}
162-
<div class="admin-report-sentiment-analysis-details">
196+
{{#unless this.showingSelectedChart}}
197+
<div class="admin-report-sentiment-analysis">
198+
{{#each this.transformedData as |data|}}
199+
<div
200+
class="admin-report-sentiment-analysis__chart-wrapper"
201+
role="button"
202+
{{on "click" (fn this.showDetails data)}}
203+
{{closeOnClickOutside
204+
(fn (mut this.selectedChart) null)
205+
(hash
206+
targetSelector=".admin-report-sentiment-analysis-details"
207+
secondaryTargetSelector=".admin-report-sentiment-analysis"
208+
)
209+
}}
210+
>
211+
<DoughnutChart
212+
@labels={{@model.labels}}
213+
@colors={{this.colors}}
214+
@data={{data.scores}}
215+
@doughnutTitle={{this.doughnutTitle data}}
216+
/>
217+
</div>
218+
{{/each}}
219+
</div>
220+
{{/unless}}
221+
222+
{{#if (and this.selectedChart this.showingSelectedChart)}}
223+
<div class="admin-report-sentiment-analysis__selected-chart">
224+
<DButton
225+
@label="back_button"
226+
@icon="chevron-left"
227+
class="btn-flat"
228+
@action={{this.backToAllCharts}}
229+
/>
163230
<h3 class="admin-report-sentiment-analysis-details__title">
164231
{{this.selectedChart.title}}
165232
</h3>
166233

167-
<ul class="admin-report-sentiment-analysis-details__scores">
168-
<li>
169-
{{dIcon "face-smile"}}
170-
{{i18n
171-
"discourse_ai.sentiments.sentiment_analysis.score_types.positive"
172-
}}:
173-
{{get this.selectedChart.scores 0}}</li>
174-
<li>
175-
{{dIcon "face-meh"}}
176-
{{i18n
177-
"discourse_ai.sentiments.sentiment_analysis.score_types.neutral"
178-
}}:
179-
{{get this.selectedChart.scores 1}}</li>
180-
<li>
181-
{{dIcon "face-angry"}}
182-
{{i18n
183-
"discourse_ai.sentiments.sentiment_analysis.score_types.negative"
184-
}}:
185-
{{get this.selectedChart.scores 2}}</li>
186-
</ul>
234+
<DoughnutChart
235+
@labels={{@model.labels}}
236+
@colors={{this.colors}}
237+
@data={{this.selectedChart.scores}}
238+
@doughnutTitle={{this.doughnutTitle this.selectedChart}}
239+
/>
240+
</div>
241+
<div class="admin-report-sentiment-analysis-details">
242+
<HorizontalOverflowNav>
243+
{{#each this.postFilters as |filter|}}
244+
<li>
245+
<DButton
246+
@icon={{filter.icon}}
247+
@translatedLabel={{filter.text}}
248+
class="btn-flat {{if filter.active 'active'}}"
249+
/>
250+
</li>
251+
{{/each}}
252+
</HorizontalOverflowNav>
187253

188254
<PostList
189255
@posts={{this.posts}}

assets/stylesheets/modules/sentiment/common/dashboard.scss

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@
8383
cursor: pointer;
8484
}
8585
}
86+
87+
&__selected-chart {
88+
border: 1px solid var(--primary-low);
89+
border-radius: var(--d-border-radius);
90+
padding: 1rem;
91+
}
8692
}
8793

8894
:root {
@@ -99,8 +105,20 @@
99105
overflow-y: auto;
100106
height: 100%;
101107

108+
.horizontal-overflow-nav {
109+
border-bottom: 1px solid var(--primary-low);
110+
margin-bottom: 1rem;
111+
}
112+
102113
&__title {
103114
font-size: var(--font-up-2);
115+
margin: 0 auto;
116+
text-align: center;
117+
margin-bottom: 1rem;
118+
margin-top: 0.3rem;
119+
padding-top: 2rem;
120+
padding-bottom: 1rem;
121+
border-top: 1px solid var(--primary-low);
104122
}
105123

106124
&__scores {

config/locales/client.en.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,8 @@ en:
651651
dashboard:
652652
title: "Sentiment"
653653
sentiment_analysis:
654-
score_types:
654+
filter_types:
655+
all: "All"
655656
positive: "Positive"
656657
neutral: "Neutral"
657658
negative: "Negative"

0 commit comments

Comments
 (0)