Frontend (Sylva Enterprise)
Sylva Enterprise (sylva-enterprise) is the primary web client: org-specific branding (domain, logo, CSS), course editor, participant flows, and integrations with the Identity Manager API, Firebase, and Wolfram backends.
Stack (see also Codebase statistics):
- Quasar 1 (Vue CLI pipeline, Webpack extended via
webpack.conf.js) - Vue 2 SPA, history router mode
- Vuex with many feature modules under
src/store/ - Axios REST client and Wolfram clients (
src/boot/api.js) - Firebase compat (Auth, Firestore, Analytics) after cloud config loads
- Global SCSS entry
src/css/app.scss→ partials undersrc/css/components/
For known limitations, CSS trade-offs, and structural improvement ideas, see Frontend — CSS, structure, and tech debt.
Repository layout (mental map)
Section titled “Repository layout (mental map)”High-signal folders under src/:
| Path | Role |
|---|---|
boot/ | Application bootstrap: API clients, router guards, Firebase/cloud config, Quasar plugins, global directives and base components |
router/ | index.js wires Vue Router + Sentry; private.js / public.js define route trees and meta for access control |
layouts/ | Three Quasar q-layout shells: course player, admin/editor, public |
pages/ | Top-level route targets (dashboard, course shell, manager, errors, analytics, …) |
components/ | Majority of UI: nested editor chrome, module players, content types, dialogs, charts |
store/ | Vuex root index.js registers feature modules (session, course, admin, theme, …) |
css/ | Global SCSS: app.scss imports Quasar variables + component partials |
mixins/ | Shared Vue mixins (e.g. cssThemes.js for org theming) |
utils/ | Pure helpers (dates, project names, Creo helpers, …) |
Components are grouped by domain (e.g. components/Menus/AdminMenu/..., components/ContentTypes/...) rather than a flat atomic design tree—use repo search and the route → page → layout chain to find entry points.
Boot order (Quasar)
Section titled “Boot order (Quasar)”quasar.conf.js registers boot files in this order (they run before the root Vue instance mounts):
flowchart LR A[api] --> B[router-guard] B --> C[init] C --> D[plugins] D --> E[directives] E --> F[base-components] F --> G[notify-defaults] G --> H[tooltip-defaults]
| Boot file | Responsibility |
|---|---|
api.js | API, WEPC_API, WWE_API axios instances; session key in LocalStorage; SSO query stripping; window.$SYLVA_API |
router-guard.js | beforeEach: auth, archive rules, fetchProject when projectId changes, feature flags, completion redirects (see Authentication and access control) |
init.js | cloud-config fetch, Firebase initializeApp, theme Vuex, window._org, Sentry-friendly init paths |
plugins.js | Vuelidate, ApexCharts, InstantSearch, MathLive, YouTube/Vimeo, timeago, global prototypes |
directives.js | Custom Vue directives |
base-components.js | Globally registered components |
notify-defaults.js, tooltip-defaults.js | Quasar UI defaults |
Runtime data flow (simplified)
Section titled “Runtime data flow (simplified)”sequenceDiagram participant B as Browser participant Q as Quasar SPA participant API as Identity Manager /api participant CC as cloud-config participant FB as Firebase B->>Q: Load JS bundle Q->>API: GET cloud-config API-->>CC: Org theme, firebase snippet, org id Q->>FB: initializeApp + optional signInWithCustomToken Q->>API: REST with Authorization + _org API-->>Q: Projects, modules, content JSON
- API base URL is chosen in
api.js(dev domain vsAPI_HOSTfrom build env—seequasar.conf.jsbuild.env). - Cloud config drives white-label: logos, CSS variables, Firebase config JSON in
custom_config. - Authenticated calls use the stored access token; org scope is applied after session/cloud config (see auth doc for
_org).
Routing model
Section titled “Routing model”Routes are privateRoutes.concat(publicRoutes) in src/router/index.js. Private routes cover the logged-in app (dashboard, /p/:projectId player, /manage/:projectId staff tools, org admin, etc.); public routes cover login, signup, legal pages, and similar.
Participant vs staff vs public (conceptual)
Section titled “Participant vs staff vs public (conceptual)”flowchart TB
subgraph public [Public routes]
P1[Login / signup / SSO callbacks]
end
subgraph participant [Participant — /p/:projectId]
R1[start, modules, courseware, assessment, survey, poll, game]
end
subgraph staff [Staff — /manage/:projectId]
R2[Editor, people, settings, results, …]
end
public --> participant
public --> staff
Examples of meta on routes (see private.js):
requiresAuth— processed in the guard together with session staterequiresPlayCourse— participant course accessrequiresStaff— course manager shelldenyArchive— block non-staff if project is archivedrequiresCompletion,appFeature,minBrowserWidth— feature and UX gates
Child routes under Course.vue use short path segments (cw, as, su, po, ga) for module modes (courseware, assessment, survey, poll, game).
Layouts (three shells)
Section titled “Layouts (three shells)”All three use q-layout + q-page-container + a two- or three-column grid (main-grid-layout*) with slots for left aside (navigation / module list), main page (router-view content lives in the parent page), and right aside (chat, CTAs, etc.).
| Layout | File | Typical use |
|---|---|---|
| Course | MainLayoutCourse.vue | Participant flows under /p/... — top bar, fullscreen game mode, chat visibility on mobile |
| Admin | MainLayoutAdmin.vue | Editor and org admin — wheel/touch forwarded for complex interactions, optional preview class |
| Public | MainLayoutPublic.vue | Marketing / auth-only pages without course chrome |
MainLayoutCourse applies route name as a CSS class on the grid (:class="{ [$route.name]: $route.name }") so page-scoped layout tweaks are possible without duplicating layouts.
State management (Vuex)
Section titled “State management (Vuex)”Root store (src/store/index.js) registers modules including:
admin, adminMenu, course, modules, session, banner, menu, colors, chat, timer, release, theme, settings, results, game, poll, report, databins, analytics.
Session and course are the usual starting points when tracing “where does current user / current project live?”. Theme is fed from cloud config and works with the cssThemes mixin (CSS custom properties for Quasar and brand colors).
Styling pipeline
Section titled “Styling pipeline”- Quasar loads
app.scssonce (seequasar.conf.js→css: ['app.scss']). app.scssimportsquasar.variablesthen a stack of partials (_global,_cards,_inputs, …).- Per-component styles live in Vue SFC
<style>blocks (often scoped, sometimes SCSS with deep selectors for Quasar internals). - Runtime theming:
mixins/cssThemes.jsmaps cloudtheme.datainto--q-color-*and--theme-color-*variables; Quasar palette names are resolved withgetPaletteColor.
Build / framework notes (from quasar.conf.js)
Section titled “Build / framework notes (from quasar.conf.js)”API_ENVselects defaultAPI_HOST(identity.sylva.ac,identity.ch.sylva.ac,api.dev.sylva.ac,identity.test.sylva.ac);API_HOSTenv overrides.build.envpassesDOMAIN,FORCE_PROD_API(fromPREVIEW_PR_PROD),SENTRY,REGION,VERSION, etc. to the client bundle.- Webpack:
extendWebpackfromwebpack.conf.js;jsonpFunction/librarynames avoid collisions when embedded. - A temporary
crypto.createHashpatch (md4→md5) is documented in config as a workaround until Webpack 5 or Vite—treat as operational debt when touching the build.
Quasar framework: importStrategy: 'auto', plugins Dialog, Notify, LocalStorage, SessionStorage, Meta, AppFullscreen; icon/font extras include eva-icons, roboto-font, material-icons (and outlined).
Environment variables (conceptual)
Section titled “Environment variables (conceptual)”| Variable | Purpose |
|---|---|
DOMAIN | Dev-only target domain for API/cloud resolution |
API_HOST | Override API hostname at build time |
API_ENV | Preset region/environment for default API host |
PREVIEW_PR_PROD | Maps to FORCE_PROD_API in client env |
VERSION | window.SYLVA_VERSION |
SENTRY | Enables Sentry initialization branch in router |
Calling backends
Section titled “Calling backends”- REST:
this.$api/window.$SYLVA_API→/api/...withwithCredentials: true. - Wolfram:
WEPC_API(/wepc-api),WWE_API(/wwe-api) — README refers to “WLS”; the boot file exportsWWE_APIfor the scalable server path. - CDN / images:
CDN_URLinapi.js;$getImageKitUrlonwindowfor ImageKit transforms (seeinit.js).
White-label behavior
Section titled “White-label behavior”Customization includes domain, logo/icons, CSS (global + variables), SSO, and (per README) possible future custom API hooks. The same build can render different orgs because cloud-config is fetched at runtime.
Quality and tests
Section titled “Quality and tests”- Lint:
npm run lint(see enterprise README). - E2E: Selenium under
test/(seetest/README.md).