SaleSys Skills registry json
← all skills

salesys-vyer

Build, test, and deploy custom SaleSys views ("vyer" / view presets / custom views / vymallar) — standalone HTML panels that mount inside app.salesys.se navigation or settings. Two authoring paradigms: the NATIVE way (handlebars + built-in components like <Field>, <SecondaryButton>, <SettingsList>, <Transition>, Modal, and the window.form.* API) for forms/config/CRUD panels, and the RAW-JS way (cookie token + direct fetch + document-level delegation) for complex apps/dashboards/games. Covers every native component and window.form method, the handlebars helper set, the views-v1/view-presets-v1/presetState/rights/entity-groups/notify APIs, the reference-object-as-database pattern, and the repo's build-harness + deploy-preset workflow. Use whenever asked to make/edit/deploy a "vy", "view", "custom view", "view preset", "vymall", "inställningsvy", or a SaleSys admin panel/dashboard/screen.

curl -fsSL https://skills.salesys.se/install.sh | sh -s salesys-vyer

Installs into ~/.claude/skills/salesys-vyer. Add --project to install into the current repo's .claude/skills.

SaleSys egna vyer (custom views)

A vy is a standalone HTML fragment that SaleSys renders as a panel inside app.salesys.se — reachable from the top navigation or from Inställningar. No framework, no build step: one HTML file with an inline <style> and <script>. It talks to the SaleSys REST API as the logged-in user.

Two layers:

⚠️ First decision: which paradigm?

There are two ways to author a view. Pick before writing a line — they don't mix well.

Native (handlebars + components) Raw JS
Best for Forms, settings/config panels, master-detail CRUD, anything that looks like the rest of SaleSys Dashboards, games, custom canvas/layout, heavy realtime, xlsx export, anything the components can't express
Uses {{ }} Yes — this is the templating language. {{custom.x}}, {{#each}}, {{#if}} Never — you build the DOM yourself; deploy-preset.py refuses any file containing {{
Components <Field>, <SecondaryButton>, <SettingsList>, <Transition>, <CheckboxField>, <Tooltip>, Modal plain HTML + your own CSS, matching native chrome via CSS variables
State window.form.custom / setState, window.form.settings / setSettings, window.form.addHook your own JS variables + a poll loop; persist in the user reference object
Data access window.form.resources.* (preloaded) + fetch with window.getAccessToken() fetch with token read from the s2_utoken cookie
Edited in the browser (admin.salesys.se) or via API authored in the vyer/ repo, deployed with deploy-preset.py
Reference example examples/webhooks.view.html, examples/stangtider.view.html (both bundled here) any dir under vyer/ (tavlingar2/, qa/, tidsrapportering/)

Rule of thumb: if it's a form or a settings screen, go native — you get validation, theming, and the SaleSys look for free. If it's an interactive app that fights the component model, go raw JS. The {{-refusal in deploy-preset.py exists because the repo standardised on raw JS — it is a repo choice, not a platform limit. The platform happily renders handlebars (Webhooks, Stängtider, Rapportverktyg all do).

Read next (progressive disclosure)

Workflow (either paradigm)

  1. Scaffold vyer/<name>/<name>.html — copy the closest existing view (native → an example here; raw JS → a vyer/ dir).
  2. Test locally before prod. Raw-JS views: python3 vyer/build-harness.py → serve /tmp/ksv-<name>-test.html and retest after document.body.innerHTML = document.body.innerHTML (the app re-renders the view DOM after init). Native views: preview in admin.salesys.se ("Förhandsgranska"); window.form.preview is true there.
  3. Deploy the preset (admin token in /tmp/ksv-token): python3 vyer/deploy-preset.py <name> (first run POSTs + records the id in presets.json; later runs PUT). Register the view in deploy-preset.py's VIEWS dict (and build-harness.py if raw JS). Preset HTML changes propagate to every org view instantly.
  4. The first org view must be created via API — a preset's settings only surface in the UI once one org view exists: POST /users/views-v1 { presetId, name, iconName, iconColor, locations, teamIds: null } (null = everyone, [] = admin-only). Thereafter edit HTML in the browser.

Rules that apply to BOTH paradigms

Raw-JS-only rules (event delegation on document, window.__<ns>Cleanup, the mandatory contrast probe in the poll loop, cookie-token auth) live in reference/apis-and-deploy.md.