Replace Salesforce with a custom CRM built in Xano. A self-contained sidestep template that re-implements Salesforce's documented Opportunity / deal-management model as transparent, inspectable XanoScript — with a Kanban + forecast-dashboard frontend for a live demo.
Every Salesforce-mirroring behavior here is modeled from the public Salesforce Object Reference / Help documentation (cited below), not a live integration. There is no Salesforce account, OAuth, or API call anywhere in this project — it runs entirely on shipped seed data. It is an independent re-implementation for teaching and demo purposes, not affiliated with or endorsed by Salesforce.
Honesty caveat. Salesforce does not publish an exact default probability for every standard stage. The seed therefore uses Salesforce's documented example probability values (10 / 10 / 20 / … / 90 / 100 / 0) and says so — it never claims these are "Salesforce's defaults." Treat every number here as illustrative of the mechanics, not as authoritative Salesforce configuration.
npm install
npm run dev # run the frontend (Vite)The app opens on a login screen with a "Load demo data" button. It calls the
idempotent POST /seed endpoint (manager + 3 reps, the 10 standard stages,
accounts/contacts/leads, and ~20 deals) and hands you demo credentials. Log in as
the manager to see all records, or as a rep to see the owner-scoped view.
sidestep login # once, to authenticate against your Xano account
npm run build # build the static frontend
npm run xano:deploy # ship the backend + built frontend togethernpm run xano:exportcompiles the backend toworkspace.json(don't commit it).npm run typecheck/npm run buildmust stay green.
xano/
index.ts default-exported workspace() registering everything
lib.ts shared statement-stack helpers (scoping, ExpectedRevenue, day math)
tables/*.ts the 9 tables (each maps to a real Salesforce object)
api/groups.ts the 4 API groups (canonical slugs pinned)
api/auth.ts signup / login / me
api/crm-*.ts accounts, contacts, leads, stages, board, deals, contact-roles, activities
api/analytics.ts dashboard/stats + cumulative forecast
api/seed.ts idempotent demo seed
frontend/src/
lib/api.ts THE ONE CONTRACT — request paths + types derived from the query defs
App.tsx + components/ Kanban board, deal drawer, leads view, forecast dashboard
The one contract. frontend/src/lib/api.ts imports the sidestep query defs
and derives request paths (getPath()) and request/response types
(InferInput / InferResponse). Nothing in the UI hand-types a URL or a request
body — change a def and the frontend types follow.
| Table | Salesforce object | Notes |
|---|---|---|
user |
User + Collaborative Forecast quota | reps / managers, role + monthly/quarterly quota; the auth table |
account |
Account | |
contact |
Contact | AccountId lookup modeled as the account FK |
lead |
Lead + convertLead |
status, rating, source, is_converted + converted_*_id |
pipeline_stage |
OpportunityStage | sort order, default probability, forecast category, is_closed/is_won |
deal |
Opportunity | the pipeline record |
opportunity_contact_role |
OpportunityContactRole | deal↔contact junction, role + is_primary |
deal_stage_history |
OpportunityHistory | from/to stage, amount + probability snapshots, days-in-stage |
activity |
Task + Event | consolidated; WhatId→deal, WhoId→contact |
-
Stage-driven probability. Advancing a deal snapshots the stage's
default_probabilityontodeal.probability; a rep can override it. Source: OpportunityProbability— "implied, but not directly controlled, by StageName… you can override." (Opportunity object reference) -
ExpectedRevenue = Amount × Probability / 100, recomputed on any amount / probability / stage change. Source: Opportunity
ExpectedRevenue— "the product of the Amount field and the Probability." (Opportunity object reference) -
Forecast category from stage (overridable); the Closed Won stage maps to
Closedand the Closed Lost stage toOmitted. Source: the standard Stage-to-Forecast-Category mapping. (Forecast Categories) -
Cumulative forecast rollup (unweighted raw amounts):
Open Pipeline = Pipeline + BestCase + Commit,Best Case = BestCase + Commit + Closed,Commit = Commit + Closed,Closed Only = Closed— plus a separate weighted ExpectedRevenue total. Source: cumulative forecast rollup methods. (Cumulative Forecast Rollups) -
Guarded stage transition. No advancing a closed deal; a manager may skip stages, a rep may not (one stage at a time, forward only); every transition writes a
deal_stage_historyrow withdays_in_previous_stage. Source: OpportunityHistory + stage validation. (OpportunityHistory, Validation Rules) -
Won / Lost close-out. Moving to the
is_won/is_loststage setsis_closed/is_won, forces probability 100 / 0, and stampsactual_close_date; Lost requires a reason. Idempotent (a second call is a no-op). Source:IsClosed/IsWon— "directly controlled by StageName." (Opportunity object reference) -
LastActivityDate roll-up. The most recent of (latest event due date, latest closed task due date). Source: Opportunity
LastActivityDate. (Opportunity object reference) -
Kanban attention alerts — the three documented flags: an overdue task, no open activities, and no activity for 30 days. Source: the Opportunity Kanban alerts. (Work with Kanban)
-
Lead conversion. Creates/links an Account + Contact (and an optional Opportunity), then sets
is_convertedand theconverted_*_idback-references. Source: theconvertLeadAPI. (Database.convertLead) -
Quota attainment. Per rep:
closed-won ÷ quota → attainment %, bucketed into color bands (0 grey / 1–33 red / 34–66 orange / 67+ green). Source: Collaborative Forecast quotas. (Forecast quotas)
As with the seed probabilities, the exact band thresholds and the Kanban alert windows are illustrative choices consistent with the documented behaviors — they demonstrate the mechanic, not a byte-for-byte reproduction of a specific Salesforce org's configuration.
| Group | Endpoints |
|---|---|
auth (/api:auth) |
signup, login, me |
crm (/api:crm) |
accounts (+create), contacts (+create), leads (+create, lead_convert), stages, board, deals (+deal_get, deal_create, deal_update, deal_advance, deal_win, deal_lose), contact_roles (+add, set-primary), activities (+create, complete) |
analytics (/api:analytics) |
dashboard, forecast |
seed (/api:seed) |
seed (idempotent) |
Auth model. user is the auth table with role (rep | manager) and a
per-user quota. Reps are scoped to records they own; managers see everything.
The scoping is one shared helper (ownerScopedList in xano/lib.ts) applied
across every list endpoint, and deal mutations run through an owner/manager
access guard.
This backend is authored entirely against the sidestep package's own types:
node_modules/@sidestep/core/llms.txt— a compact tour of every builder.- The published TypeScript types and JSDoc (
node_modules/@sidestep/core/**/*.d.ts).