@bpmnkit/user-tasks 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,160 @@
1
+ <div align="center">
2
+ <a href="https://bpmnkit.com"><img src="https://bpmnkit.com/favicon.svg" width="72" height="72" alt="BPMN Kit logo"></a>
3
+ <h1>@bpmnkit/user-tasks</h1>
4
+ <p>Embeddable user task widget for Camunda 8 — form rendering, claim/complete actions, zero dependencies</p>
5
+
6
+ [![npm](https://img.shields.io/npm/v/@bpmnkit/user-tasks?style=flat-square&color=6244d7)](https://www.npmjs.com/package/@bpmnkit/user-tasks)
7
+ [![license](https://img.shields.io/npm/l/@bpmnkit/user-tasks?style=flat-square)](https://github.com/bpmnkit/monorepo/blob/main/LICENSE)
8
+ [![typescript](https://img.shields.io/badge/TypeScript-strict-6244d7?style=flat-square&logo=typescript&logoColor=white)](https://github.com/bpmnkit/monorepo)
9
+
10
+ [Website](https://bpmnkit.com) · [Documentation](https://docs.bpmnkit.com) · [GitHub](https://github.com/bpmnkit/monorepo) · [Changelog](https://github.com/bpmnkit/monorepo/blob/main/packages/user-tasks/CHANGELOG.md)
11
+ </div>
12
+
13
+ ---
14
+
15
+ ## Overview
16
+
17
+ `@bpmnkit/user-tasks` is a zero-dependency widget for rendering and interacting with Camunda 8 user tasks. Mount it into any HTML element to get a complete task UI — form rendering via `@bpmnkit/plugins/form-viewer`, claim/unclaim, complete, and optional reject actions.
18
+
19
+ It connects to the `@bpmnkit/proxy` local server to fetch task forms and submit completions via the Camunda REST API.
20
+
21
+ ## Features
22
+
23
+ - **Form rendering** — loads the task's linked Camunda Form and renders it via `@bpmnkit/plugins/form-viewer`
24
+ - **Claim / Unclaim** — assigns or removes the task assignee
25
+ - **Complete** — submits collected form variables to the Camunda API
26
+ - **Reject** — optional reject/return action with a reason prompt
27
+ - **Metadata display** — assignee, due date (with overdue highlight), and priority
28
+ - **Theme support** — `light`, `dark`, or `neon` via `@bpmnkit/ui` design tokens
29
+ - **Zero dependencies** — no framework required; mounts into any `HTMLElement`
30
+
31
+ ## Installation
32
+
33
+ ```sh
34
+ npm install @bpmnkit/user-tasks @bpmnkit/proxy
35
+ ```
36
+
37
+ ## Quick Start
38
+
39
+ ```typescript
40
+ import { createUserTaskWidget } from "@bpmnkit/user-tasks"
41
+
42
+ const widget = createUserTaskWidget({
43
+ container: document.getElementById("task-panel")!,
44
+ task: {
45
+ userTaskKey: "2251799813685281",
46
+ name: "Review order",
47
+ assignee: "alice",
48
+ dueDate: "2025-06-01T12:00:00Z",
49
+ priority: 50,
50
+ },
51
+ proxyUrl: "http://localhost:3033", // default
52
+ theme: "dark",
53
+ onComplete(variables) {
54
+ console.log("Task completed with", variables)
55
+ },
56
+ onClaim() {
57
+ console.log("Task claimed")
58
+ },
59
+ onUnclaim() {
60
+ console.log("Task unclaimed")
61
+ },
62
+ onReject(reason) {
63
+ console.log("Task rejected:", reason)
64
+ },
65
+ })
66
+
67
+ // Later: update the displayed task
68
+ widget.setTask({ userTaskKey: "2251799813685999", name: "Approve invoice" })
69
+
70
+ // Clean up
71
+ widget.destroy()
72
+ ```
73
+
74
+ ## API Reference
75
+
76
+ ### `createUserTaskWidget(options)`
77
+
78
+ ```typescript
79
+ interface UserTaskWidgetOptions {
80
+ /** The container element to render the widget into. */
81
+ container: HTMLElement
82
+ /** The user task to display. */
83
+ task: UserTask
84
+ /** Base URL of the proxy server. Default: "http://localhost:3033" */
85
+ proxyUrl?: string
86
+ /** Active profile name for x-profile header. */
87
+ profile?: string | null
88
+ /** Visual theme. Default: "neon" */
89
+ theme?: "light" | "dark" | "neon"
90
+ /** Called when the user completes the task. */
91
+ onComplete(variables: Record<string, unknown>): void
92
+ /** Called when the user claims the task. */
93
+ onClaim(): void
94
+ /** Called when the user unclaims the task. */
95
+ onUnclaim(): void
96
+ /** Called when the user rejects/returns the task. Optional — hides the Reject button if omitted. */
97
+ onReject?(reason: string): void
98
+ }
99
+ ```
100
+
101
+ Returns a `UserTaskWidgetApi`:
102
+
103
+ ```typescript
104
+ interface UserTaskWidgetApi {
105
+ /** Update the displayed task and reload its form. */
106
+ setTask(task: UserTask): void
107
+ /** Remove the widget from the DOM and clean up. */
108
+ destroy(): void
109
+ }
110
+ ```
111
+
112
+ ### `UserTask`
113
+
114
+ ```typescript
115
+ interface UserTask {
116
+ userTaskKey: string
117
+ name?: string
118
+ assignee?: string
119
+ candidateGroups?: string[]
120
+ dueDate?: string // ISO 8601 date string
121
+ priority?: number
122
+ processInstanceKey?: string
123
+ processDefinitionKey?: string
124
+ formKey?: string
125
+ }
126
+ ```
127
+
128
+ ---
129
+
130
+ ## Related Packages
131
+
132
+ | Package | Description |
133
+ |---------|-------------|
134
+ | [`@bpmnkit/core`](https://www.npmjs.com/package/@bpmnkit/core) | BPMN/DMN/Form parser, builder, layout engine |
135
+ | [`@bpmnkit/canvas`](https://www.npmjs.com/package/@bpmnkit/canvas) | Zero-dependency SVG BPMN viewer |
136
+ | [`@bpmnkit/editor`](https://www.npmjs.com/package/@bpmnkit/editor) | Full-featured interactive BPMN editor |
137
+ | [`@bpmnkit/engine`](https://www.npmjs.com/package/@bpmnkit/engine) | Lightweight BPMN process execution engine |
138
+ | [`@bpmnkit/feel`](https://www.npmjs.com/package/@bpmnkit/feel) | FEEL expression language parser & evaluator |
139
+ | [`@bpmnkit/plugins`](https://www.npmjs.com/package/@bpmnkit/plugins) | 22 composable canvas plugins |
140
+ | [`@bpmnkit/operate`](https://www.npmjs.com/package/@bpmnkit/operate) | Monitoring and operations frontend for Camunda 8 |
141
+ | [`@bpmnkit/api`](https://www.npmjs.com/package/@bpmnkit/api) | Camunda 8 REST API TypeScript client |
142
+ | [`@bpmnkit/ascii`](https://www.npmjs.com/package/@bpmnkit/ascii) | Render BPMN diagrams as Unicode ASCII art |
143
+ | [`@bpmnkit/ui`](https://www.npmjs.com/package/@bpmnkit/ui) | Shared design tokens and UI components |
144
+ | [`@bpmnkit/profiles`](https://www.npmjs.com/package/@bpmnkit/profiles) | Shared auth, profile storage, and client factories for CLI & proxy |
145
+ | [`@bpmnkit/connector-gen`](https://www.npmjs.com/package/@bpmnkit/connector-gen) | Generate connector templates from OpenAPI specs |
146
+ | [`@bpmnkit/cli`](https://www.npmjs.com/package/@bpmnkit/cli) | Camunda 8 command-line interface (casen) |
147
+ | [`@bpmnkit/proxy`](https://www.npmjs.com/package/@bpmnkit/proxy) | Local AI bridge and Camunda API proxy server |
148
+ | [`@bpmnkit/cli-sdk`](https://www.npmjs.com/package/@bpmnkit/cli-sdk) | Plugin authoring SDK for the casen CLI |
149
+ | [`@bpmnkit/create-casen-plugin`](https://www.npmjs.com/package/@bpmnkit/create-casen-plugin) | Scaffold a new casen CLI plugin in seconds |
150
+ | [`@bpmnkit/casen-report`](https://www.npmjs.com/package/@bpmnkit/casen-report) | HTML reports from Camunda 8 incident and SLA data |
151
+ | [`@bpmnkit/casen-worker-http`](https://www.npmjs.com/package/@bpmnkit/casen-worker-http) | Example HTTP worker plugin — completes jobs with live JSONPlaceholder API data |
152
+ | [`@bpmnkit/casen-worker-ai`](https://www.npmjs.com/package/@bpmnkit/casen-worker-ai) | AI task worker — classify, summarize, extract, and decide using Claude |
153
+
154
+ ## License
155
+
156
+ [MIT](https://github.com/bpmnkit/monorepo/blob/main/LICENSE) © BPMN Kit — made by [u11g](https://u11g.com)
157
+
158
+ <div align="center">
159
+ <a href="https://bpmnkit.com"><img src="https://bpmnkit.com/favicon.svg" width="32" height="32" alt="BPMN Kit"></a>
160
+ </div>
@@ -0,0 +1,11 @@
1
+ interface TaskActionOptions {
2
+ proxyUrl: string;
3
+ profile: string | null;
4
+ taskKey: string;
5
+ }
6
+ export declare function claimTask(options: TaskActionOptions, assignee: string): Promise<void>;
7
+ export declare function unclaimTask(options: TaskActionOptions): Promise<void>;
8
+ export declare function completeTask(options: TaskActionOptions, variables: Record<string, unknown>): Promise<void>;
9
+ export declare function fetchTaskForm(proxyUrl: string, profile: string | null, taskKey: string): Promise<unknown>;
10
+ export {};
11
+ //# sourceMappingURL=actions.d.ts.map
@@ -0,0 +1,49 @@
1
+ function buildHeaders(profile) {
2
+ const headers = {
3
+ "content-type": "application/json",
4
+ accept: "application/json",
5
+ };
6
+ if (profile)
7
+ headers["x-profile"] = profile;
8
+ return headers;
9
+ }
10
+ async function proxyPost(proxyUrl, path, profile, body) {
11
+ const res = await fetch(`${proxyUrl}${path}`, {
12
+ method: "POST",
13
+ headers: buildHeaders(profile),
14
+ body: body !== undefined ? JSON.stringify(body) : undefined,
15
+ });
16
+ if (!res.ok) {
17
+ const text = await res.text().catch(() => "");
18
+ throw new Error(`HTTP ${res.status}${text ? `: ${text}` : ""}`);
19
+ }
20
+ }
21
+ async function proxyDelete(proxyUrl, path, profile) {
22
+ const res = await fetch(`${proxyUrl}${path}`, {
23
+ method: "DELETE",
24
+ headers: buildHeaders(profile),
25
+ });
26
+ if (!res.ok) {
27
+ const text = await res.text().catch(() => "");
28
+ throw new Error(`HTTP ${res.status}${text ? `: ${text}` : ""}`);
29
+ }
30
+ }
31
+ export async function claimTask(options, assignee) {
32
+ return proxyPost(options.proxyUrl, `/api/user-tasks/${options.taskKey}/assignment`, options.profile, { assignee });
33
+ }
34
+ export async function unclaimTask(options) {
35
+ return proxyDelete(options.proxyUrl, `/api/user-tasks/${options.taskKey}/assignment`, options.profile);
36
+ }
37
+ export async function completeTask(options, variables) {
38
+ return proxyPost(options.proxyUrl, `/api/user-tasks/${options.taskKey}/completion`, options.profile, { variables });
39
+ }
40
+ export async function fetchTaskForm(proxyUrl, profile, taskKey) {
41
+ const headers = { accept: "application/json" };
42
+ if (profile)
43
+ headers["x-profile"] = profile;
44
+ const res = await fetch(`${proxyUrl}/api/user-tasks/${taskKey}/form`, { headers });
45
+ if (!res.ok)
46
+ throw new Error(`HTTP ${res.status}`);
47
+ return res.json();
48
+ }
49
+ //# sourceMappingURL=actions.js.map
@@ -0,0 +1,3 @@
1
+ export { createUserTaskWidget } from "./widget.js";
2
+ export type { UserTask, UserTaskWidgetApi, UserTaskWidgetOptions } from "./types.js";
3
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { createUserTaskWidget } from "./widget.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,39 @@
1
+ import type { Theme } from "@bpmnkit/ui";
2
+ export interface UserTask {
3
+ userTaskKey: string;
4
+ name?: string;
5
+ assignee?: string;
6
+ candidateGroups?: string[];
7
+ dueDate?: string;
8
+ priority?: number;
9
+ processInstanceKey?: string;
10
+ processDefinitionKey?: string;
11
+ formKey?: string;
12
+ }
13
+ export interface UserTaskWidgetOptions {
14
+ /** The container element to render the widget into. */
15
+ container: HTMLElement;
16
+ /** The user task to display. */
17
+ task: UserTask;
18
+ /** Base URL of the proxy server. */
19
+ proxyUrl?: string;
20
+ /** Active profile name for x-profile header. */
21
+ profile?: string | null;
22
+ /** Visual theme. Defaults to "neon". */
23
+ theme?: Theme;
24
+ /** Called when the user completes the task. */
25
+ onComplete(variables: Record<string, unknown>): void;
26
+ /** Called when the user claims the task. */
27
+ onClaim(): void;
28
+ /** Called when the user unclaims the task. */
29
+ onUnclaim(): void;
30
+ /** Called when the user rejects/returns the task. */
31
+ onReject?(reason: string): void;
32
+ }
33
+ export interface UserTaskWidgetApi {
34
+ /** Update the displayed task. */
35
+ setTask(task: UserTask): void;
36
+ /** Remove the widget from the DOM and clean up. */
37
+ destroy(): void;
38
+ }
39
+ //# sourceMappingURL=types.d.ts.map
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,3 @@
1
+ import type { UserTaskWidgetApi, UserTaskWidgetOptions } from "./types.js";
2
+ export declare function createUserTaskWidget(options: UserTaskWidgetOptions): UserTaskWidgetApi;
3
+ //# sourceMappingURL=widget.d.ts.map
package/dist/widget.js ADDED
@@ -0,0 +1,228 @@
1
+ import { FormViewer } from "@bpmnkit/plugins/form-viewer";
2
+ import { applyTheme, injectUiStyles } from "@bpmnkit/ui";
3
+ import { claimTask, completeTask, fetchTaskForm, unclaimTask } from "./actions.js";
4
+ function el(tag, className, text) {
5
+ const e = document.createElement(tag);
6
+ e.className = className;
7
+ if (text !== undefined)
8
+ e.textContent = text;
9
+ return e;
10
+ }
11
+ const WIDGET_CSS = `
12
+ .ut-root { font-family: var(--bpmnkit-font, system-ui, sans-serif); color: var(--bpmnkit-fg, #1a1a2e); }
13
+ .ut-header { padding: 16px; border-bottom: 1px solid var(--bpmnkit-border, #d0d0e8); }
14
+ .ut-name { font-size: 18px; font-weight: 600; margin-bottom: 8px; }
15
+ .ut-meta { display: flex; gap: 12px; font-size: 13px; color: var(--bpmnkit-fg-muted, #6666a0); }
16
+ .ut-meta-item { display: flex; align-items: center; gap: 4px; }
17
+ .ut-overdue { color: var(--bpmnkit-danger, #dc2626); }
18
+ .ut-form { padding: 16px; flex: 1; overflow-y: auto; }
19
+ .ut-form-placeholder { padding: 32px; text-align: center; color: var(--bpmnkit-fg-muted, #6666a0); font-size: 14px; }
20
+ .ut-actions { display: flex; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--bpmnkit-border, #d0d0e8); }
21
+ .ut-btn { padding: 6px 14px; border-radius: 6px; font-size: 13px; cursor: pointer; border: 1px solid transparent; transition: opacity 0.15s; }
22
+ .ut-btn:hover { opacity: 0.85; }
23
+ .ut-btn:disabled { opacity: 0.4; cursor: not-allowed; }
24
+ .ut-btn-primary { background: var(--bpmnkit-accent, #1a56db); color: var(--bpmnkit-accent-fg, #fff); }
25
+ .ut-btn-secondary { background: transparent; border-color: var(--bpmnkit-border, #d0d0e8); color: var(--bpmnkit-fg, #1a1a2e); }
26
+ .ut-btn-danger { background: var(--bpmnkit-danger, #dc2626); color: #fff; }
27
+ .ut-error { padding: 8px 12px; background: color-mix(in srgb, var(--bpmnkit-danger, #dc2626) 15%, transparent); color: var(--bpmnkit-danger, #dc2626); border-radius: 4px; font-size: 13px; margin: 0 16px 8px; }
28
+ `;
29
+ function injectWidgetStyles() {
30
+ const id = "bpmnkit-user-tasks-css";
31
+ if (document.getElementById(id))
32
+ return;
33
+ const style = document.createElement("style");
34
+ style.id = id;
35
+ style.textContent = WIDGET_CSS;
36
+ document.head.appendChild(style);
37
+ }
38
+ export function createUserTaskWidget(options) {
39
+ injectUiStyles();
40
+ injectWidgetStyles();
41
+ const { container, proxyUrl = "http://localhost:3033", profile = null, theme = "neon", onComplete, onClaim, onUnclaim, onReject, } = options;
42
+ // Create root element
43
+ const root = el("div", "ut-root");
44
+ applyTheme(root, theme);
45
+ root.style.display = "flex";
46
+ root.style.flexDirection = "column";
47
+ root.style.height = "100%";
48
+ container.appendChild(root);
49
+ // State
50
+ let currentTask = options.task;
51
+ let formViewer = null;
52
+ let formVariables = {};
53
+ // Error area
54
+ const errorEl = el("div", "ut-error");
55
+ errorEl.style.display = "none";
56
+ // Header
57
+ const header = el("div", "ut-header");
58
+ const nameEl = el("div", "ut-name");
59
+ const metaEl = el("div", "ut-meta");
60
+ header.appendChild(nameEl);
61
+ header.appendChild(metaEl);
62
+ root.appendChild(header);
63
+ root.appendChild(errorEl);
64
+ // Form area
65
+ const formArea = el("div", "ut-form");
66
+ const formContainer = el("div", "");
67
+ formArea.appendChild(formContainer);
68
+ root.appendChild(formArea);
69
+ // Actions
70
+ const actionsEl = el("div", "ut-actions");
71
+ const claimBtn = el("button", "ut-btn ut-btn-secondary", "Claim");
72
+ claimBtn.type = "button";
73
+ const unclaimBtn = el("button", "ut-btn ut-btn-secondary", "Unclaim");
74
+ unclaimBtn.type = "button";
75
+ const completeBtn = el("button", "ut-btn ut-btn-primary", "Complete");
76
+ completeBtn.type = "button";
77
+ const rejectBtn = el("button", "ut-btn ut-btn-danger", "Reject");
78
+ rejectBtn.type = "button";
79
+ if (!onReject)
80
+ rejectBtn.style.display = "none";
81
+ actionsEl.appendChild(claimBtn);
82
+ actionsEl.appendChild(unclaimBtn);
83
+ actionsEl.appendChild(completeBtn);
84
+ if (onReject)
85
+ actionsEl.appendChild(rejectBtn);
86
+ root.appendChild(actionsEl);
87
+ function showError(msg) {
88
+ errorEl.textContent = msg;
89
+ errorEl.style.display = "block";
90
+ }
91
+ function clearError() {
92
+ errorEl.style.display = "none";
93
+ }
94
+ function renderTask(task) {
95
+ nameEl.textContent = task.name ?? `Task ${task.userTaskKey}`;
96
+ metaEl.innerHTML = "";
97
+ if (task.assignee) {
98
+ const assigneeItem = el("span", "ut-meta-item");
99
+ assigneeItem.textContent = `Assigned to ${task.assignee}`;
100
+ metaEl.appendChild(assigneeItem);
101
+ }
102
+ if (task.dueDate) {
103
+ const due = new Date(task.dueDate);
104
+ const overdue = due < new Date();
105
+ const dueItem = el("span", overdue ? "ut-meta-item ut-overdue" : "ut-meta-item");
106
+ dueItem.textContent = `Due: ${due.toLocaleDateString()}${overdue ? " (overdue)" : ""}`;
107
+ metaEl.appendChild(dueItem);
108
+ }
109
+ if (task.priority !== undefined) {
110
+ const priorityItem = el("span", "ut-meta-item");
111
+ priorityItem.textContent = `Priority: ${task.priority}`;
112
+ metaEl.appendChild(priorityItem);
113
+ }
114
+ // Update button states
115
+ claimBtn.disabled = !!task.assignee;
116
+ unclaimBtn.disabled = !task.assignee;
117
+ // Load form
118
+ void loadForm(task);
119
+ }
120
+ async function loadForm(task) {
121
+ // Clean up existing viewer
122
+ if (formViewer) {
123
+ formViewer.destroy();
124
+ formViewer = null;
125
+ }
126
+ formContainer.innerHTML = "";
127
+ try {
128
+ const formData = await fetchTaskForm(proxyUrl, profile, task.userTaskKey);
129
+ // Camunda v2 returns { schema: Record<string,unknown>, ... }
130
+ // Some versions send schema as a JSON string instead of an object
131
+ let formDef = formData;
132
+ if (formData && typeof formData === "object" && "schema" in formData) {
133
+ const raw = formData.schema;
134
+ if (raw && typeof raw === "object") {
135
+ formDef = raw;
136
+ }
137
+ else if (typeof raw === "string") {
138
+ try {
139
+ formDef = JSON.parse(raw);
140
+ }
141
+ catch {
142
+ formDef = null;
143
+ }
144
+ }
145
+ }
146
+ if (formDef && typeof formDef === "object" && "components" in formDef) {
147
+ const fvTheme = theme === "light" ? "light" : "dark";
148
+ formViewer = new FormViewer({ container: formContainer, theme: fvTheme });
149
+ formViewer.load(formDef);
150
+ }
151
+ else {
152
+ const placeholder = el("div", "ut-form-placeholder", "No form associated with this task.");
153
+ formContainer.appendChild(placeholder);
154
+ }
155
+ }
156
+ catch {
157
+ const placeholder = el("div", "ut-form-placeholder", "No form schema found or form is embedded in the process.");
158
+ formContainer.appendChild(placeholder);
159
+ }
160
+ }
161
+ // Event handlers
162
+ claimBtn.addEventListener("click", () => {
163
+ claimBtn.disabled = true;
164
+ claimBtn.textContent = "Claiming...";
165
+ claimTask({ proxyUrl, profile, taskKey: currentTask.userTaskKey }, "studio-user")
166
+ .then(() => {
167
+ clearError();
168
+ onClaim();
169
+ })
170
+ .catch((err) => {
171
+ showError(`Claim failed: ${err instanceof Error ? err.message : String(err)}`);
172
+ })
173
+ .finally(() => {
174
+ claimBtn.textContent = "Claim";
175
+ });
176
+ });
177
+ unclaimBtn.addEventListener("click", () => {
178
+ unclaimBtn.disabled = true;
179
+ unclaimBtn.textContent = "Unclaiming...";
180
+ unclaimTask({ proxyUrl, profile, taskKey: currentTask.userTaskKey })
181
+ .then(() => {
182
+ clearError();
183
+ onUnclaim();
184
+ })
185
+ .catch((err) => {
186
+ showError(`Unclaim failed: ${err instanceof Error ? err.message : String(err)}`);
187
+ })
188
+ .finally(() => {
189
+ unclaimBtn.textContent = "Unclaim";
190
+ });
191
+ });
192
+ completeBtn.addEventListener("click", () => {
193
+ completeBtn.disabled = true;
194
+ completeBtn.textContent = "Completing...";
195
+ completeTask({ proxyUrl, profile, taskKey: currentTask.userTaskKey }, formVariables)
196
+ .then(() => {
197
+ clearError();
198
+ onComplete(formVariables);
199
+ })
200
+ .catch((err) => {
201
+ showError(`Complete failed: ${err instanceof Error ? err.message : String(err)}`);
202
+ completeBtn.disabled = false;
203
+ })
204
+ .finally(() => {
205
+ completeBtn.textContent = "Complete";
206
+ });
207
+ });
208
+ rejectBtn.addEventListener("click", () => {
209
+ const reason = window.prompt("Reason for rejection (optional):", "") ?? "";
210
+ if (onReject)
211
+ onReject(reason);
212
+ });
213
+ // Initial render
214
+ renderTask(currentTask);
215
+ return {
216
+ setTask(task) {
217
+ currentTask = task;
218
+ formVariables = {};
219
+ renderTask(task);
220
+ },
221
+ destroy() {
222
+ formViewer?.destroy();
223
+ formViewer = null;
224
+ root.remove();
225
+ },
226
+ };
227
+ }
228
+ //# sourceMappingURL=widget.js.map
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@bpmnkit/user-tasks",
3
+ "version": "0.0.1",
4
+ "description": "Embeddable user task widget for Camunda 8 — form rendering, claim/complete actions, zero dependencies",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": ["LICENSE", "README.md", "dist/**/*.js", "dist/**/*.d.ts"],
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "typecheck": "tsc --noEmit",
18
+ "check": "biome check ."
19
+ },
20
+ "dependencies": {
21
+ "@bpmnkit/api": "workspace:*",
22
+ "@bpmnkit/core": "workspace:*",
23
+ "@bpmnkit/plugins": "workspace:*",
24
+ "@bpmnkit/ui": "workspace:*"
25
+ },
26
+ "keywords": [
27
+ "bpmn",
28
+ "camunda",
29
+ "user-tasks",
30
+ "workflow",
31
+ "form",
32
+ "task-management",
33
+ "typescript"
34
+ ],
35
+ "license": "MIT",
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/bpmnkit/monorepo"
42
+ },
43
+ "homepage": "https://bpmnkit.com",
44
+ "bugs": {
45
+ "url": "https://github.com/bpmnkit/monorepo/issues"
46
+ }
47
+ }