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

Commit edab37d

Browse files
committed
FEATURE: Display features that rely on multiple personas.
This change makes the previously hidden feature page visible while displaying features, like the AI helper, which relies on multiple personas.
1 parent a68ab76 commit edab37d

File tree

14 files changed

+371
-291
lines changed

14 files changed

+371
-291
lines changed

admin/assets/javascripts/discourse/routes/admin-plugins-show-discourse-ai-features-edit.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export default class AdminPluginsShowDiscourseAiFeaturesEdit extends DiscourseRo
1212

1313
const { site_settings } = await ajax("/admin/config/site_settings.json", {
1414
data: {
15-
filter_area: `ai-features/${currentFeature.ref}`,
15+
filter_area: `ai-features/${currentFeature.module_name}`,
1616
plugin: "discourse-ai",
1717
category: "discourse_ai",
1818
},

admin/assets/javascripts/discourse/templates/admin-plugins/show/discourse-ai-features/index.gjs

Lines changed: 0 additions & 156 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<AiFeatures @features={{this.model}} />

app/controllers/discourse_ai/admin/ai_features_controller.rb

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,22 @@ def index
1111

1212
def edit
1313
raise Discourse::InvalidParameters.new(:id) if params[:id].blank?
14-
render json: serialize_feature(DiscourseAi::Features.find_feature_by_id(params[:id].to_i))
14+
render json: serialize_module(DiscourseAi::Features.find_module_by_id(params[:id].to_i))
1515
end
1616

1717
private
1818

19-
def serialize_features(features)
20-
features.map { |feature| feature.merge(persona: serialize_persona(feature[:persona])) }
19+
def serialize_features(modules)
20+
modules.map { |a_module| serialize_module(a_module) }
2121
end
2222

23-
def serialize_feature(feature)
24-
return nil if feature.blank?
23+
def serialize_module(a_module)
24+
return nil if a_module.blank?
2525

26-
feature.merge(persona: serialize_persona(feature[:persona]))
26+
a_module.merge(
27+
features:
28+
a_module[:features].map { |f| f.merge(persona: serialize_persona(f[:persona])) },
29+
)
2730
end
2831

2932
def serialize_persona(persona)

assets/javascripts/discourse/admin/models/ai-feature.js

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,6 @@ import RestModel from "discourse/models/rest";
22

33
export default class AiFeature extends RestModel {
44
createProperties() {
5-
return this.getProperties(
6-
"id",
7-
"name",
8-
"ref",
9-
"description",
10-
"enable_setting",
11-
"persona",
12-
"persona_setting"
13-
);
5+
return this.getProperties("id", "module", "global_enabled", "features");
146
}
157
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { concat } from "@ember/helper";
2+
import { gt } from "truth-helpers";
3+
import DButton from "discourse/components/d-button";
4+
import { i18n } from "discourse-i18n";
5+
6+
const AiFeaturesList = <template>
7+
<div class="ai-features-list">
8+
{{#each @modules as |module|}}
9+
<div class="ai-module">
10+
<div class="ai-module__header">
11+
<div class="ai-module__module-title">
12+
<h3>{{i18n
13+
(concat "discourse_ai.features." module.module_name ".name")
14+
}}</h3>
15+
<DButton
16+
class="btn-medium edit"
17+
@label="discourse_ai.features.edit"
18+
@route="adminPlugins.show.discourse-ai-features.edit"
19+
@routeModels={{module.id}}
20+
/>
21+
</div>
22+
<div>{{i18n
23+
(concat
24+
"discourse_ai.features." module.module_name ".description"
25+
)
26+
}}</div>
27+
</div>
28+
29+
<div class="admin-section-landing-wrapper ai-feature-cards">
30+
{{#each module.features as |feature|}}
31+
<div class="admin-section-landing-item ai-feature-card">
32+
<div class="admin-section-landing-item__content">
33+
<div class="ai-feature-card__feature-name">
34+
{{i18n
35+
(concat
36+
"discourse_ai.features."
37+
module.module_name
38+
"."
39+
feature.name
40+
)
41+
}}
42+
{{#unless feature.enabled}}
43+
<span>{{i18n "discourse_ai.features.disabled"}}</span>
44+
{{/unless}}
45+
</div>
46+
<div class="ai-feature-card__persona">
47+
<span>{{i18n "discourse_ai.features.persona"}}</span>
48+
{{#if feature.persona}}
49+
<DButton
50+
class="btn-flat btn-small ai-feature-card__persona-button"
51+
@translatedLabel={{feature.persona.name}}
52+
@route="adminPlugins.show.discourse-ai-personas.edit"
53+
@routeModels={{feature.persona.id}}
54+
/>
55+
{{else}}
56+
{{i18n "discourse_ai.features.no_persona"}}
57+
{{/if}}
58+
</div>
59+
{{#if feature.persona}}
60+
<div class="ai-feature-card__groups">
61+
<span>{{i18n "discourse_ai.features.groups"}}</span>
62+
{{#if (gt feature.persona.allowed_groups.length 0)}}
63+
<ul class="ai-feature-card__item-groups">
64+
{{#each feature.persona.allowed_groups as |group|}}
65+
<li>{{group.name}}</li>
66+
{{/each}}
67+
</ul>
68+
{{else}}
69+
{{i18n "discourse_ai.features.no_groups"}}
70+
{{/if}}
71+
</div>
72+
{{/if}}
73+
</div>
74+
</div>
75+
{{/each}}
76+
</div>
77+
</div>
78+
{{/each}}
79+
</div>
80+
</template>;
81+
82+
export default AiFeaturesList;
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import Component from "@glimmer/component";
2+
import { tracked } from "@glimmer/tracking";
3+
import { fn } from "@ember/helper";
4+
import { action } from "@ember/object";
5+
import { service } from "@ember/service";
6+
import { eq } from "truth-helpers";
7+
import DBreadcrumbsItem from "discourse/components/d-breadcrumbs-item";
8+
import DButton from "discourse/components/d-button";
9+
import DPageSubheader from "discourse/components/d-page-subheader";
10+
import { i18n } from "discourse-i18n";
11+
import AiFeaturesList from "./ai-features-list";
12+
13+
const CONFIGURED = "configured";
14+
const UNCONFIGURED = "unconfigured";
15+
16+
export default class AiFeatures extends Component {
17+
@service adminPluginNavManager;
18+
19+
@tracked selectedFeatureGroup = CONFIGURED;
20+
21+
constructor() {
22+
super(...arguments);
23+
24+
if (this.configuredFeatures.length === 0) {
25+
this.selectedFeatureGroup = UNCONFIGURED;
26+
}
27+
}
28+
29+
get featureGroups() {
30+
return [
31+
{ id: CONFIGURED, label: "discourse_ai.features.nav.configured" },
32+
{ id: UNCONFIGURED, label: "discourse_ai.features.nav.unconfigured" },
33+
];
34+
}
35+
36+
get configuredFeatures() {
37+
return this.args.features.filter(
38+
(feature) => feature.module_enabled === true
39+
);
40+
}
41+
42+
get unconfiguredFeatures() {
43+
return this.args.features.filter(
44+
(feature) => feature.module_enabled === false
45+
);
46+
}
47+
48+
@action
49+
selectFeatureGroup(groupId) {
50+
this.selectedFeatureGroup = groupId;
51+
}
52+
53+
<template>
54+
<DBreadcrumbsItem
55+
@path="/admin/plugins/{{this.adminPluginNavManager.currentPlugin.name}}/ai-features"
56+
@label={{i18n "discourse_ai.features.short_title"}}
57+
/>
58+
<section class="ai-feature-list admin-detail">
59+
<DPageSubheader
60+
@titleLabel={{i18n "discourse_ai.features.short_title"}}
61+
@descriptionLabel={{i18n "discourse_ai.features.description"}}
62+
@learnMoreUrl="todo"
63+
/>
64+
65+
<div class="ai-feature-groups">
66+
{{#each this.featureGroups as |groupData|}}
67+
<DButton
68+
class={{if
69+
(eq this.selectedFeatureGroup groupData.id)
70+
"btn-primary"
71+
"btn-default"
72+
}}
73+
@action={{fn this.selectFeatureGroup groupData.id}}
74+
@label={{groupData.label}}
75+
/>
76+
{{/each}}
77+
</div>
78+
79+
{{#if (eq this.selectedFeatureGroup "configured")}}
80+
<AiFeaturesList @modules={{this.configuredFeatures}} />
81+
{{else}}
82+
<AiFeaturesList @modules={{this.unconfiguredFeatures}} />
83+
{{/if}}
84+
</section>
85+
</template>
86+
}

0 commit comments

Comments
 (0)