Skip to content

Commit 5e5f048

Browse files
committed
fix(2447): merge duplicate Save Changes buttons into one
The develop merge introduced a second "Save Changes" button (diskette "Saved" icon) for persisting Profile Links via PATCH, alongside the existing submit button that flips to "Changes Saved" text. Fold the links save into the existing button's submit flow instead of showing two buttons: link validation/save now runs before the section form submits, blocking it on failure.
1 parent d347537 commit 5e5f048

1 file changed

Lines changed: 24 additions & 21 deletions

File tree

templates/v3/user_profile_edit.html

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -158,18 +158,13 @@
158158
</div>
159159
</div>
160160
<hr class="card__hr" aria-hidden="true" />
161-
{% comment %} Persists the Profile links to the current user via PATCH /api/v1/users/me/. {% endcomment %}
161+
{% comment %} Save changes: links are validated and persisted via PATCH /api/v1/users/me/
162+
(see saveLinks in profileForm) before the section form submits, so one click covers both;
163+
a link validation/save failure blocks the section submit and the button stays "Save changes". {% endcomment %}
162164
<div class="card__cta_section">
163-
{% include 'v3/includes/_button.html' with style='primary' type='submit' name='v3_update_profile' label='Save changes' label_x_text="saved ? 'Changes Saved' : 'Save changes'" alpine_disabled='saved' only %}
165+
{% include 'v3/includes/_button.html' with style='primary' type='submit' name='v3_update_profile' label='Save changes' label_x_text="saved ? 'Changes Saved' : 'Save changes'" alpine_disabled='saved || saving' only %}
164166
{% include 'v3/includes/_button.html' with style='secondary' type='reset' label='Cancel' only %}
165-
{% include 'v3/includes/_button.html' with style='primary' type='button' label='Save Changes' alpine_disabled="saving" alpine_click="save()" only %}
166-
<span class="user-profile__save-indicator" role="status" aria-live="polite">
167-
<span class="user-profile__save-state" x-show="saveStatus === 'saved'" x-cloak>
168-
<span class="user-profile__save-text">Saved</span>
169-
{% include "includes/icon.html" with icon_name="saved" icon_class="create-post-page__save-icon" icon_size=16 %}
170-
</span>
171-
<span class="field__error" x-show="saveStatus === 'error'" x-text="saveError" x-cloak></span>
172-
</span>
167+
<span class="field__error" x-show="saveError" x-text="saveError" x-cloak></span>
173168
</div>
174169
</form>
175170
</div>
@@ -328,6 +323,13 @@
328323
return
329324
}
330325
event.preventDefault()
326+
// Only the Profile form mixes in profileForm(); other sections
327+
// (Update Details, Email Preferences) have no saveLinks and skip
328+
// straight to their own submit.
329+
if (typeof this.saveLinks === 'function') {
330+
const linksSaved = await this.saveLinks()
331+
if (!linksSaved) return
332+
}
331333
const form = event.target
332334
const submitter = event.submitter
333335
let succeeded = false
@@ -491,16 +493,14 @@
491493
const SLACK_MEMBER_ID_PATTERN = /^[A-Z0-9]{9,11}$/i;
492494

493495
return {
494-
saving: false,
495-
saveStatus: '', // '' | 'saved' | 'error' (button shows "saving" via :disabled)
496+
saving: false, // true while the links PATCH is in flight; disables the Save changes button
496497
saveError: '',
497498
errors: { github: '', website: '', email: '', slack: '' }, // inline per-link errors, keyed by type
498499

499500
// Clear a link's inline error as soon as the user edits that field.
500501
clearLinkError(e) {
501502
const type = e.target.dataset.linkType;
502503
if (type) this.errors[type] = '';
503-
this.saveStatus = '';
504504
this.saveError = '';
505505
},
506506

@@ -568,8 +568,12 @@
568568
return links;
569569
},
570570

571-
async save() {
572-
if (this.saving) return;
571+
// Validates and persists the profile links via PATCH before the section
572+
// form submit proceeds (see handleSubmit in createSectionForm). Returns
573+
// true when it's safe to continue with the section submit, false to
574+
// abort it (validation or save failure) and leave the button as-is.
575+
async saveLinks() {
576+
if (this.saving) return false;
573577

574578
// Validate all links up front; block the save if any fail.
575579
let hasError = false;
@@ -578,10 +582,9 @@
578582
this.errors[type] = msg;
579583
if (msg) hasError = true;
580584
});
581-
if (hasError) return;
585+
if (hasError) return false;
582586

583587
this.saving = true;
584-
this.saveStatus = '';
585588
this.saveError = '';
586589
try {
587590
const res = await fetch('/api/v1/users/me/', {
@@ -606,17 +609,17 @@
606609
Object.entries(linkErrors).forEach(([type, msg]) => {
607610
this.errors[type] = Array.isArray(msg) ? msg[0] : msg;
608611
});
609-
return;
612+
return false;
610613
}
611614

612615
const fieldErrors = Object.values(data).flat().filter(Boolean);
613616
throw new Error(fieldErrors.length ? fieldErrors.join(' ') : `Save failed (${res.status})`);
614617
}
615-
this.saveStatus = 'saved';
618+
return true;
616619
} catch (err) {
617-
console.error('Profile save failed:', err);
618-
this.saveStatus = 'error';
620+
console.error('Profile links save failed:', err);
619621
this.saveError = err.message || 'Could not save. Please try again.';
622+
return false;
620623
} finally {
621624
this.saving = false;
622625
}

0 commit comments

Comments
 (0)