Compass — lightweight CRM
The CRM that fits in a file: clients, deals and activities, with the big picture always in front of you. Everything stays on your device.
data space «studio-en» — the suite’s glue: on your device, multi-user with one invite (link or QR), end-to-end encrypted.
▶ Open the suite (5 micro apps)
Integrates with: Tally — same data space.
What it records
- Name or company name
- Contact person
- Phone
- Deal subject
- Value (€)
- Status...
- What to do (call, visit, send quote...)
- Activity (e.g. software, catering)
Composed of 5 micro apps
One job each, over the same data: every micro app reads and writes the shared data space “studio-en” (with your consent, asked on first open). Everything stays on your device — and with a single invite the whole suite goes multi-user, end-to-end encrypted.
Shared micro apps are the bridge between solutions: the same data also powers Tally.
How it works
This is a Reactive app: a plain text document that your browser turns into a working app — forms, live data, tables, statistics and even charts, with nothing to install or configure.
- Use it right away. Hit “Use this app”: it opens right in your browser, ready to go, with data saved only on your device.
- Your data stays yours. Everything is stored locally on your device, kept separate for each app: no account, no server, nothing to set up.
- Make it your own. The text below is the entire app: copy it, tweak fields, views and words, and your changes become interactive instantly.
- Share it as a file. An app is a simple file: save it, export it or share it with a link; and with encrypted collaboration several people edit the same data in real time.
The app’s source
# 🧭 Compass
The CRM that fits in a file: clients, deals and activities, with the big picture always in front of you. Everything stays on your device.
:::::page{title="Dashboard" icon="📊"}
## The picture at a glance
:::grid{cols="3"}
**Clients**
:count[nclienti]{path="clients"}
---
**Deals**
:count[ntrattative]{path="deals"}
---
**Pipeline (€)**
:sum[pipeline]{path="deals" field="value"}
:::
## Pipeline by status
:::if-empty{path="deals"}
*No deals yet: open one from the Deals page.*
:::
:::group-by{path="deals" field="status" as="list"}
**{title}** — {client} · € {value}
:::
:::::
:::::page{title="Clients" icon="👥"}
## Client directory
::::form{path="clients" id="fcli"}
::input{form="fcli" field="name" placeholder="Name or company name" required="true"}
::input{form="fcli" field="contact" placeholder="Contact person"}
::input{form="fcli" field="phone" placeholder="Phone"}
::input{form="fcli" field="email" type="email" placeholder="Email"}
::add-form{form="fcli" path="clients" label="Add client"}
::::
:::table{path="clients" headers="Client,Contact,Phone,Email" deletable="true" editform="fcli"}
**{name}** | {contact} | {phone} | {email}
:::
:::::
:::::page{title="Deals" icon="💼"}
## Sales pipeline
::::form{path="deals" id="ftr"}
::input{form="ftr" field="title" placeholder="Deal subject" required="true"}
::select-from{from="clients" form="ftr" field="client" label="name" value="name"}
::input{form="ftr" field="value" type="number" placeholder="Value (€)"}
:::select{form="ftr" field="status" placeholder="Status..."}
- Contact
- Proposal
- Negotiation
- Won
- Lost
:::
::input{form="ftr" field="closing" type="date" legend="Expected closing"}
::add-form{form="ftr" path="deals" label="Log deal"}
::::
:::table{path="deals" headers="Deal,Client,Value,Status,Closing" deletable="true" editform="ftr"}
**{title}** | {client} | € {value} | {status} | {closing}
:::
Total value: **€ :sum[vt]{path="deals" field="value"}**
:::::
:::::page{title="Activities" icon="✅"}
## To do, on the calendar
::::form{path="activities" id="fatt"}
::input{form="fatt" field="date" type="date" required="true" legend="When"}
::input{form="fatt" field="task" placeholder="What to do (call, visit, send quote...)" required="true"}
::select-from{from="clients" form="fatt" field="client" label="name" value="name"}
::add-form{form="fatt" path="activities" label="Schedule"}
::::
:::calendar{path="activities" field="date"}
{task} · {client}
:::
:::sort{path="activities" field="date" order="asc" as="list" deletable="true"}
{date} — **{task}** ({client})
:::
:::::
:::::page{title="ATECO" icon="🏷️"}
## The right ATECO code, from official data
Look up a client's economic activity in the official Italian ATECO 2025
classification (open data): type a word and get the code and description to
use in records and invoices. The search updates as you type.
::input[od_ateco]{placeholder="Activity (e.g. software, catering)"}
::od-query{into="od_ateco_res" sql="SELECT CODICE_ATECO_2025 AS code, COALESCE(NULLIF(PREFLABEL_EN, ''), PREFLABEL_IT) AS description FROM voc_istat_ateco_2025 WHERE PREFLABEL_EN ILIKE '%' || '{#od_ateco}' || '%' OR PREFLABEL_IT ILIKE '%' || '{#od_ateco}' || '%' ORDER BY code" limit="30"}
:::table{path="od_ateco_res" headers="Code,Description" search="false" pagesize="8"}
{code} | {description}
:::
:::::