UX: Show error and ability to try again when no suggestions
## 🔍 Overview
When the title suggestions return no suggestions, there is no indication in the UI. In the tag suggester we show a toast when there aren't any suggestions but the request was a success. In this update we make a similar UI indication with a toast for both category and title suggestions. Additionally, for all suggestions we add a "Try again" button to the toast so that suggestions can be generated again if the results yield nothing the first time.
This commit is contained in:
parent
a907bc891a
commit
d777b8a75a
|
@ -11,7 +11,10 @@ import { ajax } from "discourse/lib/ajax";
|
|||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import DMenu from "float-kit/components/d-menu";
|
||||
import { MIN_CHARACTER_COUNT } from "../../lib/ai-helper-suggestions";
|
||||
import {
|
||||
MIN_CHARACTER_COUNT,
|
||||
showSuggestionsError,
|
||||
} from "../../lib/ai-helper-suggestions";
|
||||
|
||||
export default class AiCategorySuggester extends Component {
|
||||
@service siteSettings;
|
||||
|
@ -40,9 +43,20 @@ export default class AiCategorySuggester extends Component {
|
|||
return this.siteSettings.ai_embeddings_enabled && showTrigger;
|
||||
}
|
||||
|
||||
get showDropdown() {
|
||||
if (this.suggestions?.length <= 0) {
|
||||
this.dMenu.close();
|
||||
}
|
||||
return !this.loading && this.suggestions?.length > 0;
|
||||
}
|
||||
|
||||
@action
|
||||
async loadSuggestions() {
|
||||
if (this.suggestions && !this.dMenu.expanded) {
|
||||
if (
|
||||
this.suggestions &&
|
||||
this.suggestions?.length > 0 &&
|
||||
!this.dMenu.expanded
|
||||
) {
|
||||
return this.suggestions;
|
||||
}
|
||||
|
||||
|
@ -65,7 +79,13 @@ export default class AiCategorySuggester extends Component {
|
|||
data,
|
||||
}
|
||||
);
|
||||
|
||||
this.suggestions = assistant;
|
||||
|
||||
if (this.suggestions?.length <= 0) {
|
||||
showSuggestionsError(this, this.loadSuggestions.bind(this));
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
popupAjaxError(error);
|
||||
} finally {
|
||||
|
@ -100,8 +120,14 @@ export default class AiCategorySuggester extends Component {
|
|||
|
||||
@action
|
||||
onClose() {
|
||||
if (this.suggestions?.length > 0) {
|
||||
// If all suggestions have been used,
|
||||
// re-triggering when no suggestions present
|
||||
// will cause computation issues with
|
||||
// setting the icon, so we prevent it
|
||||
this.triggerIcon = "discourse-sparkles";
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if this.showSuggestionButton}}
|
||||
|
@ -121,7 +147,7 @@ export default class AiCategorySuggester extends Component {
|
|||
{{on "click" this.loadSuggestions}}
|
||||
>
|
||||
<:content>
|
||||
{{#unless this.loading}}
|
||||
{{#if this.showDropdown}}
|
||||
<DropdownMenu as |dropdown|>
|
||||
{{#each this.suggestions as |suggestion index|}}
|
||||
<dropdown.item>
|
||||
|
@ -141,7 +167,7 @@ export default class AiCategorySuggester extends Component {
|
|||
</dropdown.item>
|
||||
{{/each}}
|
||||
</DropdownMenu>
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
</:content>
|
||||
</DMenu>
|
||||
{{/if}}
|
||||
|
|
|
@ -11,7 +11,10 @@ import { ajax } from "discourse/lib/ajax";
|
|||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import DMenu from "float-kit/components/d-menu";
|
||||
import { MIN_CHARACTER_COUNT } from "../../lib/ai-helper-suggestions";
|
||||
import {
|
||||
MIN_CHARACTER_COUNT,
|
||||
showSuggestionsError,
|
||||
} from "../../lib/ai-helper-suggestions";
|
||||
|
||||
export default class AiTagSuggester extends Component {
|
||||
@service siteSettings;
|
||||
|
@ -79,6 +82,7 @@ export default class AiTagSuggester extends Component {
|
|||
method: "POST",
|
||||
data,
|
||||
});
|
||||
|
||||
this.suggestions = assistant;
|
||||
|
||||
const model = this.args.composer
|
||||
|
@ -92,15 +96,7 @@ export default class AiTagSuggester extends Component {
|
|||
}
|
||||
|
||||
if (this.suggestions?.length <= 0) {
|
||||
this.toasts.error({
|
||||
class: "ai-suggestion-error",
|
||||
duration: 3000,
|
||||
data: {
|
||||
message: i18n(
|
||||
"discourse_ai.ai_helper.suggest_errors.no_suggestions"
|
||||
),
|
||||
},
|
||||
});
|
||||
showSuggestionsError(this, this.loadSuggestions.bind(this));
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
|
@ -9,7 +9,10 @@ import { ajax } from "discourse/lib/ajax";
|
|||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import DMenu from "float-kit/components/d-menu";
|
||||
import { MIN_CHARACTER_COUNT } from "../../lib/ai-helper-suggestions";
|
||||
import {
|
||||
MIN_CHARACTER_COUNT,
|
||||
showSuggestionsError,
|
||||
} from "../../lib/ai-helper-suggestions";
|
||||
|
||||
export default class AiTitleSuggester extends Component {
|
||||
@tracked loading = false;
|
||||
|
@ -46,9 +49,20 @@ export default class AiTitleSuggester extends Component {
|
|||
return showTrigger;
|
||||
}
|
||||
|
||||
get showDropdown() {
|
||||
if (this.suggestions?.length <= 0) {
|
||||
this.dMenu.close();
|
||||
}
|
||||
return !this.loading && this.suggestions?.length > 0;
|
||||
}
|
||||
|
||||
@action
|
||||
async loadSuggestions() {
|
||||
if (this.suggestions && !this.dMenu.expanded) {
|
||||
if (
|
||||
this.suggestions &&
|
||||
this.suggestions?.length > 0 &&
|
||||
!this.dMenu.expanded
|
||||
) {
|
||||
return this.suggestions;
|
||||
}
|
||||
|
||||
|
@ -70,7 +84,13 @@ export default class AiTitleSuggester extends Component {
|
|||
data,
|
||||
}
|
||||
);
|
||||
|
||||
this.suggestions = suggestions;
|
||||
|
||||
if (this.suggestions?.length <= 0) {
|
||||
showSuggestionsError(this, this.loadSuggestions.bind(this));
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
popupAjaxError(error);
|
||||
} finally {
|
||||
|
@ -99,8 +119,14 @@ export default class AiTitleSuggester extends Component {
|
|||
|
||||
@action
|
||||
onClose() {
|
||||
if (this.suggestions?.length > 0) {
|
||||
// If all suggestions have been used,
|
||||
// re-triggering when no suggestions present
|
||||
// will cause computation issues with
|
||||
// setting the icon, so we prevent it
|
||||
this.triggerIcon = "discourse-sparkles";
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
{{#if this.showSuggestionButton}}
|
||||
|
@ -120,7 +146,7 @@ export default class AiTitleSuggester extends Component {
|
|||
{{on "click" this.loadSuggestions}}
|
||||
>
|
||||
<:content>
|
||||
{{#unless this.loading}}
|
||||
{{#if this.showDropdown}}
|
||||
<DropdownMenu as |dropdown|>
|
||||
{{#each this.suggestions as |suggestion index|}}
|
||||
<dropdown.item>
|
||||
|
@ -135,7 +161,7 @@ export default class AiTitleSuggester extends Component {
|
|||
</dropdown.item>
|
||||
{{/each}}
|
||||
</DropdownMenu>
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
</:content>
|
||||
</DMenu>
|
||||
{{/if}}
|
||||
|
|
|
@ -1 +1,34 @@
|
|||
import { getOwner } from "@ember/application";
|
||||
import { later } from "@ember/runloop";
|
||||
import { i18n } from "discourse-i18n";
|
||||
|
||||
export const MIN_CHARACTER_COUNT = 40;
|
||||
|
||||
export function showSuggestionsError(context, reloadFn) {
|
||||
const toasts = getOwner(context).lookup("service:toasts");
|
||||
|
||||
toasts.error({
|
||||
class: "ai-suggestion-error",
|
||||
duration: "long",
|
||||
showProgressBar: true,
|
||||
data: {
|
||||
message: i18n("discourse_ai.ai_helper.suggest_errors.no_suggestions"),
|
||||
actions: [
|
||||
{
|
||||
label: i18n("discourse_ai.ai_helper.context_menu.regen"),
|
||||
icon: "rotate",
|
||||
class: "btn btn-small",
|
||||
action: async (toast) => {
|
||||
toast.close();
|
||||
|
||||
await reloadFn();
|
||||
|
||||
if (context.dMenu?.show && context.suggestions?.length > 0) {
|
||||
later(() => context.dMenu.show(), 50);
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -599,7 +599,7 @@ en:
|
|||
trigger: "Ask AI"
|
||||
loading: "AI is generating"
|
||||
cancel: "Cancel"
|
||||
regen: "Try Again"
|
||||
regen: "Try again"
|
||||
confirm: "Confirm"
|
||||
discard: "Discard"
|
||||
changes: "Suggested edits"
|
||||
|
|
Loading…
Reference in New Issue