Factorizer/Cluster ERP WikiCluster ERP Türkçe
Home / Web app

Explanation

Module apps and app scope

How module-app.js turns a backend module into an app with tabs, how running apps stay alive, and how app-scope.js publishes modules.

In the web app a backend module is used as an app: it opens from the home screen, shows its sections as tabs, and keeps running in the background when the user leaves it. This page explains the app catalogue entries the client reads, how webapp/js/module-app.js builds a module app, how running apps keep their screens, and how webapp/js/app-scope.js publishes which modules are installed. The home screen itself is on home screen, dock, and launcher.

In short

  • The client knows its apps only from GET /app_store/registry, which returns each app's manifest and its entitlement state for the signed-in user (webapp/js/home.js:409-419, src/cluster_erp/modules/app_store/routes.py:18-40).
  • The catalogue file holds 28 app manifests; their screen values are module (20), list (2), and one each of settings, library, store, assistant, mail, and appmgr (counted by parsing src/cluster_erp/modules/app_store/catalog.json).
  • screenRenderer(a) maps the screen value to a render function; module becomes makeModuleScreen (webapp/js/home.js:2415-2441).
  • makeModuleScreen turns each sidebar section into a tab of a bottom tab bar (webapp/js/module-app.js:69-235).
  • Leaving an app keeps its live screen in memory; only the switcher's close resets it (webapp/js/home.js:2252-2326, webapp/js/home.js:2535-2557).

How it works

App manifests

Each registry entry carries a manifest from the catalogue. The client reads these manifest fields:

Field Use in the client Source
id, label, glyph, color, bg Tile, dock icon, and title webapp/js/home.js:520, webapp/js/home.js:1063-1070
screen Which renderer opens the app webapp/js/home.js:2415-2441
module, modules The backend module the app shows, and every module it covers webapp/js/home.js:794-800, webapp/js/home.js:2429
entity, endpoint The entity of a list app webapp/js/home.js:2431-2437
tableList Full-column tables in the app's lists webapp/js/home.js:2429-2437
menu The app opens as a transient menu, not a running app webapp/js/home.js:2452-2465, webapp/js/home.js:2469-2473
permanent The tile cannot be removed from the home screen webapp/js/home.js:742-744
dock_fixed The app is pinned at the start of the dock and cannot be unpinned webapp/js/home.js:990-997
folder The app joins a system folder when added to the home screen webapp/js/home.js:762-780
gated_entry The app's custom code ships as a gated bundle webapp/js/home.js:452-457

The state map from the registry hides an app on every surface unless it is installed, licensed, and seat-assigned to one of the user's roles (webapp/js/home.js:425-430). The layers behind these flags are described on app store, licensing, and entitlements.

Building a module app

makeModuleScreen(moduleName, {table, accent}) returns a render function that waits for metadata and for the module's gated bundle, then reads the module's sidebar (webapp/js/module-app.js:69-101):

  1. Sections with hidden_from_bar are skipped. They stay reachable from search (webapp/js/module-app.js:103-105).
  2. Sections with required_permission are skipped for a user without that code (webapp/js/module-app.js:106-108).
  3. Each remaining section becomes one tab, or one tab per entity for tabbed_list (webapp/js/module-app.js:109-137). See metadata rendering for the engine per view_type.
  4. A module with one tab shows its content without a tab bar (webapp/js/module-app.js:148-154).
  5. The tab bar is a tablist with roving focus; the left and right arrow keys move between tabs (webapp/js/module-app.js:156-212).
  6. Each tab paints into a fresh pane, so a late answer from a previous tab cannot overwrite the current one (webapp/js/module-app.js:183-192).
  7. A cluster:module-tab event with a module and a section key switches tabs; parameters re-render the target tab even when it is already active (webapp/js/module-app.js:213-231).

Tab icons are looked up by the section's icon name in TAB_ICONS, with a square as fallback (webapp/js/module-app.js:21-66).

Running apps and keep-alive

The home screen keeps a list of running apps, each with its own view stack (webapp/js/home.js:2223-2234):

  • openApp(a) moves the current app's screen aside and either restores the target app's screen or renders its root (webapp/js/home.js:2469-2500).
  • stashActive() moves the live DOM nodes, scroll offsets, title, and back label into a document fragment and records the data version seen (webapp/js/home.js:2252-2291).
  • unstashInto(entry) puts the nodes back. If data changed meanwhile, or a paint was dropped while the screen was away, the screen is redrawn, except when a form is open (webapp/js/home.js:2293-2326). See data refresh and change signals.
  • After a language change, stashed screens are marked stale and redrawn on return (webapp/js/home.js:2334-2342, webapp/js/home.js:2501-2511).
  • closeRunningApp(id) discards the stashed screen and the module's form drafts (webapp/js/home.js:2533-2557).
  • On logout closeAllApps() empties the app content area so no screen of the old session remains (webapp/js/home.js:2559-2578).

pushView(title, render) adds a sub-screen to the active stack and popView() removes it; popping the root returns to the home screen with the app still running (webapp/js/home.js:2400-2413). Feature code calls these through nav.js, which home.js fills at load, so engines never import the shell (webapp/js/nav.js:5-30, webapp/js/home.js:2848-2849).

Opening a section from elsewhere

A widget tap, a record link, and a search result open an app section through the same path. openModuleSection(module, section, params) opens the app that covers the module, returns to its root screen, and dispatches cluster:module-tab after the tab bar exists (webapp/js/home.js:221-240). Generic code that cannot import the shell raises cluster:open-section instead (webapp/js/home.js:512-519, webapp/js/gen-form.js:2371-2385).

App scope

syncAppScope() collects the modules of every installed app and passes them to setInstalledModules (webapp/js/home.js:792-800). app-scope.js stores the sorted list and dispatches cluster:apps-changed when it changes (webapp/js/app-scope.js:7-18). The assistant sends this list with every message and cancels an open task card whose module was removed (webapp/js/assistant.js:201-230). See assistant and chat screens.

Why it is built this way

  • The project's written engineering rules state that a module is an application and its pages are that app's tabs, and that the CRM folder is an approved exception.
  • The comment in webapp/js/home.js:2252-2258 states that keep-alive keeps typed input, sub-pages, and scroll positions without serialising anything, so the state dies with the session.
  • The comment in webapp/js/module-app.js:183-187 states that per-tab panes stop a delayed answer from a previous tab from overwriting the new tab.

Rules and boundaries

  • crm has no module app. Its entities appear as two list apps (customer-parts, rfqs) and in the modules of the parties app (src/cluster_erp/modules/app_store/catalog.json:1002-1150).
  • A menu app (menu: true, the Apps library) is never added to the running apps and never appears in the switcher (webapp/js/home.js:2448-2465).
  • Browser tests: web-client/_e2e_switcher.mjs, web-client/_e2e_keepalive_paint.mjs, web-client/_e2e_lang_background.mjs, web-client/_e2e_mobile_app.mjs.

Interaction with other parts

  • Custom sections are rendered by widgets from gated bundles; see gated app bundles.
  • The store, app manager, and licence screens are built by renderStore, renderAppManager, and renderLicense in webapp/js/home.js:3588-4319. Their server side is on app store module.

Limits and open points

  • view_type: "detail" is accepted by SidebarSectionMeta but makeModuleScreen has no branch for it (webapp/js/module-app.js:109-137). No module declares it.
  • A reload restores running apps at their root screens only (webapp/js/home.js:2579-2598).

Terms introduced

Term Code identifier Definition Turkish term Source
module app makeModuleScreen A backend module presented as an app whose sidebar sections are tabs. modül uygulaması webapp/js/module-app.js:69
app manifest registry.apps[].manifest The catalogue entry that tells the client how to show and open an app. uygulama manifestosu webapp/js/home.js:447
keep-alive stashActive Keeping a left app's live screen in memory until it is closed. arka planda canlı tutma webapp/js/home.js:2259
app scope setInstalledModules The list of modules covered by the installed apps. uygulama kapsamı webapp/js/app-scope.js:9

Verified against commit 92f8051 on 2026-11-13.

Verified against commit 92f8051 · 2026-11-13