{"_id":"@3halves-labs/score-interactions","name":"@3halves-labs/score-interactions","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@3halves-labs/score-interactions","version":"0.1.0","description":"Embeddable renderer for loyalty interactions (surveys, polls, trivia, quizzes) — web component + React, on the score-interactions-core contract","license":"MIT","keywords":["loyalty","survey","poll","trivia","quiz","web-component","embeddable","widget"],"main":"dist/index.cjs.js","module":"dist/index.esm.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.esm.js","require":"./dist/index.cjs.js"},"./react":{"types":"./dist/react/index.d.ts","import":"./dist/react/index.esm.js","require":"./dist/react/index.cjs.js"}},"sideEffects":false,"scripts":{"build":"tsup","clean":"rimraf dist","dev":"tsup --watch","typecheck":"tsc --noEmit","prepublishOnly":"npm run clean && npm run build","test":"vitest run","test:watch":"vitest","release":"../scripts/release-sdk.sh interactions interactions","release:patch":"../scripts/release-sdk.sh interactions interactions patch","release:minor":"../scripts/release-sdk.sh interactions interactions minor","release:major":"../scripts/release-sdk.sh interactions interactions major"},"peerDependencies":{"react":">=17"},"peerDependenciesMeta":{"react":{"optional":true}},"devDependencies":{"@types/react":"^18.2.0","jsdom":"^25.0.1","react":"^18.2.0","rimraf":"^5.0.5","tsup":"^8.0.1","typescript":"^5.3.3","vitest":"^2.1.9"},"publishConfig":{"access":"public"},"_id":"@3halves-labs/score-interactions@0.1.0","gitHead":"385d6459aff08fa4032d9e3ea2ef06fffb2cbb08","_nodeVersion":"18.20.4","_npmVersion":"10.7.0","dist":{"integrity":"sha512-0SHHCe4xSbOrDErAR+mc/rxuN6DTscq12zwRgtNcsh8zjhvsipm7GOb+fFDR362l+JxUYHbNm/qt095h+LqGsg==","shasum":"b443bea2c2d1dafd8310bac73f65f9f344b7b0ac","tarball":"https://registry.npmjs.org/@3halves-labs/score-interactions/-/score-interactions-0.1.0.tgz","fileCount":14,"unpackedSize":211496,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCp9P47Dp32Aw/GY/qiEbadt+qPOOoiOVrsyiej6rdDEAIhAPleJ39E4L3wKb+DohEcjOxlVEA0Oq2bZakwKIx4ZV89"}]},"_npmUser":{"name":"3halves-labs","email":"info@3halves-labs.com"},"directories":{},"maintainers":[{"name":"3halves-labs","email":"info@3halves-labs.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/score-interactions_0.1.0_1787077732704_0.707709496810478"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-18T18:28:52.333Z","0.1.0":"2026-08-18T18:28:52.855Z","modified":"2026-08-18T18:28:53.241Z"},"maintainers":[{"name":"3halves-labs","email":"info@3halves-labs.com"}],"description":"Embeddable renderer for loyalty interactions (surveys, polls, trivia, quizzes) — web component + React, on the score-interactions-core contract","keywords":["loyalty","survey","poll","trivia","quiz","web-component","embeddable","widget"],"license":"MIT","readme":"# @3halves-labs/score-interactions\n\nEmbeddable renderer for loyalty **interactions** — surveys, polls, trivia and quizzes. Drop one on any page and let URC style it with their own CSS/Tailwind. It renders the **fan shape** served by the S3 middleware (`GET /api/interactions/:id`) — correct answers withheld.\n\nPart of epic **#304** / tickets **#310 / #320**. Odoo is the source of truth; the widget reads a published definition and submits responses through the headless middleware API (S3, #307). Scoring is **server-authoritative** — the widget never grades (the fan shape carries no correct answers).\n\n## Three ways to embed\n\n### 1. Web component (script-tag drop-in)\n```html\n<script type=\"module\" src=\"https://…/score-interactions.esm.js\"></script>\n<script type=\"module\">\n  import { defineInteractionElement } from '@3halves-labs/score-interactions';\n  defineInteractionElement();\n</script>\n\n<score-interaction\n  interaction-id=\"rapid-fire-r12\"\n  endpoint=\"https://api.urc.anything-loyalty.3halves-labs.com\">\n</score-interaction>\n```\nRenders in a shadow root (style isolation). Listen for completion:\n```js\ndocument.querySelector('score-interaction')\n  .addEventListener('complete', (e) => console.log(e.detail)); // { accepted, result }\n```\n\n### 2. React\n```tsx\nimport { InteractionWidget } from '@3halves-labs/score-interactions/react';\nimport { createHttpClient } from '@3halves-labs/score-interactions';\n\nconst client = createHttpClient({\n  endpoint: 'https://api.urc.anything-loyalty.3halves-labs.com',\n  getToken: () => auth0.getTokenSilently(), // fan Auth0 token\n});\n\n<InteractionWidget definition={def} client={client} className=\"my-tailwind-wrapper\"\n                   onComplete={(r) => toast(`+${r.result?.total ?? 0} pts`)} />\n```\n\n### 3. Headless (bring your own UI)\n```ts\nimport { createHttpClient } from '@3halves-labs/score-interactions';\nconst client = createHttpClient({ endpoint });\nconst def = await client.getDefinition('rapid-fire-r12');\n// …render your own DOM, collect `answers`…\nconst { accepted, result } = await client.submitResponse(def.id, answers);\n```\n\n## Trivia (timed, one-at-a-time) — #312\n\nFor a paced trivia game, use `renderTrivia` instead of `renderInteraction`: it shows **one question at a time** with a per-question **countdown** (`timeLimit` seconds), auto-advances on answer or timeout, submits the collected answers at the end (server-scored), and shows the result + a **leaderboard** (via `client.getResults`).\n\n```ts\nimport { renderTrivia, createHttpClient } from '@3halves-labs/score-interactions';\nconst client = createHttpClient({ endpoint, getToken });\nconst def = await client.getDefinition('rapid-fire-trivia');\nrenderTrivia(document.getElementById('trivia')!, def, { client, showLeaderboard: true });\n```\nExtra styling hooks: `si-trivia · si-progress · si-timer · si-leaderboard · si-leaderboard-row`.\n\n## Styling — three levels\n\nThe renderer emits **no colours inline**. Every element exposes three hooks:\n\n| Hook | Example |\n| --- | --- |\n| CSS variables (pierce shadow DOM) | `score-interaction { --si-primary: #e4002b; --si-radius: 4px; }` |\n| `::part(...)` (web component) | `score-interaction::part(submit) { text-transform: uppercase; }` |\n| `.si-*` classes (React / light DOM) | `.si-question { @apply rounded-xl border p-4; }` |\n\nParts/classes: `root · header · title · description · questions · question · prompt · help · options · option · input · rating · rating-star · error · submit · result`. Skip the default theme with the `no-default-theme` attribute (web component) or by not passing `useDefaultTheme` (React).\n\n## Auth & events\n\nPass a `getToken` to `createHttpClient` (a fan Auth0 token) — sent as `Authorization: Bearer`. Pair with `@3halves-labs/score-tracker` on the host page to emit `survey_completed` / `trivia_completed` earning events (points flow via the Odoo tracking-rule path).\n\n## Dev / testing\n\n`createMockClient({ [id]: def })` serves fan definitions (indexed by record-key, id, and gameId) and returns a canned result — for offline dev, previews, and tests. It doesn't grade (the fan shape has no answer key; the server scores).\n\n## Notes\n\n- Zero framework deps in the core renderer; `react` is an optional peer used only by `/react`.\n- Self-contained (zero runtime deps); the web component base class is SSR/Node-safe.\n- Wired to the live S3 API (#307); `createMockClient` / an injected `.definition` for offline dev.\n","readmeFilename":"README.md","_rev":"1-171f7e80eb87f72d429b80e79a76d9f3"}