fix(admin-categories): key create-draft localStorage recovery on a stable id, not the ephemeral generated one
Bug: startCreate() generated a fresh id via category-${Date.now()}
every call and wrote/read the autosave draft under
admin-category-draft:<that id>. Since the id changes every time
startCreate() runs, a draft saved during one "create category" visit
can never be found by a later visit (even seconds later, same tab) -
draft recovery for new (unsaved) categories was completely dead, and
every abandoned attempt left an orphaned, never-cleaned localStorage
entry.
Fix: create-mode drafts now persist under a fixed key
(admin-category-draft:new) tracked via a new draftStorageKey field,
independent of the draft's own id. Edit-mode drafts are unaffected -
they already keyed on the real, stable category id.
Verified live via window.ng.getComponent() on
/ru/backoffice/categories/create (devBypassAdmin=true):
- Before fix: updateDraft({title}) -> localStorage key
admin-category-draft:category-<ts1>; calling startCreate() again
(simulating navigate-away/back) generated category-<ts2> and never
recovered - draft.title reset to '', dirty=false, old key orphaned.
- After fix: same sequence recovers title/dirty correctly under
admin-category-draft:new; saveDraft() clears that key as expected.
This commit is contained in:
@@ -6,6 +6,7 @@ import { AdminCategoriesLocalGateway } from '../services/admin-categories-local.
|
||||
import { LocalStorageService } from '../../../../core/storage/local-storage.service';
|
||||
|
||||
const DRAFT_KEY_PREFIX = 'admin-category-draft:';
|
||||
const NEW_CATEGORY_DRAFT_KEY = `${DRAFT_KEY_PREFIX}new`;
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class AdminCategoriesFacade {
|
||||
@@ -21,6 +22,7 @@ export class AdminCategoriesFacade {
|
||||
readonly dirty = signal(false);
|
||||
readonly slugTaken = signal(false);
|
||||
private savedSnapshot: string | null = null;
|
||||
private draftStorageKey: string | null = null;
|
||||
|
||||
readonly rootCategories = computed(() => this.categories().filter(category => !category.parentId));
|
||||
readonly childrenByParent = computed(() => {
|
||||
@@ -69,22 +71,23 @@ export class AdminCategoriesFacade {
|
||||
|
||||
startCreate(): void {
|
||||
this.editorMode.set('create');
|
||||
const empty = this.formFactory.createEmpty();
|
||||
const recovered = this.localStorage.getJSON<AdminCategory>(`${DRAFT_KEY_PREFIX}${empty.id}`);
|
||||
this.draft.set(recovered ?? empty);
|
||||
this.draftStorageKey = NEW_CATEGORY_DRAFT_KEY;
|
||||
const recovered = this.localStorage.getJSON<AdminCategory>(this.draftStorageKey);
|
||||
this.draft.set(recovered ?? this.formFactory.createEmpty());
|
||||
this.savedSnapshot = null;
|
||||
this.dirty.set(!!recovered);
|
||||
}
|
||||
|
||||
loadForEdit(id: string): void {
|
||||
this.editorMode.set('edit');
|
||||
this.draftStorageKey = `${DRAFT_KEY_PREFIX}${id}`;
|
||||
this.gateway.loadCategory(id).pipe(take(1)).subscribe({
|
||||
next: category => {
|
||||
if (!category) {
|
||||
this.draft.set(null);
|
||||
return;
|
||||
}
|
||||
const recovered = this.localStorage.getJSON<AdminCategory>(`${DRAFT_KEY_PREFIX}${id}`);
|
||||
const recovered = this.localStorage.getJSON<AdminCategory>(this.draftStorageKey!);
|
||||
this.draft.set(recovered ?? { ...category });
|
||||
this.savedSnapshot = JSON.stringify(category);
|
||||
this.dirty.set(!!recovered && JSON.stringify(recovered) !== this.savedSnapshot);
|
||||
@@ -96,7 +99,9 @@ export class AdminCategoriesFacade {
|
||||
this.draft.update(current => {
|
||||
if (!current) return current;
|
||||
const updated = { ...current, ...patch, updatedAt: new Date().toISOString() };
|
||||
this.localStorage.setJSON(`${DRAFT_KEY_PREFIX}${updated.id}`, updated);
|
||||
if (this.draftStorageKey) {
|
||||
this.localStorage.setJSON(this.draftStorageKey, updated);
|
||||
}
|
||||
this.dirty.set(this.savedSnapshot !== JSON.stringify(updated));
|
||||
return updated;
|
||||
});
|
||||
@@ -124,7 +129,10 @@ export class AdminCategoriesFacade {
|
||||
|
||||
request.pipe(take(1)).subscribe({
|
||||
next: saved => {
|
||||
this.localStorage.removeItem(`${DRAFT_KEY_PREFIX}${saved.id}`);
|
||||
if (this.draftStorageKey) {
|
||||
this.localStorage.removeItem(this.draftStorageKey);
|
||||
}
|
||||
this.draftStorageKey = `${DRAFT_KEY_PREFIX}${saved.id}`;
|
||||
this.savedSnapshot = JSON.stringify(saved);
|
||||
this.dirty.set(false);
|
||||
this.loadList();
|
||||
@@ -133,9 +141,8 @@ export class AdminCategoriesFacade {
|
||||
}
|
||||
|
||||
discardDraftRecovery(): void {
|
||||
const draft = this.draft();
|
||||
if (!draft) return;
|
||||
this.localStorage.removeItem(`${DRAFT_KEY_PREFIX}${draft.id}`);
|
||||
if (!this.draft() || !this.draftStorageKey) return;
|
||||
this.localStorage.removeItem(this.draftStorageKey);
|
||||
}
|
||||
|
||||
canDelete(id: string): boolean {
|
||||
|
||||
Reference in New Issue
Block a user