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

Commit ee7e58a

Browse files
committed
FIX: Persona editor keeps dirty data after persisting a single field
1 parent a6b0827 commit ee7e58a

File tree

2 files changed

+57
-17
lines changed

2 files changed

+57
-17
lines changed

assets/javascripts/discourse/components/ai-persona-editor.gjs

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,25 @@ export default class PersonaEditor extends Component {
3232
@tracked isSaving = false;
3333
@tracked availableForcedTools = [];
3434

35+
dirtyFormData = null;
36+
3537
@cached
3638
get formData() {
37-
const data = this.args.model.toPOJO();
39+
// This is to recover a dirty state after persisting a single form field.
40+
// It's meant to be consumed only once.
41+
if (this.dirtyFormData) {
42+
const data = this.dirtyFormData;
43+
this.dirtyFormData = null;
44+
return data;
45+
} else {
46+
const data = this.args.model.toPOJO();
47+
48+
if (data.tools) {
49+
data.toolOptions = this.mapToolOptions(data.toolOptions, data.tools);
50+
}
3851

39-
if (data.tools) {
40-
data.toolOptions = this.mapToolOptions(data.toolOptions, data.tools);
52+
return data;
4153
}
42-
43-
return data;
4454
}
4555

4656
get chatPluginEnabled() {
@@ -144,15 +154,15 @@ export default class PersonaEditor extends Component {
144154
}
145155

146156
@action
147-
async toggleEnabled(value, { set }) {
157+
async toggleEnabled(dirtyData, value, { set }) {
148158
set("enabled", value);
149-
await this.persistField("enabled", value);
159+
await this.persistField(dirtyData, "enabled", value);
150160
}
151161

152162
@action
153-
async togglePriority(value, { set }) {
163+
async togglePriority(dirtyData, value, { set }) {
154164
set("priority", value);
155-
await this.persistField("priority", value, true);
165+
await this.persistField(dirtyData, "priority", value, true);
156166
}
157167

158168
@action
@@ -172,15 +182,15 @@ export default class PersonaEditor extends Component {
172182
}
173183

174184
@action
175-
async removeUpload(form, currentUploads, upload) {
185+
async removeUpload(form, dirtyData, currentUploads, upload) {
176186
const updatedUploads = currentUploads.filter(
177187
(file) => file.id !== upload.id
178188
);
179189

180190
form.set("rag_uploads", updatedUploads);
181191

182192
if (!this.args.model.isNew) {
183-
await this.persistField("rag_uploads", updatedUploads);
193+
await this.persistField(dirtyData, "rag_uploads", updatedUploads);
184194
}
185195
}
186196

@@ -232,14 +242,16 @@ export default class PersonaEditor extends Component {
232242
return updatedOptions;
233243
}
234244

235-
async persistField(field, newValue, sortPersonas) {
236-
this.args.model.set(field, newValue);
237-
245+
async persistField(dirtyData, field, newValue, sortPersonas) {
238246
if (!this.args.model.isNew) {
247+
const updatedDirtyData = Object.assign({}, dirtyData);
248+
updatedDirtyData[field] = newValue;
249+
239250
try {
240251
const args = {};
241252
args[field] = newValue;
242253

254+
this.dirtyFormData = updatedDirtyData;
243255
await this.args.model.update(args);
244256
if (sortPersonas) {
245257
this.#sortPersonas();
@@ -274,7 +286,7 @@ export default class PersonaEditor extends Component {
274286
<form.Field
275287
@name="enabled"
276288
@title={{i18n "discourse_ai.ai_persona.enabled"}}
277-
@onSet={{this.toggleEnabled}}
289+
@onSet={{fn this.toggleEnabled data}}
278290
as |field|
279291
>
280292
<field.Toggle />
@@ -283,7 +295,7 @@ export default class PersonaEditor extends Component {
283295
<form.Field
284296
@name="priority"
285297
@title={{i18n "discourse_ai.ai_persona.priority"}}
286-
@onSet={{this.togglePriority}}
298+
@onSet={{fn this.togglePriority data}}
287299
@tooltip={{i18n "discourse_ai.ai_persona.priority_help"}}
288300
as |field|
289301
>
@@ -499,7 +511,7 @@ export default class PersonaEditor extends Component {
499511
@target={{data}}
500512
@targetName="AiPersona"
501513
@updateUploads={{fn this.updateUploads form}}
502-
@onRemove={{fn this.removeUpload form field.value}}
514+
@onRemove={{fn this.removeUpload form data field.value}}
503515
@allowImages={{@personas.resultSetMeta.settings.rag_images_enabled}}
504516
/>
505517
</field.Custom>

spec/system/admin_ai_persona_spec.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,34 @@
7171
expect(persona.name).not_to eq("Test Persona 1")
7272
end
7373

74+
it "enabling a persona doesn't reset other fields" do
75+
persona = Fabricate(:ai_persona, enabled: false)
76+
updated_name = "Update persona 1"
77+
78+
visit "/admin/plugins/discourse-ai/ai-personas/#{persona.id}/edit"
79+
80+
form.field("name").fill_in(updated_name)
81+
form.field("enabled").toggle
82+
83+
try_until_success { expect(persona.reload.enabled).to eq(true) }
84+
85+
expect(form.field("name").value).to eq(updated_name)
86+
end
87+
88+
it "toggling a persona's priority doesn't reset other fields" do
89+
persona = Fabricate(:ai_persona, priority: false)
90+
updated_name = "Update persona 1"
91+
92+
visit "/admin/plugins/discourse-ai/ai-personas/#{persona.id}/edit"
93+
94+
form.field("name").fill_in(updated_name)
95+
form.field("priority").toggle
96+
97+
try_until_success { expect(persona.reload.priority).to eq(true) }
98+
99+
expect(form.field("name").value).to eq(updated_name)
100+
end
101+
74102
it "can navigate the AI plugin with breadcrumbs" do
75103
visit "/admin/plugins/discourse-ai/ai-personas"
76104
expect(page).to have_css(".d-breadcrumbs")

0 commit comments

Comments
 (0)