@zecimo/websdk
v0.1.4
Published
zecimo Web SDK — embed pontaj și alte widget-uri
Maintainers
Readme
@zecimo/websdk
JavaScript SDK pentru integrarea widget-urilor zecimo în site-uri externe (iframe-based).
Expune două widget-uri de pontaj, ambele scriind prin aceeași logică de backend ca aplicația principală (HrTimesheetService), deci rezultatul în DB este identic:
timesheet— varianta pentru contabil: grilă pe taburi, selector de lună</>.employerTimesheet— Pontaj Angajator: wizard ghidat în 7 pași pentru administratorul firmei (listă de pontaje → completare pas-cu-pas pentru toți angajații → trimitere → rezumat read-only).
Restul vor urma (payroll, employees, etc.).
Instalare
Via script tag (recomandat pentru integrări simple):
<script src="https://embed.zecimo.ro/web-sdk.js"></script>Sau via npm:
pnpm add @zecimo/websdkimport { zecimoSdk } from '@zecimo/websdk';Flow-ul de integrare
[backend client] ──POST /developer/embed/session──> { sessionToken }
(header: X-API-Key)
[frontend client] ──new zecimoSdk().timesheet({ sessionToken, elementId })─────────> iframe (contabil)
──new zecimoSdk().employerTimesheet({ sessionToken, elementId })──> iframe (angajator)Același sessionToken funcționează pentru ambele widget-uri.
1. Obține o cheie API (o dată)
Un admin de workspace creează cheia din Admin → Developer → API Keys. Permisiune necesară: developer.api-keys.manage.
2. Backend: schimbă cheia API contra unui session token
Session tokenul este un JWT legat de (workspaceId, companyId), valabil 12h (configurabil pe server prin EMBED_SESSION_EXPIRY_HOURS). Luna se alege din UI.
curl -X POST https://api.zecimo.ro/developer/embed/session \
-H 'X-API-Key: prod_…' \
-H 'Content-Type: application/json' \
-d '{ "cui": "RO12345678" }'Răspuns:
{
"data": {
"sessionToken": "eyJhbGc…",
"expiresAt": "2026-05-06T12:34:56.000Z"
}
}Erori posibile: 401 (cheie API invalidă), 404 (CUI inexistent în workspace).
3. Frontend: montează widget-ul
<div id="zecimo-timesheet"></div>
<script src="https://embed.zecimo.ro/web-sdk.js"></script>
<script>
const sdk = new zecimoSdk();
const widget = sdk.timesheet({
sessionToken: '<din pasul 2>',
elementId: 'zecimo-timesheet',
onEvent: e => console.log(e.type, e.payload),
});
// mai târziu:
// widget.unmount();
</script>Pentru varianta angajator, schimbă doar metoda — opțiunile sunt identice:
const widget = sdk.employerTimesheet({
sessionToken: '<din pasul 2>',
elementId: 'zecimo-timesheet',
onEvent: e => console.log(e.type, e.payload),
});Înălțime
Implicit, widget-ul se redimensionează automat după conținut. Dacă vrei o înălțime fixă (widget-ul umple spațiul și derulează intern), pasează height:
// umple containerul (care trebuie să aibă o înălțime definită)
sdk.employerTimesheet({ sessionToken, elementId: 'zecimo-timesheet', height: '100%' });
// înălțime fixă în px
sdk.employerTimesheet({ sessionToken, elementId: 'zecimo-timesheet', height: 600 });CSS personalizat
Iframe-ul e cross-origin, deci CSS-ul din pagina gazdă nu îl afectează. Pasează customCss ca să injectezi stiluri în widget — de exemplu să mărești font-size-ul general:
sdk.employerTimesheet({
sessionToken,
elementId: 'zecimo-timesheet',
customCss: 'html { font-size: 16px }',
});API
new zecimoSdk(options?)
| Opțiune | Tip | Default | Descriere |
| -------- | -------- | ------------------------- | ------------------------------------------------------------ |
| sdkUrl | string | https://embed.zecimo.ro | Origin-ul aplicației de embed (override pentru dev/staging). |
sdk.timesheet(options): EmbeddedWidget
Montează widget-ul de pontaj într-un container din pagina părinte.
| Opțiune | Tip | Obligatoriu | Descriere |
| -------------- | -------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| sessionToken | string | da | Token-ul obținut de la POST /developer/embed/session. |
| elementId | string | da | ID-ul <div>-ului care va găzdui iframe-ul. |
| height | number \| string | nu | Înălțime fixă a iframe-ului. Implicit: auto-resize la conținut (min 450px). number → px (600 → 600px); string → valoare CSS ('100%', '80vh', '600px'). La înălțime fixă, auto-resize-ul e dezactivat, widget-ul umple spațiul (footer fix + scroll intern). Pentru '100%', containerul (elementId) trebuie să aibă o înălțime definită. |
| customCss | string | nu | CSS injectat în documentul din iframe (host-ul nu poate stila direct iframe-ul cross-origin). Aplicat ca <style> la finalul <head>, deci suprascrie stilurile widget-ului la specificitate egală. Ex: 'html { font-size: 16px }' mărește font-size-ul general. |
| onEvent | (e: zecimoWidgetEvent) => void | nu | Callback pentru evenimente — vezi mai jos. |
Returnează un handle:
interface EmbeddedWidget {
unmount(): void;
}sdk.employerTimesheet(options): EmbeddedWidget
Montează wizard-ul Pontaj Angajator (varianta prietenoasă, pentru administratorul firmei). Acceptă exact aceleași opțiuni ca sdk.timesheet (sessionToken, elementId, height, onEvent) și returnează același handle EmbeddedWidget.
Evenimente
type zecimoWidgetEvent =
| { type: 'ready' }
| { type: 'resize'; payload: { height: number } }
| { type: 'saved' }
| { type: 'submitted' }
| { type: 'error'; payload: { message: string } };| Eveniment | Când se emite |
| ----------- | ----------------------------------------------------------------------------------------------------------------------- |
| ready | După prima încărcare reușită a pontajului. |
| resize | La fiecare schimbare de înălțime — doar în modul auto-resize (când nu se pasează height). SDK-ul ajustează iframe-ul. |
| saved | Pontajul a fost salvat ca draft (la timesheet manual; la employerTimesheet automat, la fiecare schimbare de pas). |
| submitted | Utilizatorul a închis luna (status → submitted). |
| error | Eșec la încărcare sau autentificare. |
Comportamentul widget-urilor
timesheet (contabil)
- Selector de lună integrat — utilizatorul navighează între luni cu
</>. - Default este luna trecută (cazul tipic pentru pontaj).
- Auto-create — dacă nu există pontaj pentru luna selectată, se creează automat la prima încărcare.
- Editabil până la închidere. La
status === 'submitted', formularul devine read-only și butonul Închide dispare. - Salvează și Închide sunt expuse în header. Reopen-ul nu este disponibil în embed (acțiune de admin în aplicația principală).
employerTimesheet (angajator)
- Ecran de start cu lista pontajelor existente pe luni + un card pentru luna în curs (default).
- Wizard în 7 pași: Prezență (click pe o zi → alegi statusul), Concedii medicale, Ore suplimentare, Tichete, Beneficii, Persoane în întreținere, Verificare.
- Pornește de la ideea că toți au lucrat normal — angajatorul marchează doar excepțiile.
- Validare blocantă — nu se poate trece la pasul următor până nu e complet/confirmat.
- Auto-create la deschiderea unei luni; autosave (
status: 'saved') la fiecare schimbare de pas — emitesaved. - După Trimite (
status: 'submitted'), pontajul e închis: se afișează un rezumat read-only și nu mai poate fi editat sau redeschis din embed.
Dezvoltare locală
nx run web-sdk:build # bundle ESM/CJS/IIFE + declarații TS
nx run sdk:serve # rulează aplicația host pe :4501Pentru un test cross-origin rapid, apps/sdk/public/test.html permite paste-ul unui token și montează oricare widget direct (butoanele Mount (contabil) / Mount (angajator)).
Documentație extinsă
Vezi docs/embed-sdk.md pentru flow-ul complet, inclusiv exemple de eroare și semantica auditării.
