From d852b0325f0cd3104b237492218714154f802b60 Mon Sep 17 00:00:00 2001 From: Dave Roberts Date: Tue, 26 Nov 2024 16:31:33 +0000 Subject: [PATCH] Styling and error handling --- .../components/button/lib/common.test.ts | 11 +---- src/frontend/components/button/lib/common.ts | 10 +--- .../form-group/autosave/_autosave.scss | 20 ++++++++ .../form-group/autosave/lib/autosave.test.ts | 15 ------ .../form-group/autosave/lib/autosaveBase.ts | 10 +--- .../form-group/autosave/lib/component.js | 4 +- .../form-group/autosave/lib/modal.js | 46 +++++++++++++------ .../modal/modals/curval/lib/component.js | 2 +- src/frontend/js/lib/set-field-value.test.ts | 4 +- .../encryptedStorage/lib/encryptedStorage.ts | 2 +- .../lib/util/gadsStorage/lib/gadsStorage.ts | 15 ++++-- 11 files changed, 72 insertions(+), 67 deletions(-) diff --git a/src/frontend/components/button/lib/common.test.ts b/src/frontend/components/button/lib/common.test.ts index d5898c790..027baaee3 100644 --- a/src/frontend/components/button/lib/common.test.ts +++ b/src/frontend/components/button/lib/common.test.ts @@ -1,11 +1,7 @@ import "../../../testing/globals.definitions"; -import {layoutId, recordId, table_key} from "./common"; +import {layoutId, recordId} from "./common"; describe("Common button tests",()=>{ - it("should populate table_key",()=>{ - expect(table_key()).toBe("linkspace-record-change-undefined-0"); // Undefiined because $('body').data('layout-identifier') is not defined - }); - it("should have a layoutId", ()=>{ $('body').data('layout-identifier', 'layoutId'); expect(layoutId()).toBe('layoutId'); @@ -15,9 +11,4 @@ describe("Common button tests",()=>{ expect(isNaN(parseInt(location.pathname.split('/').pop() ?? ""))).toBe(true); expect(recordId()).toBe(0); }); - - it("should populate table_key fully",()=>{ - $('body').data('layout-identifier', 'layoutId'); - expect(table_key()).toBe("linkspace-record-change-layoutId-0"); - }); }); \ No newline at end of file diff --git a/src/frontend/components/button/lib/common.ts b/src/frontend/components/button/lib/common.ts index e003e8fe9..a0b4967b7 100644 --- a/src/frontend/components/button/lib/common.ts +++ b/src/frontend/components/button/lib/common.ts @@ -4,13 +4,9 @@ export async function clearSavedFormValues($form: JQuery) { if (!$form || $form.length === 0) return; const layout = layoutId(); const record = recordId(); - const ls = storage(); - let item = await ls.getItem(table_key()); - - if (item) await ls.removeItem(`linkspace-record-change-${layout}-${record}`); await Promise.all($form.find(".linkspace-field").map(async (_, el) => { const field_id = $(el).data("column-id"); - item = await ls.getItem(`linkspace-column-${field_id}-${layout}-${record}`); + const item = await gadsStorage.getItem(`linkspace-column-${field_id}-${layout}-${record}`); if (item) gadsStorage.removeItem(`linkspace-column-${field_id}-${layout}-${record}`); })); } @@ -23,10 +19,6 @@ export function recordId() { return isNaN(parseInt(location.pathname.split('/').pop())) ? 0 : parseInt(location.pathname.split('/').pop()); } -export function table_key() { - return `linkspace-record-change-${layoutId()}-${recordId()}`; -} - export function storage() { return location.hostname === 'localhost' || window.test ? localStorage : gadsStorage; } \ No newline at end of file diff --git a/src/frontend/components/form-group/autosave/_autosave.scss b/src/frontend/components/form-group/autosave/_autosave.scss index a4431d2ee..9b0c4f8bd 100644 --- a/src/frontend/components/form-group/autosave/_autosave.scss +++ b/src/frontend/components/form-group/autosave/_autosave.scss @@ -3,4 +3,24 @@ background-color: $field-highlight; border-radius: $input-border-radius; } +} + +li.li-success { + list-style: none; + &::before { + content: '\2713'; + color: green; + font-size: 1.5em; + margin-right: 0.5em; + } +} + +li.li-error { + list-style: none; + &::before { + content: '\2717'; + color: red; + font-size: 1.5em; + margin-right: 0.5em; + } } \ No newline at end of file diff --git a/src/frontend/components/form-group/autosave/lib/autosave.test.ts b/src/frontend/components/form-group/autosave/lib/autosave.test.ts index 1fe28217e..869ac0f39 100644 --- a/src/frontend/components/form-group/autosave/lib/autosave.test.ts +++ b/src/frontend/components/form-group/autosave/lib/autosave.test.ts @@ -21,11 +21,6 @@ describe('AutosaveBase', () => { document.body.innerHTML = ''; }); - it('should return test', () => { - const autosave = new TestAutosave(document.getElementById('test')!); - expect(autosave.test).toBe(true); - }); - it('should return layoutId', () => { const autosave = new TestAutosave(document.getElementById('test')!); expect(autosave.layoutId).toBe(1); @@ -35,14 +30,4 @@ describe('AutosaveBase', () => { const autosave = new TestAutosave(document.getElementById('test')!); expect(autosave.recordId).toBe(0); }); - - it('should return table_key', () => { - const autosave = new TestAutosave(document.getElementById('test')!); - expect(autosave.table_key).toBe('linkspace-record-change-1-0'); - }); - - it('should return storage', () => { - const autosave = new TestAutosave(document.getElementById('test')!); - expect(autosave.storage).toBe(localStorage); - }); }); diff --git a/src/frontend/components/form-group/autosave/lib/autosaveBase.ts b/src/frontend/components/form-group/autosave/lib/autosaveBase.ts index 5e66b035d..eb33b8dba 100644 --- a/src/frontend/components/form-group/autosave/lib/autosaveBase.ts +++ b/src/frontend/components/form-group/autosave/lib/autosaveBase.ts @@ -2,10 +2,6 @@ import { Component } from "component"; import gadsStorage from "util/gadsStorage"; export default abstract class AutosaveBase extends Component { - get test() { - return location.hostname === 'localhost' || window.test; - } - constructor(element: HTMLElement) { super(element); this.initAutosave(); @@ -24,12 +20,8 @@ export default abstract class AutosaveBase extends Component { return isNaN(parseInt(id)) ? 0 : id; } - get table_key() { - return `linkspace-record-change-${this.layoutId}-${this.recordId}`; - } - get storage() { - return this.test ? localStorage: gadsStorage; + return gadsStorage; } columnKey($field:JQuery) { diff --git a/src/frontend/components/form-group/autosave/lib/component.js b/src/frontend/components/form-group/autosave/lib/component.js index 42e66e7e3..c7d628bfd 100644 --- a/src/frontend/components/form-group/autosave/lib/component.js +++ b/src/frontend/components/form-group/autosave/lib/component.js @@ -40,7 +40,7 @@ class AutosaveComponent extends AutosaveBase { values = values.map((item) => Number.isInteger(item) ? item : indexed[item]); } await self.storage.setItem(column_key, JSON.stringify(values)); - await self.storage.setItem(self.table_key, true); + // await self.storage.setItem(self.table_key, true); } else { // Delete any values now deleted let existing = await self.storage.getItem(column_key) ? JSON.parse(await self.storage.getItem(column_key)) : []; @@ -50,7 +50,7 @@ class AutosaveComponent extends AutosaveBase { // deleted, this will need setting if the change was triggered as a // result of a modal submit for a curval add - everything else will // have already been saved) - await self.storage.setItem(self.table_key, true); + // await self.storage.setItem(self.table_key, true); } }); diff --git a/src/frontend/components/form-group/autosave/lib/modal.js b/src/frontend/components/form-group/autosave/lib/modal.js index c3995f0b8..b96f1015f 100644 --- a/src/frontend/components/form-group/autosave/lib/modal.js +++ b/src/frontend/components/form-group/autosave/lib/modal.js @@ -5,36 +5,52 @@ class AutosaveModal extends AutosaveBase { async initAutosave() { const $modal = $(this.element); const self = this; + const $form = $('.form-edit'); - $modal.find('.btn-js-restore-values').on('click', function (e) { + $modal.find('.btn-js-restore-values').on('click', async function (e) { e.preventDefault(); - const $form = $('.form-edit'); let $list = $(""); const $body = $modal.find(".modal-body"); $body.html("

Restoring values...

").append($list); - $form.find('.linkspace-field').each(async function(){ + await Promise.all($form.find('.linkspace-field').map(async function () { const $field = $(this); - const json = await self.storage.getItem(self.columnKey($field)); - if (json) { - const values = JSON.parse(json); + await self.storage.getItem(self.columnKey($field)).then(json => { + let values = json ? JSON.parse(json) : undefined; + return values && Array.isArray(values) && values.length ? values : undefined; + }).then(values => { const $editButton = $field.closest('.card--topic').find('.btn-js-edit'); - if($editButton && $editButton.length) $editButton.trigger('click'); - if (Array.isArray(values)) + if ($editButton && $editButton.length) $editButton.trigger('click'); + if (Array.isArray(values) && values.length) { setFieldValues($field, values); $field.addClass("field--changed"); const name = $field.data("name"); - let $li = $(`
  • Restored ${name}
  • `); + let $li = $(`
  • Restored ${name}
  • `); $list.append($li); - } + } + }).catch(e => { + const name = $field.data("name"); + let $li = $(`
  • Failed to restore ${name}
  • `); + console.error(e); + $list.append($li); + }); + })).then(() => { + $body.append("

    All values restored.

    "); + }).catch(e => { + $body.append(`

    Critical error restoring values

    ${e}

    `); + }).finally(() => { + $modal.find(".modal-footer").find("button:not(.btn-cancel)").hide(); + $modal.find(".modal-footer").find(".btn-cancel").text("Close"); }); - $body.append("

    All values restored.

    "); - $modal.find(".modal-footer").find("button:not(.btn-cancel)").hide(); - $modal.find(".modal-footer").find(".btn-cancel").text("Close"); }); - const item = await self.storage.getItem(this.table_key); - + const item = (await Promise.all($form.find('.linkspace-field').map(async (_,field)=> { + if(await self.storage.getItem(self.columnKey($(field)))) { + return true; + } + return false; + }))).includes(true) + if (item){ $modal.modal('show'); $modal.find('.btn-js-delete-values').attr('disabled', 'disabled').hide(); diff --git a/src/frontend/components/modal/modals/curval/lib/component.js b/src/frontend/components/modal/modals/curval/lib/component.js index 76ec9ef81..9740d7da4 100644 --- a/src/frontend/components/modal/modals/curval/lib/component.js +++ b/src/frontend/components/modal/modals/curval/lib/component.js @@ -229,7 +229,7 @@ class CurvalModalComponent extends ModalComponent { } existing.push(existing_row) // Store as array for consistency with other field types - await gadsStorage.setItem(parent_key, JSON.stringify(existing), 'local') + await gadsStorage.setItem(parent_key, JSON.stringify(existing)) } $(this.element).modal('hide') diff --git a/src/frontend/js/lib/set-field-value.test.ts b/src/frontend/js/lib/set-field-value.test.ts index 026a71c21..9fb690ea6 100644 --- a/src/frontend/js/lib/set-field-value.test.ts +++ b/src/frontend/js/lib/set-field-value.test.ts @@ -762,10 +762,10 @@ describe('setFieldValue', () => { selectWidgetComponent(dom); document.body.appendChild(dom); const field = $(dom); - const values = [{"year":2024,"month":11,"day":12,"hour":0,"minute":0,"second":0,"epoch":1731369600}]; + const values = [{"year":2024,"month":11,"day":26,"hour":0,"minute":0,"second":0,"epoch":1731369600}]; setFieldValues(field, values); const input = field.find('input'); - expect(input.val()).toBe('2024-11-12'); + expect(input.val()).toBe('2024-11-26'); }) }); diff --git a/src/frontend/js/lib/util/encryptedStorage/lib/encryptedStorage.ts b/src/frontend/js/lib/util/encryptedStorage/lib/encryptedStorage.ts index 3eabc6fe0..8eda9083d 100644 --- a/src/frontend/js/lib/util/encryptedStorage/lib/encryptedStorage.ts +++ b/src/frontend/js/lib/util/encryptedStorage/lib/encryptedStorage.ts @@ -26,7 +26,7 @@ class EncryptedStorage { if (!encryptedValue) { return null; } - return this.decrypt(encryptedValue, encryptionKey); + return await this.decrypt(encryptedValue, encryptionKey); } removeItem(key: string) { diff --git a/src/frontend/js/lib/util/gadsStorage/lib/gadsStorage.ts b/src/frontend/js/lib/util/gadsStorage/lib/gadsStorage.ts index 62926a157..920bcd2d5 100644 --- a/src/frontend/js/lib/util/gadsStorage/lib/gadsStorage.ts +++ b/src/frontend/js/lib/util/gadsStorage/lib/gadsStorage.ts @@ -1,12 +1,16 @@ -import { fromJson } from "util/common"; import { EncryptedStorage } from "util/encryptedStorage"; class GadsStorage { + private get test() { + return location.hostname==="localhost"; + } + private storage: EncryptedStorage | Storage; private storageKey: string; constructor() { - this.storage = location.hostname == "localhost" ? localStorage : EncryptedStorage.instance(); + this.test && console.log("Using localStorage"); + this.storage = this.test ? localStorage : EncryptedStorage.instance(); } private async getStorageKey() { @@ -19,7 +23,12 @@ class GadsStorage { } async setItem(key: string, value: string) { - if(!value || (Array.isArray(fromJson(value)) && value.length === 0)) return; + if(!value || value === "[]") { + if(await this.getItem(key)) { + this.removeItem(key); + } + return; + } if (!this.storageKey) { await this.getStorageKey(); }