From 4acb8fae99b39ef03a1817af83ecfcda1926c858 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 13 Jan 2025 11:09:22 +0000 Subject: [PATCH 1/8] Convert portal templates store --- packages/builder/src/stores/portal/templates.js | 16 ---------------- packages/builder/src/stores/portal/templates.ts | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 16 deletions(-) delete mode 100644 packages/builder/src/stores/portal/templates.js create mode 100644 packages/builder/src/stores/portal/templates.ts diff --git a/packages/builder/src/stores/portal/templates.js b/packages/builder/src/stores/portal/templates.js deleted file mode 100644 index 2ff23866ab..0000000000 --- a/packages/builder/src/stores/portal/templates.js +++ /dev/null @@ -1,16 +0,0 @@ -import { writable } from "svelte/store" -import { API } from "@/api" - -export function templatesStore() { - const { subscribe, set } = writable([]) - - return { - subscribe, - load: async () => { - const templates = await API.getAppTemplates() - set(templates) - }, - } -} - -export const templates = templatesStore() diff --git a/packages/builder/src/stores/portal/templates.ts b/packages/builder/src/stores/portal/templates.ts new file mode 100644 index 0000000000..caf1a54ced --- /dev/null +++ b/packages/builder/src/stores/portal/templates.ts @@ -0,0 +1,16 @@ +import { API } from "@/api" +import { BudiStore } from "../BudiStore" +import { TemplateMetadata } from "@budibase/types" + +class TemplateStore extends BudiStore { + constructor() { + super([]) + } + + async load() { + const templates = await API.getAppTemplates() + this.set(templates) + } +} + +export const templates = new TemplateStore() From 085617a5220be1dcbc2d31cde6cb645709b6cf67 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 13 Jan 2025 11:17:11 +0000 Subject: [PATCH 2/8] Convert portal temporal store to TS --- .../portal/licensing/LicensingOverlays.svelte | 4 +- .../portal/licensing/licensingBanners.js | 4 +- .../builder/src/stores/portal/temporal.js | 45 ---------------- .../builder/src/stores/portal/temporal.ts | 53 +++++++++++++++++++ 4 files changed, 57 insertions(+), 49 deletions(-) delete mode 100644 packages/builder/src/stores/portal/temporal.js create mode 100644 packages/builder/src/stores/portal/temporal.ts diff --git a/packages/builder/src/components/portal/licensing/LicensingOverlays.svelte b/packages/builder/src/components/portal/licensing/LicensingOverlays.svelte index 3de2b464e0..8280251839 100644 --- a/packages/builder/src/components/portal/licensing/LicensingOverlays.svelte +++ b/packages/builder/src/components/portal/licensing/LicensingOverlays.svelte @@ -20,7 +20,7 @@ const processModals = () => { const defaultCacheFn = key => { - temporalStore.actions.setExpiring(key, {}, oneDayInSeconds) + temporalStore.setExpiring(key, {}, oneDayInSeconds) } const dismissableModals = [ @@ -50,7 +50,7 @@ }, ] return dismissableModals.filter(modal => { - return !temporalStore.actions.getExpiring(modal.key) && modal.criteria() + return !temporalStore.getExpiring(modal.key) && modal.criteria() }) } diff --git a/packages/builder/src/components/portal/licensing/licensingBanners.js b/packages/builder/src/components/portal/licensing/licensingBanners.js index 230b9fe6f6..62ca6caa1b 100644 --- a/packages/builder/src/components/portal/licensing/licensingBanners.js +++ b/packages/builder/src/components/portal/licensing/licensingBanners.js @@ -6,7 +6,7 @@ import { BANNER_TYPES } from "@budibase/bbui" const oneDayInSeconds = 86400 const defaultCacheFn = key => { - temporalStore.actions.setExpiring(key, {}, oneDayInSeconds) + temporalStore.setExpiring(key, {}, oneDayInSeconds) } const upgradeAction = key => { @@ -148,7 +148,7 @@ export const getBanners = () => { buildUsersAboveLimitBanner(ExpiringKeys.LICENSING_USERS_ABOVE_LIMIT_BANNER), ].filter(licensingBanner => { return ( - !temporalStore.actions.getExpiring(licensingBanner.key) && + !temporalStore.getExpiring(licensingBanner.key) && licensingBanner.criteria() ) }) diff --git a/packages/builder/src/stores/portal/temporal.js b/packages/builder/src/stores/portal/temporal.js deleted file mode 100644 index 96b47d1c7f..0000000000 --- a/packages/builder/src/stores/portal/temporal.js +++ /dev/null @@ -1,45 +0,0 @@ -import { createLocalStorageStore } from "@budibase/frontend-core" -import { get } from "svelte/store" - -export const createTemporalStore = () => { - const initialValue = {} - - const localStorageKey = `bb-temporal` - const store = createLocalStorageStore(localStorageKey, initialValue) - - const setExpiring = (key, data, duration) => { - const updated = { - ...data, - expiry: Date.now() + duration * 1000, - } - - store.update(state => ({ - ...state, - [key]: updated, - })) - } - - const getExpiring = key => { - const entry = get(store)[key] - if (!entry) { - return - } - const currentExpiry = entry.expiry - if (currentExpiry < Date.now()) { - store.update(state => { - delete state[key] - return state - }) - return null - } else { - return entry - } - } - - return { - subscribe: store.subscribe, - actions: { setExpiring, getExpiring }, - } -} - -export const temporalStore = createTemporalStore() diff --git a/packages/builder/src/stores/portal/temporal.ts b/packages/builder/src/stores/portal/temporal.ts new file mode 100644 index 0000000000..acbe6feff2 --- /dev/null +++ b/packages/builder/src/stores/portal/temporal.ts @@ -0,0 +1,53 @@ +import { get } from "svelte/store" +import { BudiStore, PersistenceType } from "../BudiStore" + +type TemporalItem = Record & { expiry: number } +type TemporalState = Record + +class TemporalStore extends BudiStore { + constructor() { + super( + {}, + { + persistence: { + key: "bb-temporal", + type: PersistenceType.LOCAL, + }, + } + ) + } + + setExpiring = ( + key: string, + data: Record, + durationSeconds: number + ) => { + const updated: TemporalItem = { + ...data, + expiry: Date.now() + durationSeconds * 1000, + } + this.update(state => ({ + ...state, + [key]: updated, + })) + } + + getExpiring(key: string) { + const entry = get(this.store)[key] + if (!entry) { + return null + } + const currentExpiry = entry.expiry + if (currentExpiry < Date.now()) { + this.update(state => { + delete state[key] + return state + }) + return null + } else { + return entry + } + } +} + +export const temporalStore = new TemporalStore() From 5f3825118f175b66c03c2ac2147c0364ca8f838f Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 13 Jan 2025 11:23:09 +0000 Subject: [PATCH 3/8] Convert portal theme store to TS --- packages/builder/src/stores/portal/theme.js | 37 ----------------- packages/builder/src/stores/portal/theme.ts | 45 +++++++++++++++++++++ 2 files changed, 45 insertions(+), 37 deletions(-) delete mode 100644 packages/builder/src/stores/portal/theme.js create mode 100644 packages/builder/src/stores/portal/theme.ts diff --git a/packages/builder/src/stores/portal/theme.js b/packages/builder/src/stores/portal/theme.js deleted file mode 100644 index 11a87845e1..0000000000 --- a/packages/builder/src/stores/portal/theme.js +++ /dev/null @@ -1,37 +0,0 @@ -import { createLocalStorageStore } from "@budibase/frontend-core" -import { derived } from "svelte/store" -import { - DefaultBuilderTheme, - ensureValidTheme, - getThemeClassNames, - ThemeOptions, - ThemeClassPrefix, -} from "@budibase/shared-core" - -export const getThemeStore = () => { - const themeElement = document.documentElement - const initialValue = { - theme: DefaultBuilderTheme, - } - const store = createLocalStorageStore("bb-theme", initialValue) - const derivedStore = derived(store, $store => ({ - ...$store, - theme: ensureValidTheme($store.theme, DefaultBuilderTheme), - })) - - // Update theme class when store changes - derivedStore.subscribe(({ theme }) => { - const classNames = getThemeClassNames(theme).split(" ") - ThemeOptions.forEach(option => { - const className = `${ThemeClassPrefix}${option.id}` - themeElement.classList.toggle(className, classNames.includes(className)) - }) - }) - - return { - ...store, - subscribe: derivedStore.subscribe, - } -} - -export const themeStore = getThemeStore() diff --git a/packages/builder/src/stores/portal/theme.ts b/packages/builder/src/stores/portal/theme.ts new file mode 100644 index 0000000000..5198555024 --- /dev/null +++ b/packages/builder/src/stores/portal/theme.ts @@ -0,0 +1,45 @@ +import { derived, Writable } from "svelte/store" +import { + DefaultBuilderTheme, + ensureValidTheme, + getThemeClassNames, + ThemeOptions, + ThemeClassPrefix, +} from "@budibase/shared-core" +import { Theme } from "@budibase/types" +import { DerivedBudiStore, PersistenceType } from "../BudiStore" + +interface ThemeState { + theme: Theme +} + +class ThemeStore extends DerivedBudiStore { + constructor() { + const makeDerivedStore = (store: Writable) => { + return derived(store, $store => ({ + ...$store, + theme: ensureValidTheme($store.theme, DefaultBuilderTheme), + })) + } + super({ theme: DefaultBuilderTheme }, makeDerivedStore, { + persistence: { + key: "bb-theme", + type: PersistenceType.LOCAL, + }, + }) + + // Update theme class when store changes + this.subscribe(({ theme }) => { + const classNames = getThemeClassNames(theme).split(" ") + ThemeOptions.forEach(option => { + const className = `${ThemeClassPrefix}${option.id}` + document.documentElement.classList.toggle( + className, + classNames.includes(className) + ) + }) + }) + } +} + +export const themeStore = new ThemeStore() From 65bd89250de002307ce62c1c2845ba1efa3e48ad Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 13 Jan 2025 11:24:04 +0000 Subject: [PATCH 4/8] Convert portal barrel file to TS --- packages/builder/src/stores/portal/{index.js => index.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/builder/src/stores/portal/{index.js => index.ts} (100%) diff --git a/packages/builder/src/stores/portal/index.js b/packages/builder/src/stores/portal/index.ts similarity index 100% rename from packages/builder/src/stores/portal/index.js rename to packages/builder/src/stores/portal/index.ts From 12b283d41ed8b7be2e41b099321adcf47efe1ad0 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 13 Jan 2025 11:28:04 +0000 Subject: [PATCH 5/8] Convert portal navigation store to BudiStore --- .../builder/src/pages/builder/_layout.svelte | 2 +- .../builder/src/stores/portal/navigation.ts | 31 +++++++------------ 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/packages/builder/src/pages/builder/_layout.svelte b/packages/builder/src/pages/builder/_layout.svelte index 209cf2746d..7e765d7366 100644 --- a/packages/builder/src/pages/builder/_layout.svelte +++ b/packages/builder/src/pages/builder/_layout.svelte @@ -18,7 +18,7 @@ $: useAccountPortal = cloud && !$admin.disableAccountPortal - navigation.actions.init($redirect) + navigation.init($redirect) const validateTenantId = async () => { const host = window.location.host diff --git a/packages/builder/src/stores/portal/navigation.ts b/packages/builder/src/stores/portal/navigation.ts index 4eb50bc84f..f289c86bf9 100644 --- a/packages/builder/src/stores/portal/navigation.ts +++ b/packages/builder/src/stores/portal/navigation.ts @@ -1,38 +1,31 @@ -import { writable } from "svelte/store" +import { BudiStore } from "../BudiStore" type GotoFuncType = (path: string) => void -interface PortalNavigationStore { +interface NavigationState { initialisated: boolean goto: GotoFuncType } -export function createNavigationStore() { - const store = writable({ - initialisated: false, - goto: undefined as any, - }) - const { set, subscribe } = store +class NavigationStore extends BudiStore { + constructor() { + super({ + initialisated: false, + goto: undefined as any, + }) + } - const init = (gotoFunc: GotoFuncType) => { + init(gotoFunc: GotoFuncType) { if (typeof gotoFunc !== "function") { throw new Error( `gotoFunc must be a function, found a "${typeof gotoFunc}" instead` ) } - - set({ + this.set({ initialisated: true, goto: gotoFunc, }) } - - return { - subscribe, - actions: { - init, - }, - } } -export const navigation = createNavigationStore() +export const navigation = new NavigationStore() From 502c1605307c3efdcb4c6ea27e5a5a2e08a9ccff Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 13 Jan 2025 11:31:44 +0000 Subject: [PATCH 6/8] Convert admin store to BudiStore --- packages/builder/src/stores/portal/admin.ts | 71 ++++++++++----------- 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/packages/builder/src/stores/portal/admin.ts b/packages/builder/src/stores/portal/admin.ts index f262d54bff..1cb0be1313 100644 --- a/packages/builder/src/stores/portal/admin.ts +++ b/packages/builder/src/stores/portal/admin.ts @@ -1,4 +1,4 @@ -import { writable, get } from "svelte/store" +import { get } from "svelte/store" import { API } from "@/api" import { auth } from "@/stores/portal" import { banner } from "@budibase/bbui" @@ -7,42 +7,44 @@ import { GetEnvironmentResponse, SystemStatusResponse, } from "@budibase/types" +import { BudiStore } from "../BudiStore" -interface PortalAdminStore extends GetEnvironmentResponse { +interface AdminState extends GetEnvironmentResponse { loaded: boolean checklist?: ConfigChecklistResponse status?: SystemStatusResponse } -export function createAdminStore() { - const admin = writable({ - loaded: false, - multiTenancy: false, - cloud: false, - isDev: false, - disableAccountPortal: false, - offlineMode: false, - maintenance: [], - }) +class AdminStore extends BudiStore { + constructor() { + super({ + loaded: false, + multiTenancy: false, + cloud: false, + isDev: false, + disableAccountPortal: false, + offlineMode: false, + maintenance: [], + }) + } - async function init() { - await getChecklist() - await getEnvironment() + async init() { + await this.getChecklist() + await this.getEnvironment() // enable system status checks in the cloud - if (get(admin).cloud) { - await getSystemStatus() - checkStatus() + if (get(this.store).cloud) { + await this.getSystemStatus() + this.checkStatus() } - - admin.update(store => { + this.update(store => { store.loaded = true return store }) } - async function getEnvironment() { + async getEnvironment() { const environment = await API.getEnvironment() - admin.update(store => { + this.update(store => { store.multiTenancy = environment.multiTenancy store.cloud = environment.cloud store.disableAccountPortal = environment.disableAccountPortal @@ -56,43 +58,36 @@ export function createAdminStore() { }) } - const checkStatus = async () => { - const health = get(admin)?.status?.health + async checkStatus() { + const health = get(this.store).status?.health if (!health?.passing) { await banner.showStatus() } } - async function getSystemStatus() { + async getSystemStatus() { const status = await API.getSystemStatus() - admin.update(store => { + this.update(store => { store.status = status return store }) } - async function getChecklist() { + async getChecklist() { const tenantId = get(auth).tenantId const checklist = await API.getChecklist(tenantId) - admin.update(store => { + this.update(store => { store.checklist = checklist return store }) } - function unload() { - admin.update(store => { + unload() { + this.update(store => { store.loaded = false return store }) } - - return { - subscribe: admin.subscribe, - init, - unload, - getChecklist, - } } -export const admin = createAdminStore() +export const admin = new AdminStore() From 153d905921c48be839c8aa1a74d76743017fe8b3 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 13 Jan 2025 11:33:39 +0000 Subject: [PATCH 7/8] Update exports --- packages/builder/src/stores/portal/admin.test.js | 4 ++-- packages/builder/src/stores/portal/admin.ts | 2 +- packages/builder/src/stores/portal/auditLogs.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/builder/src/stores/portal/admin.test.js b/packages/builder/src/stores/portal/admin.test.js index 1528042630..261e94eb6f 100644 --- a/packages/builder/src/stores/portal/admin.test.js +++ b/packages/builder/src/stores/portal/admin.test.js @@ -1,5 +1,5 @@ import { it, expect, describe, beforeEach, vi } from "vitest" -import { createAdminStore } from "./admin" +import { AdminStore } from "./admin" import { writable, get } from "svelte/store" import { API } from "@/api" import { auth } from "@/stores/portal" @@ -46,7 +46,7 @@ describe("admin store", () => { ctx.writableReturn = { update: vi.fn(), subscribe: vi.fn() } writable.mockReturnValue(ctx.writableReturn) - ctx.returnedStore = createAdminStore() + ctx.returnedStore = new AdminStore() }) it("returns the created store", ctx => { diff --git a/packages/builder/src/stores/portal/admin.ts b/packages/builder/src/stores/portal/admin.ts index 1cb0be1313..90e3a5cdc9 100644 --- a/packages/builder/src/stores/portal/admin.ts +++ b/packages/builder/src/stores/portal/admin.ts @@ -15,7 +15,7 @@ interface AdminState extends GetEnvironmentResponse { status?: SystemStatusResponse } -class AdminStore extends BudiStore { +export class AdminStore extends BudiStore { constructor() { super({ loaded: false, diff --git a/packages/builder/src/stores/portal/auditLogs.ts b/packages/builder/src/stores/portal/auditLogs.ts index ff29f0cd1b..6f11f228d6 100644 --- a/packages/builder/src/stores/portal/auditLogs.ts +++ b/packages/builder/src/stores/portal/auditLogs.ts @@ -13,7 +13,7 @@ interface PortalAuditLogsStore { logs?: SearchAuditLogsResponse } -export class AuditLogsStore extends BudiStore { +class AuditLogsStore extends BudiStore { constructor() { super({}) } From 9e10921e2981b73b8ca497354d0b55c5089e7fff Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 13 Jan 2025 12:01:29 +0000 Subject: [PATCH 8/8] Update tests --- packages/builder/src/stores/portal/admin.test.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/packages/builder/src/stores/portal/admin.test.js b/packages/builder/src/stores/portal/admin.test.js index 261e94eb6f..8924a5e6fb 100644 --- a/packages/builder/src/stores/portal/admin.test.js +++ b/packages/builder/src/stores/portal/admin.test.js @@ -49,15 +49,6 @@ describe("admin store", () => { ctx.returnedStore = new AdminStore() }) - it("returns the created store", ctx => { - expect(ctx.returnedStore).toEqual({ - subscribe: expect.toBe(ctx.writableReturn.subscribe), - init: expect.toBeFunc(), - unload: expect.toBeFunc(), - getChecklist: expect.toBeFunc(), - }) - }) - describe("init method", () => { beforeEach(async ctx => { let getMockIndex = 0