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-vyerInstalls into ~/.claude/skills/salesys-vyer. Add --project to install into the current repo's .claude/skills.
- SKILL.md7.6 KB
- examples/all-view-presets.json2.0 MB
- examples/modal.view.html4.5 KB
- examples/stangtider.view.html20.9 KB
- examples/webhooks.view.html35.2 KB
- reference/apis-and-deploy.md14.0 KB
- reference/native-components.md13.0 KB
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:
- View preset (
view-presets-v1, admin) = the reusable template (html+ optionalsettingsHtml, icon, rights). - Org view (
views-v1) = a per-organisation instance pointing at a preset viapresetId, deciding navigation placement + team/role visibility. URL:https://app.salesys.se/views/<viewId>.
⚠️ 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)
reference/native-components.md— the full catalog: every component + attribute, the completewindow.form.*API, the modal system, the handlebars helper set,resources, partials. Read this before building a native view. The bundledexamples/webhooks.view.htmlis the masterclass — a full CRUD panel (list → modal editor → preview → nested actions) built entirely from native components;examples/stangtider.view.htmlis a second complete worked example (opening-hours rules + call routing).reference/apis-and-deploy.md— REST endpoints,presetState, customrights, thereference-object-as-database pattern, in-app notifications, files, entity groups, and the build-harness → deploy-preset workflow with the raw-JS gotchas. Read this before deploying anything or for any data/API question.- In the SaleSys repo:
vyer/README.md,vyer/deploy-preset.py,vyer/presets.json,vyer/PROJECT-LOG.md, and any existing view dir are the live source of truth — always cross-check them.
Workflow (either paradigm)
- Scaffold
vyer/<name>/<name>.html— copy the closest existing view (native → an example here; raw JS → avyer/dir). - Test locally before prod. Raw-JS views:
python3 vyer/build-harness.py→ serve/tmp/ksv-<name>-test.htmland retest afterdocument.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.previewistruethere. - Deploy the preset (admin token in
/tmp/ksv-token):python3 vyer/deploy-preset.py <name>(first run POSTs + records the id inpresets.json; later runs PUT). Register the view indeploy-preset.py'sVIEWSdict (andbuild-harness.pyif raw JS). Preset HTML changes propagate to every org view instantly. - 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
- No in-view title/heading — the app already renders the view's name as a large title top-left
(part of the nav chrome). Do NOT repeat it with your own
<h1>/header inside the view — it shows the name twice. Start straight with content. Native views can add a subtitle viawindow.form.setSubtitle("…")and a title-bar action viawindow.form.setButton(...); a detail/sub heading inside a card is fine, just not a top-level view title. (For a full-bleed compact layout you may instead hide the app title with CSS, e.g..ribbon_title__3ukce{display:none}— the QA view does this.) - Never set page width — the SaleSys app container controls the view's width and outer padding.
A view fills the container fluidly; it must NOT impose its own page width,
max-width, fixed pixel width,margin: 0 autocentering, or outer horizontal margins on its root. Lay out within the given width with fluid/responsive CSS (%,fr, flex/grid,minmax); constrain only inner elements when needed, never the whole view. Don't assume a viewport width either — the container narrows on smaller screens and when side widgets are open. - No emojis — FontAwesome 5.10.2 (global) or inline SVG.
✓/×typographic marks are fine. (User rule.) - Theme via CSS variables with fallbacks —
var(--color-background-2, #fff),--color-text-2/3,--color-primary,--color-background-1/3,--color-element-hover,--border-radius-button(8). - No native
<select>in raw-JS forms — use icon-card grids / chip rows / preview cards + a side info-panel. (Native views use<Field select-type="dropdown|switches">instead, which is fine.) - Icons: FontAwesome 5 free — https://fontawesome.com/v5/search?ic=free-collection
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.