UX: Applying more admin UI guidelines (#956)
This commit applies further admin UI guidelines, now that they have been more fleshed out in core, to the AI admin UI: * Tools * LLMs * Personas The changes include but are not limited to: * Applying the table CSS classes, for desktop and mobile * Adding a description and learn more link for each tab * Adding an empty list placeholder with CTA using `AdminConfigAreaEmptyList` * Replacing custom headings with `AdminPageSubheader`
This commit is contained in:
parent
7a094cde18
commit
2f7895bb91
|
@ -1,5 +1,7 @@
|
|||
<AiToolEditor
|
||||
@tools={{this.allTools}}
|
||||
@model={{this.model}}
|
||||
@presets={{this.presets}}
|
||||
/>
|
||||
<section class="ai-persona-tool-editor__current admin-detail pull-left">
|
||||
<AiToolEditor
|
||||
@tools={{this.allTools}}
|
||||
@model={{this.model}}
|
||||
@presets={{this.presets}}
|
||||
/>
|
||||
</section>
|
|
@ -1,5 +1,7 @@
|
|||
<AiToolEditor
|
||||
@tools={{this.allTools}}
|
||||
@model={{this.model}}
|
||||
@presets={{this.presets}}
|
||||
/>
|
||||
<section class="ai-persona-tool-editor__current admin-detail pull-left">
|
||||
<AiToolEditor
|
||||
@tools={{this.allTools}}
|
||||
@model={{this.model}}
|
||||
@presets={{this.presets}}
|
||||
/>
|
||||
</section>
|
|
@ -4,7 +4,6 @@ import { action } from "@ember/object";
|
|||
import { LinkTo } from "@ember/routing";
|
||||
import { service } from "@ember/service";
|
||||
import DBreadcrumbsItem from "discourse/components/d-breadcrumbs-item";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import I18n from "discourse-i18n";
|
||||
import AdminPageSubheader from "admin/components/admin-page-subheader";
|
||||
|
@ -124,8 +123,10 @@ export default class AiLlmsListEditor extends Component {
|
|||
<section class="ai-llms-list-editor__configured">
|
||||
<AdminPageSubheader
|
||||
@titleLabel="discourse_ai.llms.configured.title"
|
||||
@descriptionLabel="discourse_ai.llms.preconfigured.description"
|
||||
@learnMoreUrl="https://meta.discourse.org/t/discourse-ai-large-language-model-llm-settings-page/319903"
|
||||
/>
|
||||
<table>
|
||||
<table class="d-admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{i18n "discourse_ai.llms.display_name"}}</th>
|
||||
|
@ -135,8 +136,11 @@ export default class AiLlmsListEditor extends Component {
|
|||
</thead>
|
||||
<tbody>
|
||||
{{#each @llms as |llm|}}
|
||||
<tr data-llm-id={{llm.name}} class="ai-llm-list__row">
|
||||
<td class="column-name">
|
||||
<tr
|
||||
data-llm-id={{llm.name}}
|
||||
class="ai-llm-list__row d-admin-row__content"
|
||||
>
|
||||
<td class="d-admin-row__overview">
|
||||
<h3>{{llm.display_name}}</h3>
|
||||
<p>
|
||||
{{this.modelDescription llm}}
|
||||
|
@ -149,18 +153,20 @@ export default class AiLlmsListEditor extends Component {
|
|||
</ul>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td class="column-provider">
|
||||
<td class="d-admin-row__detail">
|
||||
<div class="d-admin-row__mobile-label">
|
||||
{{i18n "discourse_ai.llms.provider"}}
|
||||
</div>
|
||||
{{i18n
|
||||
(concat "discourse_ai.llms.providers." llm.provider)
|
||||
}}
|
||||
</td>
|
||||
<td class="column-edit">
|
||||
<td class="d-admin-row__controls">
|
||||
<LinkTo
|
||||
@route="adminPlugins.show.discourse-ai-llms.show"
|
||||
class="btn btn-default"
|
||||
class="btn btn-default btn-small ai-llm-list__edit-button"
|
||||
@model={{llm.id}}
|
||||
>
|
||||
{{icon "wrench"}}
|
||||
<div class="d-button-label">
|
||||
{{i18n "discourse_ai.llms.edit"}}
|
||||
</div>
|
||||
|
@ -173,7 +179,17 @@ export default class AiLlmsListEditor extends Component {
|
|||
</section>
|
||||
{{/if}}
|
||||
<section class="ai-llms-list-editor__templates">
|
||||
<AdminPageSubheader @titleLabel={{this.preconfiguredTitle}} />
|
||||
<AdminPageSubheader
|
||||
@titleLabel={{this.preconfiguredTitle}}
|
||||
@descriptionLabel={{unless
|
||||
this.hasLlmElements
|
||||
"discourse_ai.llms.preconfigured.description"
|
||||
}}
|
||||
@learnMoreUrl={{unless
|
||||
this.hasLlmElements
|
||||
"https://meta.discourse.org/t/discourse-ai-large-language-model-llm-settings-page/319903"
|
||||
}}
|
||||
/>
|
||||
|
||||
<AdminSectionLandingWrapper
|
||||
class="ai-llms-list-editor__templates-list"
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { fn } from "@ember/helper";
|
||||
import { on } from "@ember/modifier";
|
||||
import { action } from "@ember/object";
|
||||
|
@ -9,26 +8,13 @@ import DBreadcrumbsItem from "discourse/components/d-breadcrumbs-item";
|
|||
import DToggleSwitch from "discourse/components/d-toggle-switch";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { cook } from "discourse/lib/text";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import I18n from "discourse-i18n";
|
||||
import AdminConfigAreaEmptyList from "admin/components/admin-config-area-empty-list";
|
||||
import AdminPageSubheader from "admin/components/admin-page-subheader";
|
||||
import AiPersonaEditor from "./ai-persona-editor";
|
||||
|
||||
export default class AiPersonaListEditor extends Component {
|
||||
@service adminPluginNavManager;
|
||||
@tracked _noPersonaText = null;
|
||||
|
||||
get noPersonaText() {
|
||||
if (this._noPersonaText === null) {
|
||||
const raw = I18n.t("discourse_ai.ai_persona.no_persona_selected");
|
||||
cook(raw).then((result) => {
|
||||
this._noPersonaText = result;
|
||||
});
|
||||
}
|
||||
|
||||
return this._noPersonaText;
|
||||
}
|
||||
|
||||
@action
|
||||
async toggleEnabled(persona) {
|
||||
|
@ -53,74 +39,76 @@ export default class AiPersonaListEditor extends Component {
|
|||
{{#if @currentPersona}}
|
||||
<AiPersonaEditor @model={{@currentPersona}} @personas={{@personas}} />
|
||||
{{else}}
|
||||
<div class="ai-persona-list-editor__header">
|
||||
<h3>{{i18n "discourse_ai.ai_persona.short_title"}}</h3>
|
||||
{{#unless @currentPersona.isNew}}
|
||||
<LinkTo
|
||||
<AdminPageSubheader
|
||||
@titleLabel="discourse_ai.ai_persona.short_title"
|
||||
@descriptionLabel="discourse_ai.ai_persona.persona_description"
|
||||
@learnMoreUrl="https://meta.discourse.org/t/ai-bot-personas/306099"
|
||||
>
|
||||
<:actions as |actions|>
|
||||
<actions.Primary
|
||||
@label="discourse_ai.ai_persona.new"
|
||||
@route="adminPlugins.show.discourse-ai-personas.new"
|
||||
class="btn btn-small btn-primary"
|
||||
>
|
||||
{{icon "plus"}}
|
||||
<span>{{I18n.t "discourse_ai.ai_persona.new"}}</span>
|
||||
</LinkTo>
|
||||
{{/unless}}
|
||||
</div>
|
||||
@icon="plus"
|
||||
class="ai-persona-list-editor__new-button"
|
||||
/>
|
||||
</:actions>
|
||||
</AdminPageSubheader>
|
||||
|
||||
<div class="ai-persona-list-editor__empty">
|
||||
<details class="details__boxed">
|
||||
<summary>{{i18n
|
||||
"discourse_ai.ai_persona.what_are_personas"
|
||||
}}</summary>
|
||||
{{this.noPersonaText}}
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<table class="content-list ai-persona-list-editor">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{i18n "discourse_ai.ai_persona.name"}}</th>
|
||||
<th>{{i18n "discourse_ai.ai_persona.enabled"}}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each @personas as |persona|}}
|
||||
<tr
|
||||
data-persona-id={{persona.id}}
|
||||
class={{concatClass
|
||||
"ai-persona-list__row"
|
||||
(if persona.priority "priority")
|
||||
}}
|
||||
>
|
||||
<td>
|
||||
<div class="ai-persona-list__name-with-description">
|
||||
<div class="ai-persona-list__name">
|
||||
<strong>
|
||||
{{persona.name}}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="ai-persona-list__description">
|
||||
{{persona.description}}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<DToggleSwitch
|
||||
@state={{persona.enabled}}
|
||||
{{on "click" (fn this.toggleEnabled persona)}}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<LinkTo
|
||||
@route="adminPlugins.show.discourse-ai-personas.show"
|
||||
@model={{persona}}
|
||||
class="btn btn-text btn-small"
|
||||
>{{i18n "discourse_ai.ai_persona.edit"}} </LinkTo>
|
||||
</td>
|
||||
{{#if @personas}}
|
||||
<table class="content-list ai-persona-list-editor d-admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{i18n "discourse_ai.ai_persona.name"}}</th>
|
||||
<th>{{i18n "discourse_ai.ai_persona.enabled"}}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each @personas as |persona|}}
|
||||
<tr
|
||||
data-persona-id={{persona.id}}
|
||||
class={{concatClass
|
||||
"ai-persona-list__row d-admin-row__content"
|
||||
(if persona.priority "priority")
|
||||
}}
|
||||
>
|
||||
<td class="d-admin-row__overview">
|
||||
<div class="ai-persona-list__name-with-description">
|
||||
<div class="ai-persona-list__name">
|
||||
<strong>
|
||||
{{persona.name}}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="ai-persona-list__description">
|
||||
{{persona.description}}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="d-admin-row__detail">
|
||||
<DToggleSwitch
|
||||
@state={{persona.enabled}}
|
||||
{{on "click" (fn this.toggleEnabled persona)}}
|
||||
/>
|
||||
</td>
|
||||
<td class="d-admin-row__controls">
|
||||
<LinkTo
|
||||
@route="adminPlugins.show.discourse-ai-personas.show"
|
||||
@model={{persona}}
|
||||
class="btn btn-text btn-small"
|
||||
>{{i18n "discourse_ai.ai_persona.edit"}} </LinkTo>
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{else}}
|
||||
<AdminConfigAreaEmptyList
|
||||
@ctaLabel="discourse_ai.ai_persona.new"
|
||||
@ctaRoute="adminPlugins.show.discourse-ai-personas.new"
|
||||
@ctaClass="ai-persona-list-editor__empty-new-button"
|
||||
@emptyLabel="discourse_ai.ai_persona.no_personas"
|
||||
/>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</section>
|
||||
</template>
|
||||
|
|
|
@ -2,9 +2,10 @@ import Component from "@glimmer/component";
|
|||
import { LinkTo } from "@ember/routing";
|
||||
import { service } from "@ember/service";
|
||||
import DBreadcrumbsItem from "discourse/components/d-breadcrumbs-item";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import I18n from "discourse-i18n";
|
||||
import AdminConfigAreaEmptyList from "admin/components/admin-config-area-empty-list";
|
||||
import AdminPageSubheader from "admin/components/admin-page-subheader";
|
||||
|
||||
export default class AiToolListEditor extends Component {
|
||||
@service adminPluginNavManager;
|
||||
|
@ -15,44 +16,64 @@ export default class AiToolListEditor extends Component {
|
|||
@label={{i18n "discourse_ai.tools.short_title"}}
|
||||
/>
|
||||
<section class="ai-tool-list-editor__current admin-detail pull-left">
|
||||
<div class="ai-tool-list-editor__header">
|
||||
<h3>{{I18n.t "discourse_ai.tools.short_title"}}</h3>
|
||||
<LinkTo
|
||||
@route="adminPlugins.show.discourse-ai-tools.new"
|
||||
class="btn btn-small btn-primary ai-tool-list-editor__new-button"
|
||||
>
|
||||
{{icon "plus"}}
|
||||
<span>{{I18n.t "discourse_ai.tools.new"}}</span>
|
||||
</LinkTo>
|
||||
</div>
|
||||
<AdminPageSubheader
|
||||
@titleLabel="discourse_ai.tools.short_title"
|
||||
@learnMoreUrl="https://meta.discourse.org/t/ai-bot-custom-tools/314103"
|
||||
@descriptionLabel="discourse_ai.tools.subheader_description"
|
||||
>
|
||||
<:actions as |actions|>
|
||||
<actions.Primary
|
||||
@label="discourse_ai.tools.new"
|
||||
@route="adminPlugins.show.discourse-ai-tools.new"
|
||||
@icon="plus"
|
||||
class="ai-tool-list-editor__new-button"
|
||||
/>
|
||||
</:actions>
|
||||
</AdminPageSubheader>
|
||||
|
||||
<table class="content-list ai-tool-list-editor">
|
||||
<tbody>
|
||||
{{#each @tools as |tool|}}
|
||||
<tr data-tool-id={{tool.id}} class="ai-tool-list__row">
|
||||
<td>
|
||||
<div class="ai-tool-list__name-with-description">
|
||||
<div class="ai-tool-list__name">
|
||||
<strong>
|
||||
{{tool.name}}
|
||||
</strong>
|
||||
{{#if @tools}}
|
||||
<table class="d-admin-table ai-tool-list-editor">
|
||||
<thead>
|
||||
<th>{{i18n "discourse_ai.tools.name"}}</th>
|
||||
<th></th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each @tools as |tool|}}
|
||||
<tr
|
||||
data-tool-id={{tool.id}}
|
||||
class="ai-tool-list__row d-admin-row__content"
|
||||
>
|
||||
<td class="d-admin-row__overview">
|
||||
<div class="ai-tool-list__name-with-description">
|
||||
<div class="ai-tool-list__name">
|
||||
<strong>
|
||||
{{tool.name}}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="ai-tool-list__description">
|
||||
{{tool.description}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="ai-tool-list__description">
|
||||
{{tool.description}}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<LinkTo
|
||||
@route="adminPlugins.show.discourse-ai-tools.show"
|
||||
@model={{tool}}
|
||||
class="btn btn-text btn-small"
|
||||
>{{I18n.t "discourse_ai.tools.edit"}}</LinkTo>
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
<td class="d-admin-row__controls">
|
||||
<LinkTo
|
||||
@route="adminPlugins.show.discourse-ai-tools.show"
|
||||
@model={{tool}}
|
||||
class="btn btn-text btn-small"
|
||||
>{{I18n.t "discourse_ai.tools.edit"}}</LinkTo>
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{else}}
|
||||
<AdminConfigAreaEmptyList
|
||||
@ctaLabel="discourse_ai.tools.new"
|
||||
@ctaRoute="adminPlugins.show.discourse-ai-tools.new"
|
||||
@ctaClass="ai-tool-list-editor__empty-new-button"
|
||||
@emptyLabel="discourse_ai.tools.no_tools"
|
||||
/>
|
||||
{{/if}}
|
||||
</section>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -67,43 +67,6 @@
|
|||
}
|
||||
|
||||
.ai-llms-list-editor__configured {
|
||||
p {
|
||||
margin: 0;
|
||||
color: var(--primary-high);
|
||||
@include breakpoint("mobile-extra-large") {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
th {
|
||||
white-space: nowrap;
|
||||
}
|
||||
tr:hover {
|
||||
background: transparent;
|
||||
}
|
||||
td {
|
||||
padding: 1em 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.column-name {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.column-edit {
|
||||
text-align: right;
|
||||
|
||||
@include breakpoint("mobile-extra-large") {
|
||||
.d-button-label {
|
||||
display: none;
|
||||
}
|
||||
.d-icon {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.d-toggle-switch {
|
||||
justify-content: center;
|
||||
}
|
||||
|
@ -129,7 +92,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
p {
|
||||
.admin-section-landing-item__description {
|
||||
color: var(--primary-high);
|
||||
margin: 0.25em 0 0.5em;
|
||||
line-height: var(--line-height-large);
|
||||
|
|
|
@ -173,6 +173,7 @@ en:
|
|||
allowed_groups: "Allowed groups"
|
||||
confirm_delete: "Are you sure you want to delete this persona?"
|
||||
new: "New persona"
|
||||
no_personas: "You have not created any personas yet"
|
||||
title: "Personas"
|
||||
short_title: "Personas"
|
||||
delete: "Delete"
|
||||
|
@ -185,17 +186,7 @@ en:
|
|||
tool_options: "Tool options"
|
||||
rag_conversation_chunks: "Search conversation chunks"
|
||||
rag_conversation_chunks_help: "The number of chunks to use for the RAG model searches. Increase to increase the amount of context the AI can use."
|
||||
what_are_personas: "What are Personas?"
|
||||
no_persona_selected: |
|
||||
Personas are a powerful feature that allows you to customize the behavior of the AI engine in your Discourse forum. They act as a 'system message' that guides the AI's responses and interactions, helping to create a more personalized and engaging user experience.
|
||||
|
||||
#### Why use personas?
|
||||
|
||||
With personas, you can tailor the AI's behavior to better fit the context and tone of your forum. Whether you want the AI to be more formal for a professional setting, more casual for a community forum, or even embody a specific character for a role-playing game, personas give you the flexibility to do so.
|
||||
|
||||
#### Group-specific access to personas
|
||||
|
||||
Moreover, you can set it up so that certain user groups have access to specific personas. This means you can have different AI behaviors for different sections of your forum, further enhancing the diversity and richness of your community's interactions.
|
||||
persona_description: "Personas are a powerful feature that allows you to customize the behavior of the AI engine in your Discourse forum. They act as a 'system message' that guides the AI's responses and interactions, helping to create a more personalized and engaging user experience."
|
||||
|
||||
rag:
|
||||
options:
|
||||
|
@ -219,8 +210,10 @@ en:
|
|||
tools:
|
||||
back: "Back"
|
||||
short_title: "Tools"
|
||||
new: "New tool"
|
||||
no_tools: "You have not created any tools yet"
|
||||
name: "Name"
|
||||
subheader_description: "Tools extend the capabilities of AI bots with user-defined JavaScript functions."
|
||||
new: "New tool"
|
||||
name_help: "The unique name of the tool as used by the language model"
|
||||
description: "Description"
|
||||
description_help: "A clear description of the tool's purpose for the language model"
|
||||
|
@ -303,6 +296,7 @@ en:
|
|||
preconfigured:
|
||||
title_no_llms: "Select a template to get started"
|
||||
title: "Unconfigured LLM templates"
|
||||
description: "LLMs (Large Language Models) are AI tools optimized for tasks like summarizing content, generating reports, automating customer interactions, and facilitating forum moderation and insights."
|
||||
fake: "Manual configuration"
|
||||
button: "Set up"
|
||||
next:
|
||||
|
|
|
@ -11,7 +11,7 @@ RSpec.describe "Admin AI persona configuration", type: :system, js: true do
|
|||
|
||||
it "allows creation of a persona" do
|
||||
visit "/admin/plugins/discourse-ai/ai-personas"
|
||||
find(".ai-persona-list-editor__header .btn-primary").click()
|
||||
find(".ai-persona-list-editor__new-button").click()
|
||||
find(".ai-persona-editor__name").set("Test Persona")
|
||||
find(".ai-persona-editor__description").fill_in(with: "I am a test persona")
|
||||
find(".ai-persona-editor__system_prompt").fill_in(with: "You are a helpful bot")
|
||||
|
|
|
@ -76,14 +76,14 @@ RSpec.describe "Managing LLM configurations", type: :system, js: true do
|
|||
it "shows the provider as CDCK in the UI" do
|
||||
visit "/admin/plugins/discourse-ai/ai-llms"
|
||||
expect(page).to have_css(
|
||||
"[data-llm-id='cdck-hosted'] .column-provider",
|
||||
"[data-llm-id='cdck-hosted']",
|
||||
text: I18n.t("js.discourse_ai.llms.providers.CDCK"),
|
||||
)
|
||||
end
|
||||
|
||||
it "shows an info alert to the user about the seeded LLM" do
|
||||
visit "/admin/plugins/discourse-ai/ai-llms"
|
||||
find("[data-llm-id='#{llm_model.name}'] .column-edit .btn").click()
|
||||
find("[data-llm-id='#{llm_model.name}'] .ai-llm-list__edit-button").click()
|
||||
expect(page).to have_css(
|
||||
".alert.alert-info",
|
||||
text: I18n.t("js.discourse_ai.llms.seeded_warning"),
|
||||
|
@ -92,7 +92,7 @@ RSpec.describe "Managing LLM configurations", type: :system, js: true do
|
|||
|
||||
it "limits and shows disabled inputs for the seeded LLM" do
|
||||
visit "/admin/plugins/discourse-ai/ai-llms"
|
||||
find("[data-llm-id='cdck-hosted'] .column-edit .btn").click()
|
||||
find("[data-llm-id='cdck-hosted'] .ai-llm-list__edit-button").click()
|
||||
expect(page).to have_css(".ai-llm-editor__display-name[disabled]")
|
||||
expect(page).to have_css(".ai-llm-editor__name[disabled]")
|
||||
expect(page).to have_css(".ai-llm-editor__provider.is-disabled")
|
||||
|
|
Loading…
Reference in New Issue